最近在Academy做了一个练习TaskVision,用WPF+SQL Server 2008完成。主要的功能是,实现一个任务分配管理。具体如下:
系统登录,登录完成后,在MainWindow中用DataGrid显示任务的信息,可根据指定条件进行过滤;单击列表,页面下显示详细信息;双击则可对该任务进行重新编辑;在Menu中点击添加,单独显示一页进行任务添加。
下面就其中SQL Server及WPF涉及的,感觉有必要进一步说明的地方,列表如下:
- 1.MS SQL Server 2008 Management Studio-一个设置问题(知道就行)
- 2.sqlserver string to datetime,datetime to String问题(貌似Oracle没有遇到这个问题!)
- 3.登录窗体完成后关闭登录窗体(和Winform不同,后面的博文有讲到Winform实现);单实例运行WPF窗体---WPF应用程序生命周期
- 4.ComboBox和DataGrid Binding示例
- 5.获取ComboBox和DataGrid选中项的值----通用的方法:根据Visual Tree取值!
- 6.WPF窗体间传值---构造函数,Public类的public Static 字段和构造函数(既然写了就再提提)
- 7.从一个Form中刷新(触发)另一个Form的方法:更改或添加Task的Form完成后更新MainForm,这个还不同于前面DebugLZQ的另一篇博文:.NET一个线程更新另一个线程的UI(两种实现方法)
看完以上目录,如果你已经了然于胸,则可以绕行了。本身也只是个很小的一个Demo,没什么高端的东西。MainWindow如下:
下面对上面提及的几点依次进行说明。
1.MS SQL Server 2008 Management Studio-一个设置问题(知道就行)
使用MS SQL Server 2008 Management Studio的图形化界面对表进行编辑的时候,譬如说增加一个字段。当进行保存后,提示:表需要re-creation,无法保存。不合理啊!
解决方法如下:
在Tools-Options中进行如下取消"Prevent saving changes that require table re-creation"勾选。
这样使用起来就方便多了。
2.sqlserver string to datetime,datetime to String问题(貌似Oracle没有遇到这个问题!)
我为什么抱怨这个,先来看Oracle如何做的
函数是不是很方便记忆,当然类似如此的格式化方式:“MM/DD/YYYY"怎么会记不住呢?
下面来看下SQL Server如何搞的:
use TaskVision; SELECT convert(datetime, 'Oct 23 2012 11:01AM', 100) -- mon dd yyyy hh:mmAM (or PM) SELECT convert(datetime, 'Oct 23 2012 11:01AM') -- 2012-10-23 11:01:00.000 -- Without century (yy) string date conversion - convert string to datetime SELECT convert(datetime, 'Oct 23 12 11:01AM', 0) -- mon dd yy hh:mmAM (or PM) SELECT convert(datetime, 'Oct 23 12 11:01AM') -- 2012-10-23 11:01:00.000 -- Convert string to datetime sql - convert string to date sql - sql dates format -- T-SQL convert string to datetime - SQL Server convert string to date SELECT convert(datetime, '10/23/2016', 101) -- mm/dd/yyyy SELECT convert(datetime, '2016.10.23', 102) -- yyyy.mm.dd SELECT convert(datetime, '23/10/2016', 103) -- dd/mm/yyyy SELECT convert(datetime, '23.10.2016', 104) -- dd.mm.yyyy SELECT convert(datetime, '23-10-2016', 105) -- dd-mm-yyyy -- mon types are nondeterministic conversions, dependent on language setting SELECT convert(datetime, '23 OCT 2016', 106) -- dd mon yyyy SELECT convert(datetime, 'Oct 23, 2016', 107) -- mon dd, yyyy -- 2016-10-23 00:00:00.000 SELECT convert(datetime, '20:10:44', 108) -- hh:mm:ss -- 1900-01-01 20:10:44.000 -- mon dd yyyy hh:mm:ss:mmmAM (or PM) - sql time format SELECT convert(datetime, 'Oct 23 2016 11:02:44:013AM', 109) -- 2016-10-23 11:02:44.013 SELECT convert(datetime, '10-23-2016', 110) -- mm-dd-yyyy SELECT convert(datetime, '2016/10/23', 111) -- yyyy/mm/dd SELECT convert(datetime, '20161023', 112) -- yyyymmdd -- 2016-10-23 00:00:00.000 SELECT convert(datetime, '23 Oct 2016 11:02:07:577', 113) -- dd mon yyyy hh:mm:ss:mmm -- 2016-10-23 11:02:07.577 SELECT convert(datetime, '20:10:25:300', 114) -- hh:mm:ss:mmm(24h) -- 1900-01-01 20:10:25.300 SELECT convert(datetime, '2016-10-23 20:44:11', 120) -- yyyy-mm-dd hh:mm:ss(24h) -- 2016-10-23 20:44:11.000 SELECT convert(datetime, '2016-10-23 20:44:11.500', 121) -- yyyy-mm-dd hh:mm:ss.mmm -- 2016-10-23 20:44:11.500 SELECT convert(datetime, '2008-10-23T18:52:47.513', 126) -- yyyy-mm-ddThh:mm:ss.mmm -- 2008-10-23 18:52:47.513 -- Convert DDMMYYYY format to datetime SELECT convert(datetime, STUFF(STUFF('31012016',3,0,'-'),6,0,'-'), 105) -- 2016-01-31 00:00:00.000 -- SQL string to datetime conversion without century - some exceptions SELECT convert(datetime, '10/23/16', 1) -- mm/dd/yy SELECT convert(datetime, '16.10.23', 2) -- yy.mm.dd SELECT convert(datetime, '23/10/16', 3) -- dd/mm/yy SELECT convert(datetime, '23.10.16', 4) -- dd.mm.yy SELECT convert(datetime, '23-10-16', 5) -- dd-mm-yy SELECT convert(datetime, '23 OCT 16', 6) -- dd mon yy SELECT convert(datetime, 'Oct 23, 16', 7) -- mon dd, yy SELECT convert(datetime, '20:10:44', 8) -- hh:mm:ss SELECT convert(datetime, 'Oct 23 16 11:02:44:013AM', 9) SELECT convert(datetime, '10-23-16', 10) -- mm-dd-yy SELECT convert(datetime, '16/10/23', 11) -- yy/mm/dd SELECT convert(datetime, '161023', 12) -- yymmdd SELECT convert(datetime, '23 Oct 16 11:02:07:577', 13) -- dd mon yy hh:mm:ss:mmm SELECT convert(datetime, '20:10:25:300', 14) -- hh:mm:ss:mmm(24h) SELECT convert(datetime, '2016-10-23 20:44:11',20) -- yyyy-mm-dd hh:mm:ss(24h) SELECT convert(datetime, '2016-10-23 20:44:11.500', 21) -- yyyy-mm-dd hh:mm:ss.mmm
以上代码试过,可以正常转换。
注意这个101和103搞了我好久!
3.登录窗体完成后关闭登录窗体,单实例运行WPF窗体---WPF应用程序生命周期
点击登录后显示Main窗体,并关闭此登录窗体。Baidu了下,搜索到了各种奇葩的答案。
正解如下:在WPF中Application的关闭模式同Winform确实不同,WPF中应用程序的关闭模式有三种,它由Application对象的ShutdownMode属性来决定的。它的枚举值如下:
枚举名称 |
枚举值 |
说明 |
OnLastWindowClose |
0 |
当应用程序最后一个窗口关闭后则整个应用结束 |
OnMainWindowClose |
1 |
当主窗口关闭后则应用程序结束 |
OnExplicitShutdown |
2 |
只用通过调用Application.Current.Shutdown()才能结束应用程序 |
从上表我们也可以看到默认情况下ShutdownMode值是OnLastWindowClose,因此当MainWindow关闭后应用程序没有退出,如果要修改它可以将光标放到App.xaml中的XAML编辑窗口中,然后修改属性窗口中的ShutdownMode,也可以在XAML中或者程序中设置ShutdownMode属性。因此直接关闭就好!
WPF单实例运行窗体和Winform是一样的,修改App.xaml.cs如下:
using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Linq; using System.Windows; using System.Threading; namespace TaskVision_V_1 { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : Application { //单实例运行程序 Mutex mutex = null; protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); bool createNew = false; mutex = new Mutex(true, "single", out createNew); if (!createNew) { MessageBox.Show("应用程序正在运行!"); Application.Current.Shutdown(); } } } }
4.ComboBox和DataGrid Binding示例
<Grid> <DataGrid SelectionMode="Single" SelectionUnit="FullRow" AlternatingRowBackground="LemonChiffon" AutoGenerateColumns="False" Margin="168,51,48,216" Name="dataGrid1" SelectionChanged="dataGrid1_SelectionChanged" MouseDoubleClick="dataGrid1_MouseDoubleClick"> <DataGrid.Columns> <DataGridTextColumn Header="Id" Width="20" Binding="{Binding Id}" IsReadOnly="True"/> <DataGridTextColumn Header="!" Width="20" Binding="{Binding PLevel}" Visibility="Hidden"/> <DataGridTemplateColumn Header="!" Width="20" IsReadOnly="True" > <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Image Source="{Binding Image}" /> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn Header="分配给" Width="60" Binding="{Binding Distribution}" IsReadOnly="True"/> <DataGridTextColumn Header="摘要" Width="260" Binding="{Binding Abstract}" IsReadOnly="True"/> <DataGridTextColumn Header="状态" Width="60" Binding="{Binding Status}" IsReadOnly="True"/> <!--<DataGridTextColumn Header="进度" Width="160" Binding="{Binding Rate}"/>--> <!----> <DataGridTemplateColumn Header="进度" SortMemberPath="Rate" Width="100" IsReadOnly="True"> <DataGridTemplateColumn.CellTemplate> <DataTemplate > <Grid> <Rectangle Height="20" MinWidth="0" MaxWidth="100" Fill="#FF9CB8F1" Width="{Binding Path=Rate}" VerticalAlignment="Center" HorizontalAlignment="Left" /> <StackPanel Orientation="Horizontal"> <TextBlock Height="20" Width="30" Text="{Binding Path=Rate}" TextAlignment="Right" /> <TextBlock Height="20" Width="30" Text="%" TextAlignment="Left"/> </StackPanel> </Grid> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> <DataGridTextColumn Foreground="Red" Header="截止日期" Width="140" Binding="{Binding Deadline}" IsReadOnly="True"/> <DataGridTextColumn Header="修改人" Width="30" Binding="{Binding Mender}" IsReadOnly="True" Visibility="Hidden"/> <DataGridTextColumn Header="详细" Width="60" Binding="{Binding Detail}" IsReadOnly="True" Visibility="Hidden"/> </DataGrid.Columns> </DataGrid>
<ComboBox Height="23" HorizontalAlignment="Left" Margin="12,85,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" SelectionChanged="comboBox1_SelectionChanged"/>
//Binding DataGrid DataTable dataTable = SQLHelper.GetDataTable("select * from tb_TaskInfo"); dataGrid1.ItemsSource = dataTable.DefaultView;
//Binding ComboBox DataTable dataTable2 = SQLHelper.GetDataTable("select distinct TaskName from tb_TaskInfo"); comboBox1.ItemsSource = dataTable2.DefaultView; comboBox1.DisplayMemberPath = "TaskName";
SQLHelper类如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Data.SqlClient; namespace TaskVision_V_1 { class SQLHelper { public const string connectionString = @"server=LocalHost;database=TaskVision;Trusted_Connection=SSPI"; public static DataTable GetDataTable(string sqlText) { using (SqlConnection conn = new SqlConnection(connectionString)) { SqlDataAdapter sda = new SqlDataAdapter(sqlText, conn); DataTable dt = new DataTable(); sda.Fill(dt); return dt; } } public static int ExecuteNonQuery(string sqlText) { using (SqlConnection conn = new SqlConnection(connectionString)) { SqlCommand cmd = new SqlCommand (sqlText, conn); conn.Open(); int temp = cmd.ExecuteNonQuery(); return temp; } } } }
其结果为前面的MainWindow所示。
5.获取ComboBox和DataGrid选中项的值----通用的方法:根据Visual Tree取值!
获取DataGrid选中行的值
//获取DataGrid选中行值 DataRowView selectedItem = dataGrid1.SelectedItem as DataRowView; string PLevel = selectedItem["PLevel"].ToString();
获取ComboBox选中行的值
如下ComboBox:
<ComboBox Height="23" HorizontalAlignment="Left" Margin="60,79,0,0" Name="cbBoxPLevel" VerticalAlignment="Top" Width="120" > <ComboBoxItem > <StackPanel Orientation="Horizontal" > <Image Width="19" Height="19" Source="/TaskVision_V_1;component/Images/Major.gif" /> <TextBlock Height="19" Text="Major"/> </StackPanel> </ComboBoxItem> <ComboBoxItem > <StackPanel Orientation="Horizontal" > <Image Width="19" Height="19" Source="/TaskVision_V_1;component/Images/Medium.gif" /> <TextBlock Height="19" Text="Medium"/> </StackPanel> </ComboBoxItem> <ComboBoxItem > <StackPanel Orientation="Horizontal" > <Image Width="19" Height="19" Source="/TaskVision_V_1;component/Images/Minor.gif" /> <TextBlock Height="19" Text="Minor"/> </StackPanel> </ComboBoxItem> </ComboBox>
//根据Visual Tree获得指定的内容--典型代表 string New_PLevel = (((cbBoxPLevel.Items[cbBoxPLevel.SelectedIndex] as ComboBoxItem).Content as StackPanel).Children[1] as TextBlock).Text;
如下ComboBox,其值通过Bingding获得。
<ComboBox Height="23" HorizontalAlignment="Left" Margin="251,6,0,0" Name="cbBoxDistribution" VerticalAlignment="Top" Width="120" />
string New_Distribution = (cbBoxDistribution.GetValue(ComboBox.SelectedValueProperty) as DataRowView).Row.ItemArray.GetValue(0).ToString();
下面这样的最简单
<ComboBox Height="23" HorizontalAlignment="Left" Margin="251,79,0,0" Name="cbBoxStatus" VerticalAlignment="Top" Width="120"> <ComboBoxItem Content="Open" /> <ComboBoxItem Content="Close" /> </ComboBox>
string New_Status = (cbBoxStatus.Items[cbBoxStatus.SelectedIndex] as ComboBoxItem).Content.ToString();
6.WPF窗体间传值---构造函数,公共类的public Static 字段和构造函数(既然写了就再提提)
窗体间传值,可以通过公共类的pubic static字段。添加一个Globle.cs类。
using System; namespace TaskVision_V_1 { class Global { public static string userName = ""; } }
然后进行赋值取值。
也可以通过构造函数。就用这个例子来说,从MainForm中打开TaskDetail,并把DataGrid选中行的Id传过去。
重载TaskDetail的构造函数:
//构造函数传值 private string id; public TaskDetail(string _id) { InitializeComponent(); id = _id; }
MainForm中调用该构造函数,如下:
private void dataGrid1_MouseDoubleClick(object sender, MouseButtonEventArgs e) { //获取选中行值 DataRowView selectedItem = dataGrid1.SelectedItem as DataRowView; string Id = selectedItem["Id"].ToString(); //构造函数传值 TaskDetail taskDetailForm = new TaskDetail(Id); taskDetailForm.Show(); }
7.从一个Form中刷新另一个Form/一个Form触发另一个Form中的方法:更改或添加Task的Form完成后更新MainForm,这个还不同于前面DebugLZQ的另一篇博文:.NET一个线程更新另一个线程的UI(两种实现方法)
在MainForm中添加一个public static MainWindow,及相关的刷新方法。如下:
public static MainWindow mainWindow = null; public MainWindow() { InitializeComponent(); mainWindow = this; } public void RefreshWindow() { //Binding DataGrid DataTable dataTable = SQLHelper.GetDataTable("select * from tb_TaskInfo"); dataGrid1.ItemsSource = dataTable.DefaultView; } public void RefreshWindow2() { Window_Loaded(this, null); }
在TaskDetail/TaskNew中使用的地方如下:
private void Window_Closed(object sender, EventArgs e) { //A窗体更新B窗体 MainWindow.mainWindow.RefreshWindow(); //MainWindow mainwindow = new MainWindow();//事实证明不行;不new,做一个public static的方法?怎么访问非static的控件 //mainwindow.RefreshWindow(); }
private void Window_Closed(object sender, EventArgs e) { MainWindow.mainWindow.RefreshWindow2(); }
文章介绍的内容没有什么可圈可点的东西,点滴积累,不喜勿喷~
后面应该会加入LINQ、Entity Framework、MVVM等框架重新实现之,请期待~