Final Project Plan__Class Schedule
一、应用功能介绍
我们的应用是一个Windows 10 端的课程表,能实现课程表的基本功能——查看课表,在此基础上添加了课程信息介绍、课程作业的提醒,并且还添加了代课模块,可以理解为把课程表和日程表相结合,还能增添一些因老师的课程调整以及时间灵活的实验课。
二、应用特色(具体的在功能模块剖析中)
I.用户友好
界面简单,菜单导航清晰,功能全面;课程不仅仅可以通过教务处导入,还可以通过自己编辑添加一些课程,比如,**实验课。
II.蹭课功能;
考虑到学生课程的局限性,我们增加蹭课功能,一方面让学生拓宽知识面,同时也学到了额自己感兴趣的东西。
III.代课功能很实用;
代课功能有点坏坏的,嘻嘻;但是,人有三急嘛,万一有个紧急事件可是可以谅解的嘛。不过代课可是要收费的哦,公平交易。
IV.日程与提醒;
我们把课程表和日程表结合起来实现的一个有意思的功能;作业忘记了,考试临近了,都可以在最初添加事件用来提醒自己,每天电脑右下角弹出,“你要学习了,你要学习了”。
三、功能模块剖析
①·功能说明_课程介绍:
该功能是在课程表显示之后点击相应的课程磁贴,能进入一个关于该课程的讨论界面,里面有我们内置的课程介绍、前置课程,以及上过的同学对该课程的评价、留言界面,帮助同学们在学期初了解这个课程。
·功能预计实现图:(上面就是课程和百度百科的介绍,下面是评论区)
·预计实现方法:
这个功能涉及一开始点进去的界面转换,以及默认课程介绍的TextBlock以及评论区功能。对于评论区如何实现,现在还是有点迷茫。通过查找资料得知,评论回复的功能涉及ASP和Sql数据库,以下有一个留言板系统的功能实现可供参考: https://wenku.baidu.com/view/24c704d158f5f61fb7366646.html
https://wenku.baidu.com/view/4072d2ad33d4b14e8424687c.html
代码:
protected void Page_Load(object sender, EventArgs e)
{
try
{
this.Label1.Text = Session["Uname"].ToString();
SqlConnection con = db.CreateConnection();
con.Open();
string strsql = "select count(*) from login";
SqlCommand cmd = new SqlCommand(strsql, con);
SqlDataReader rd = cmd.ExecuteReader();
while (rd.Read())
{
this.Label2.Text = rd[0].ToString();
}
rd.Close();
con.Close();
}
catch
{
Response.Write("<script>alert('用户未登录成功!')</script>");
Response.Redirect("login.aspx");
}
}
protected void Button1_Click(object sender, EventArgs e)
{
SqlConnection con = db.CreateConnection();
con.Open();
string strsql="insert into messages values('"+Label1 .Text +"','"+TextBox2.Text +"','"+TextBox3 .Text +"','"+DateTime .Now .ToString ()+"')";
SqlCommand cmd =new SqlCommand (strsql ,con );
cmd .ExecuteNonQuery();
con.Close ();
Response.Write("<script>alert('留言成功!')</script>");
Response .Redirect ("messageout.aspx");
}
"取消"事件代码:
protected void Button2_Click(object sender, EventArgs e)
{
TextBox2.Text = "";
TextBox3.Text = "";
}
②·功能说明_代课:
该功能是帮助同学们更便捷、可靠的完成校园里有偿帮助上课这一活动。有的时候遇到紧急事件又不想因为缺课而被老师抓从而影响心情和成绩,这个时候花一点小钱找空闲的同学帮忙上课就能解燃眉之急,很实用的功能。
·功能预计实现图:
模式基本是:任务发布-任务接受,双方联系并完成交易。
这个功能我觉得类似留言评论功能,也就是可以理解为一个学生留下了时间地点和联系方式(手机号),然后另一个同学看到了这条“评论”,联系之后完成此次交易,之后发表评论的人选择删除自己的留言,那么这一过程就结束了,也就是赋予权限,可以删除自己的留言,别人不能删除自己的留言,就可以实现这一功能了,这个功能只是提供一个平台,所以就避免了私聊功能的介入。
③·功能说明_登录、导入与编辑:
我们软件面向东大学生,所以我们的登录设置为学号和教务处密码进行登录;所以,我们可以根据教务处导入我们的课程;也可以通过自行编辑课表,添加课程;
登录界面的实现可以参考:
https://docs.microsoft.com/zh-cn/windows/uwp/security/microsoft-passport-login-auth-service
代码:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical">
<TextBlock Text="Login" FontSize="36" Margin="4" TextAlignment="Center"/>
<TextBlock x:Name="ErrorMessage" Text="" FontSize="20" Margin="4" Foreground="Red" TextAlignment="Center"/>
<TextBlock Text="Enter your credentials below" Margin="0,0,0,20"
TextWrapping="Wrap" Width="300"
TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<!-- Username Input -->
<TextBlock x:Name="UserNameTextBlock" Text="Username: "
FontSize="20" Margin="4" Width="100"/>
<TextBox x:Name="UsernameTextBox" PlaceholderText="sampleUsername" Width="200" Margin="4"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<!-- Password Input -->
<TextBlock x:Name="PasswordTextBlock" Text="Password: "
FontSize="20" Margin="4" Width="100"/>
<PasswordBox x:Name="PasswordBox" PlaceholderText="samplePassword" Width="200" Margin="4"/>
</StackPanel>
<Button x:Name="PassportSignInButton" Content="Login" Background="DodgerBlue" Foreground="White"
Click="PassportSignInButton_Click" Width="80" HorizontalAlignment="Center" Margin="0,20"/>
<TextBlock Text="Don't have an account?"
TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
<TextBlock x:Name="RegisterButtonTextBlock" Text="Register now"
PointerPressed="RegisterButtonTextBlock_OnPointerPressed"
Foreground="DodgerBlue"
TextAlignment="Center" VerticalAlignment="Center" FontSize="16"/>
<Border x:Name="PassportStatus" Background="#22B14C"
Margin="0,20" Height="100">
<TextBlock x:Name="PassportStatusText" Text="Windows Hello is ready to use!"
Margin="4" TextAlignment="Center" VerticalAlignment="Center" FontSize="20"/>
</Border>
<TextBlock x:Name="LoginExplaination" FontSize="24" TextAlignment="Center" TextWrapping="Wrap"
Text="Please Note: To demonstrate a login, validation will only occur using the default username
'sampleUsername' and default password 'samplePassword'"/>
</StackPanel>
</Grid>
在 Login 类代码隐藏中,需要将该类顶部的 Account 私有变量更改为 UserAccount。 更改 OnNavigateTo 事件,以便将该类型强制转换为 UserAccount。代码如下:
using PassportLogin.AuthService;
namespace PassportLogin.Views
{
public sealed partial class Login : Page
{
private UserAccount _account;
private bool _isExistingAccount;
public Login()
{
this.InitializeComponent();
}
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
//Check Windows Hello is setup and available on this machine
if (await MicrosoftPassportHelper.MicrosoftPassportAvailableCheckAsync())
{
if (e.Parameter != null)
{
_isExistingAccount = true;
//Set the account to the existing account being passed in
_account = (UserAccount)e.Parameter;
UsernameTextBox.Text = _account.Username;
SignInPassport();
}
}
}
}
}
大致界面用Excel实现如下图。可以通过点击右上角加号通过教务处导入课表或者手动。
其中的菜单栏设计可以用NavigationView控件实现。
<u>https://docs.microsoft.com/zh-cn/windows/uwp/design/controls-and-patterns/navigationview</u>
代码:
private void NavView_Loaded(object sender, RoutedEventArgs e)
{
// you can also add items in code behind
NavView.MenuItems.Add(new NavigationViewItemSeparator());
NavView.MenuItems.Add(new NavigationViewItem()
{ Content = "My content", Icon = new SymbolIcon(Symbol.Folder), Tag = "content" });
// set the initial SelectedItem
foreach (NavigationViewItemBase item in NavView.MenuItems)
{
if (item is NavigationViewItem && item.Tag.ToString() == "home")
{
NavView.SelectedItem = item;
break;
}
}
ContentFrame.Navigated += On_Navigated;
// add keyboard accelerators for backwards navigation
KeyboardAccelerator GoBack = new KeyboardAccelerator();
GoBack.Key = VirtualKey.GoBack;
GoBack.Invoked += BackInvoked;
KeyboardAccelerator AltLeft = new KeyboardAccelerator();
AltLeft.Key = VirtualKey.Left;
AltLeft.Invoked += BackInvoked;
this.KeyboardAccelerators.Add(GoBack);
this.KeyboardAccelerators.Add(AltLeft);
// ALT routes here
AltLeft.Modifiers = VirtualKeyModifiers.Menu;
}
private void NavView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
{
ContentFrame.Navigate(typeof(SettingsPage));
}
else
{
// find NavigationViewItem with Content that equals InvokedItem
var item = sender.MenuItems.OfType<NavigationViewItem>().First(x => (string)x.Content == (string)args.InvokedItem);
NavView_Navigate(item as NavigationViewItem);
}
}
private void NavView_Navigate(NavigationViewItem item)
{
switch (item.Tag)
{
case "home":
ContentFrame.Navigate(typeof(HomePage));
break;
case "apps":
ContentFrame.Navigate(typeof(AppsPage));
break;
case "games":
ContentFrame.Navigate(typeof(GamesPage));
break;
case "music":
ContentFrame.Navigate(typeof(MusicPage));
break;
case "content":
ContentFrame.Navigate(typeof(MyContentPage));
break;
}
}
private void NavView_BackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
{
On_BackRequested();
}
private void BackInvoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
On_BackRequested();
args.Handled = true;
}
private bool On_BackRequested()
{
bool navigated = false;
// don't go back if the nav pane is overlayed
if (NavView.IsPaneOpen && (NavView.DisplayMode == NavigationViewDisplayMode.Compact || NavView.DisplayMode == NavigationViewDisplayMode.Minimal))
{
return false;
}
else
{
if (ContentFrame.CanGoBack)
{
ContentFrame.GoBack();
navigated = true;
}
}
return navigated;
}
private void On_Navigated(object sender, NavigationEventArgs e)
{
NavView.IsBackEnabled = ContentFrame.CanGoBack;
if (ContentFrame.SourcePageType == typeof(SettingsPage))
{
NavView.SelectedItem = NavView.SettingsItem as NavigationViewItem;
}
else
{
Dictionary<Type, string> lookup = new Dictionary<Type, string>()
{
{typeof(HomePage), "home"},
{typeof(AppsPage), "apps"},
{typeof(GamesPage), "games"},
{typeof(MusicPage), "music"},
{typeof(MyContentPage), "content"}
};
String stringTag = lookup[ContentFrame.SourcePageType];
// set the new SelectedItem
foreach (NavigationViewItemBase item in NavView.MenuItems)
{
if (item is NavigationViewItem && item.Tag.Equals(stringTag))
{
item.IsSelected = true;
break;
}
}
}
}
这个界面我还想增加一点界面交互的实现;比如,突出显示。突出显示是当用户在交互式元素(如命令栏)附近移动指针时突出显示这些元素的灯光效果。我们可以对每个课程的Button自定义,实现突出显示的效果。
<u>https://docs.microsoft.com/zh-cn/windows/uwp/design/style/reveal</u>
代码:
<Grid RequestedTheme="Dark">
<Button Content="Button" Click="Button_Click" Style="{ThemeResource ButtonRevealStyle}"/>
</Grid>
点击课程,弹出课程信息界面。主要包括一下几个功能:1.简要介绍本课程信息;2.本本课程所在教室;3.教学时长;4.课程日程。课程日程,主要是可以添加课程日程,比如预习、复习、作业;并添加时限,以便及时提醒完成。
我们也可以点击右上角的编辑,编辑课程名称,教室,时间。也可以点击右上角的添加日程,添加新的日程(日程若是过了结束时间会自动删除);同时每个课程都对应一个吐槽区,点击可在其中发表自己对此课程的看法或感受;
学号、密码可以通过Textblock、Textbox控件实现
主界面可由NavigationView控件,课表结构可以下通过Grid实现;具体可见可以用Button或Textblock实现;
课程信息中的日程开始、结束时间,用CalenderDatepicker控件实现;
④·功能说明_蹭课
蹭课功能跟课程导入差不多。通过点击右上角的加号,选择蹭课,选择学院,选择专业,选择蹭课课程;
⑤·功能说明_导出课程表
这里我们会实现将当前的gird整体输出,以导出一张课程表。因此需要使用一下技术
- RenderTargetBitmap类(选择UI位图)
RenderTargetBitmap类可以实现将UI元素转化为位图 ,即将UI元素截图,生成一张图片。与截图不同的是,它可以定义UIElement,这里,我只需要截图的是图片和文字部分,即上面的字Grid里面的图像信息。
- StorageFolder类(选择文件,并保存)
要实现截图并保存到应用内存储,就先要定义文件名
再定义文件存储的位置,并创建文件
实例化对象成果:
⑥功能说明_消息提醒
I:Popup 类
不需要我们自己额外去写一个弹窗类,微软自己有一个Popup 弹窗类。当弹窗打开时,会自动放在当前应用页面的最顶层。
Popup类里有一个Child属性,用来存弹窗中的内容。
child的类型是UIElement。
UIElement 是具有可视外观并可以处理基本输入的大多数对象的基类。
因此child属性可以存grid stackpannel 这些......
Popup类还有一个IsOpen属性,当会true的时候,弹窗是打开的。false则相反。
II:PS
当创建一个popup的对象,并且将它的IsOpen属性设置为true的时候,代表将会有一个弹窗 显示在当前应用的最顶层。
像上面图中的做法,看上去只有一小块是弹窗,其实我的做法是,最顶层的popup的child属性里放的是一个grid,在grid里才是我显示出来的那一小块提示框,因为grid如果没有背景颜色的话,底下一层是会显示的,所以没有什么问题。不会因为盖了一层grid,底下的内容会被盖住。
可参考:http://www.cnblogs.com/MzwCat/p/7748033.html
四、市场调研&采访结果
因为Markdown的原因,这里不能直接插入视频,所以以下是采访视频的传送门:(密码是uvp)
http://v.youku.com/v_show/id_XMzUzMTEwNTA3Ng==.html?spm=a2h3j.8428770.3416059.1
http://v.youku.com/v_show/id_XMzUzMTA4NzUxNg==.html?spm=a2h3j.8428770.3416059.1
http://v.youku.com/v_show/id_XMzUzMTEzOTkyMA==.html?spm=a2hzp.8244740.0.0
采访回馈:通过本次采访,我们发现,课程表这类应用,在windows端的生长环境并不好,也就是说,如果是手机端的话实用性更强一点,而我们推出的代课功能其实在网上也有过类似的软件传出,并且带来了较大的负面影响,但是从我们学生的角度来说,如果不是恶意无故跷课,有的时候真的是必要的,或许我们应该设置找代课限制,不能让同学们无法无天……抛去平台的差异性,我们只从应用的功能性看,我们的应用功能比较精干,不像某课程表那样庞杂,又是小纸条,又是景点买票,又是火车票什么的,试想有多少同学会使用那些功能?而且会有很多垃圾消息的灌入,比如看dskjd%¥&%……&加Vsakjdhashd_219873,好了不说这个了,我们的应用能体现课程表的主干功能,好用,精简!
鸣谢:所有被采访者和工作人员