WPF+SQL Server 2008 TaskVision Demo小結

weixin_34344677發表於2013-04-14

  最近在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如何做的

TO_DATE
Convert A String With Default Format To A Date TO_DATE(<string>) RETURN DATE
SELECT TO_DATE('01-JAN-2004') FROM dual;
Convert A String With A Non-Default Format To A Date TO_DATE(<string>, <format mask>)
SELECT TO_DATE('01/01/2004', 'MM/DD/YYYY') FROM dual;
Convert A String With A Non-Default Format And Specify The Language TO_DATE(<string>, <format mask>) RETURN DATE
SELECT TO_DATE('January 12, 2005, 11:03 A.M.', 'MONTH DD, YYYY, HH:MI A.M.', 'NLS_DATE_LANGUAGE = American') FROM dual;
Convert A String With A Non-Default Format And Specify The Language TO_DATE(<date_string>, <format mask>, <NLS_PARAMETER>) RETURN DATE
ALTER SESSION SET NLS_TERRITORY = 'JAPAN';
SELECT TO_DATE('January 12, 2005, 11:03 A.M.', 'Month dd, YYYY, HH:MI A.M.', 'NLS_DATE_LANGUAGE = American') FROM DUAL;
ALTER SESSION SET NLS_TERRITORY = 'AMERICA';
Convert A String To 24 Hour Time TO_DATE(<date_string>, <format mask>) RETURN DATE
SELECT TO_CHAR(SYSDATE, 'MM/DD/YY HH24:MI:SS') FROM dual;

函式是不是很方便記憶,當然類似如此的格式化方式:“MM/DD/YYYY"怎麼會記不住呢?

下面來看下SQL Server如何搞的: 

View Code
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類如下:

View Code
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等框架重新實現之,請期待~

相關文章