設計Windows Phone 7.5資料庫應用程式

by Vivid 25. 四月 2012 03:18

.NET Magazine國際中文電子雜誌
者:許薰尹
稿:張智凱
文章編號:N120412302
出刊日期: 2012/4/25

Windows Phone OS 7.1版中,可以將資料儲存在本機資料庫(Local Database)。在Windows Phone應用程式中,要以物件的方式來操作資料庫中的資料。若Windows Phone應用程式可以使用LINQ to SQL技術搭配資料庫來存放資料,參考圖1所示。透過LINQ to SQL,你可以定義資料庫結構資訊、查詢資料庫資料,或將應用程式中的資料儲存到存在於Isolated Storage的資料庫。本文將介紹如何設計一個資料存取程式。

clip_image002

圖 1:Windows Phone資料存取應用程式。

本機資料庫(Local Database)執行在Windows Phone應用程式的程序(Process)中,不是以背景服務的方式執行。因為本機資料庫存在於Isolated Storage,因此只有對應的Windows Phone應用程式才能夠存取本機資料庫,其它的應用程式不能夠存取之。

本機資料庫(Local Database)只支援LINQ to SQL,不支援T-SQL。System.Data.Linq,DataContext是LINQ to SQL的核心物件,代表資料庫,它包含Table物件,對應到資料庫中的資料表。每一個Table物件都是由許多Entity物件組成,每一個Entity物件代表一筆資料。

我們先從建立Windows Phone應用程式專案開始,從Visual Studio 2010工具選單「File」-「New」-「Project」,選取Visual C#語言,點選「Silverlight for Windows Phone」-「Windows Phone Application」,使用預設的命名。接著會出現「New Windows Phone Application」對話方塊,「Target Windows Phone OS Version」項目則選取「Windows Phone OS 7.1」,然後按下「OK」按鈕,就會自動建立專案。

在專案之中新增一個類別以進行資料存取。請參考圖2所示,選取Visual Studio 2010選單「Project」-「Add Class」,便會跳出「Add New Item」對話盒,在對話盒中設定類別的檔案名稱為「DataClass」,按「Add」按鈕,就可以將類別檔案加入專案之中。

clip_image004

圖 2:新增一個類別檔案。

若要使用LINQ,您需要在專案之中,加入「System.Data.linq.dll」組件的參考。請參考圖3所示,在「Solution Explorer」視窗,點選你的專案 -「Reference」項目,按滑鼠右鍵,從突顯式選單中選取「Add Reference」,開啟「Add Reference」對話方塊,從「.NET」頁,選取「System.Data.linq.dll」組件,然後按下「OK」按鈕。

clip_image006

圖 3:加入「System.Data.linq.dll」組件的參考。

設計Entity資料類別

要建立本機資料庫之前,你需要先定義DataContext與Entity類別。這些類別定義了資料庫結構與LINQ to SQL物件模型之間的對應。

在設計Entity資料類別之前,需先引用相關的命名空間。開啟DataClass.cs程式設計畫面,在檔案最上方using區塊中加入以下程式碼,匯入以下命名空間:

using System.Linq;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Collections.ObjectModel;
using System.ComponentModel;

接下來,我們要為想存取的資料設計一個Entity類別,然後再利用LINQ自動在資料庫中建立對應的資料表(Table)。例如,未來資料庫想要儲存員工的清單,你可以在DataClass.cs檔案中,定義一個Employee類別如下:

[Table]
  public class Employee : INotifyPropertyChanged , INotifyPropertyChanging
  {
      public Employee ( int id,string name)
      {
          ID = id;
          Name = name;
      }
      public Employee ( )
      {

      }
      private int _id;

      [Column(IsPrimaryKey = true )]
      public int ID
      {
          get
          {
              return _id;
          }
          set
          {
              if ( PropertyChanging != null )
              {
                  PropertyChanging(this ,
                        new PropertyChangingEventArgs("ID"));
              }
              _id = value;
              if ( PropertyChanged != null )
              {
                  PropertyChanged(this ,
                        new PropertyChangedEventArgs("ID"));
              }
          }
      }

      private string _name;

      [Column()]
      public string Name
      {
          get
          {
              return _name;
          }
          set
          {
              if ( PropertyChanging != null )
              {
                  PropertyChanging(this ,
                        new PropertyChangingEventArgs("Name"));
              }
              _name = value;
              if ( PropertyChanged != null )
              {
                  PropertyChanged(this ,
                        new PropertyChangedEventArgs("Name"));
              }
          }
      }

      public event PropertyChangedEventHandler PropertyChanged;

      public event PropertyChangingEventHandler PropertyChanging;
  }

我們來分解這個類別中的程式碼,Employee類別上方套用了Table Attribute,表示對應到資料庫中的資料表。Table Attribute是定義在System.Data.Linq.Mapping命名空間之下:

[Table]
public class Employee : INotifyPropertyChanged , INotifyPropertyChanging
{

}

Employee類別也實作了INotifyPropertyChanged 與INotifyPropertyChanging兩個介面,這兩個介面分別定義了PropertyChanged與PropertyChanging事件程式碼,以通知LINQ,Employee物件中的資料是否被異動:

public event PropertyChangedEventHandler PropertyChanged;

public event PropertyChangingEventHandler PropertyChanging;

Employee類別之中,包含了兩個屬性,分別是ID與Name屬性,這兩個屬性的上方套用了Column Attribute,代表對應到資料表中的欄位。其中ID屬性的Column Attribute之IsPrimaryKey屬性設定為true,表示未來ID屬性的值是不能夠重複的,同樣的ID屬性也會對應到資料庫中資料表的主鍵欄位:

private int _id;

[Column(IsPrimaryKey = true )]
public int ID
{
    get
    {
        return _id;
    }
    set
    {
        if ( PropertyChanging != null )
        {
            PropertyChanging(this ,
                  new PropertyChangingEventArgs("ID"));
        }
        _id = value;
        if ( PropertyChanged != null )
        {
            PropertyChanged(this ,
                  new PropertyChangedEventArgs("ID"));
        }
    }
}

private string _name;

[Column()]
public string Name
{
    get
    {
        return _name;
    }
    set
    {
        if ( PropertyChanging != null )
        {
            PropertyChanging(this ,
                  new PropertyChangingEventArgs("Name"));
        }
        _name = value;
        if ( PropertyChanged != null )
        {
            PropertyChanged(this ,
                  new PropertyChangedEventArgs("Name"));
        }
    }
}

 

這兩個屬性get存取子單純地將私有變數_id與_name的值回傳。而set存取子較為複雜,先判斷是否有物件附加到PropertyChanging與PropertyChanged事件處理常式(即某個物件註冊了PropertyChanging與PropertyChanged事件的意思),若有(不為null),則分別在屬性資料變動之前與之後,觸發PropertyChanging與PropertyChanged事件。觸發事件時,傳入this代表目前的Employee物件實體,與PropertyChangingEventArgs物件當參數,PropertyChangingEventArgs物件的建構函式中傳入變動的屬性名稱。

設計Data Context類別

Data Context類別的主要功能是用來管理資料庫連線(Database Connection),以及所有Employee物件。我們在專案之中DataClass.cs檔案內加入一個DB類別,此類別繼承自System.Data.Linq 命名空間下的DataContext類別:

public class DB : DataContext
    {
        public string Name { get; set; }

        public Table<Employee> EmployeeTable;

        public DB ( string cn )
            : base(cn)
        {
        }

        public static void CreateDB ( )
        {
            DB db = new DB("Data Source=isostore:/mydb.sdf");

            if ( db.DatabaseExists() )
            {
                db.DeleteDatabase();
            }

            db.CreateDatabase();

            db.EmployeeTable.InsertOnSubmit ( new Employee(1,"Mary"));
            db.EmployeeTable.InsertOnSubmit(new Employee(2 , "Candy"));
            db.EmployeeTable.InsertOnSubmit(new Employee(3 , "Lilly"));
        
            db.SubmitChanges();
        }
    }

DB類別的建構函式中叫用了DataContext類別的建構函式,並傳入連接字串,以便連接、存取本機資料庫。類別中的CreateDB靜態(static)方法則用來建立本機資料庫,以及新增一些測試用的員工資料,方法中首先建立DataContext物件,傳入連接字串:

DB db = new DB("Data Source=isostore:/mydb.sdf");

連接字串中Data Source設定為「isostore」代表使用的是Isolated Storage來存放本機資料庫DataClass.cs檔案。本機資料庫的檔案名稱將會是mydb.sdf檔案。

Windows Phone上的LINQ to SQL並不直接支援T-SQL語法的執行,無法直接執行DDL或DML語法,需要使用ADO.NET物件來處理。範例中利用DataContext類別的DatabaseExists方法判斷資料庫是否已存在,若已存在則叫用DeleteDatabase方法將之刪除,若不存在則叫用DataContext類別CreateDatabase方法來建立本機資料庫:

if ( db.DatabaseExists() )
{
    db.DeleteDatabase();
}

db.CreateDatabase();

接下來,我們叫用InsertOnSubmit方法,新增三個員工物件:

db.EmployeeTable.InsertOnSubmit ( new Employee(1,"Mary"));

db.EmployeeTable.InsertOnSubmit(new Employee(2 , "Candy"));

db.EmployeeTable.InsertOnSubmit(new Employee(3 , "Lilly"));

最後透過DataContext類別的SubmitChanges方法,將三筆員工資料寫入資料庫中的資料表:

db.SubmitChanges();

設計使用者介面

開啟MainPage.xaml設計畫面,從Toolbox拖曳一個ListBox控制項到設計畫面,以便顯示員工的清單,請參考圖4所示:

clip_image008

圖 4:設計使用者介面。

請參考圖5所示,選取設計畫面上的PhoneApplicationPage (或將游標停留在XAML編輯畫面PhoneApplicationPage項目上),點選「Properties」視窗「Events」按鈕切換到事件,然後雙擊Loaded事件,產生Loaded事件處理常式。

clip_image010

圖 5:產生Loaded事件處理常式。

在PhoneApplicationPage 的Loaded事件處理常式加入以下程式碼:

private void PhoneApplicationPage_Loaded ( object sender , RoutedEventArgs e )
        {
            DB.CreateDB();
            DB db = new DB("Data Source=isostore:/mydb.sdf");
            var empList = from Employee emp in db.EmployeeTable
                          select emp;
            listBox1.ItemsSource = empList;
        }

首先叫用DB類別的CreateDB方法建立資料庫與測試資料。當本機資料庫建立完成之後,就可以利用LINQ to SQL來存取資料庫資料。接著建立DB物件,連結到資料庫,透過LINQ查詢,將資料庫中所有的Employee資料回傳最後將回傳的資料顯示在ListBox控制項中。

應用程式測試

我們先來看看設計到目前這個階段個成果,在Visual Studio 2010開發工具上,按F5或選取「Debug」-「Start Debugging」選項執行程式,此時會啟動Windows Phone Emulator,並看到畫面如下圖,ListBox顯示的是Employee型別的清單資料,我們稍後再來解決這個問題。

clip_image012

圖 6:應用程式測試。

檢視Isolated Storage中的本機資料庫檔案

設計Windows Phone程式時,你可以將本機資料庫檔案儲存在Isolated Storage之中,顧名思義,只有你的這個程式可以存取到它的內容,你可以使用Windows SDK 附的Isolated Storage Explorer 工具程式,來檢視Isolated Storage 中所包含的檔案。Isolated Storage Explorer 工具程式,是一個沒有UI介面的工具,當你安裝Windows Phone SDK之後,它會出現在以下路徑:

C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Tools\IsolatedStorageExplorerTool

使用此工具程式時,要設定幾個參數,常用的參數表列如下:

  • ts (Take snapshot) :將特定應用程式的Isolated Storage之所有內容,複製到本機目錄。
  • rs(Restore snapshot) :將本機某目錄中所有的內容複製到模擬器。
  • dir: 列出Isolated Storage中的目錄和檔案。
  • xd:代表模擬器。
  • de:代表實體機器。
  • 應用程式的GUID。可以找尋應用程式專案,bin\debug目錄下WMAppManifest.xml檔案,的ProductID。

例如本範例的ProductID參考如圖7:

clip_image014

圖 7:WMAppManifest.xml檔案。

利用以下指令執行Isolated Storage Explorer 工具程式:

ISETool.exe dir xd 7150fad1-3dbe-4ce7-9f7e-ba3dd6254e8d

將會列出目前Isolated Storage中包含一個mydb.sdf檔,請參考圖8所示:

clip_image016

圖 8:使用Isolated Storage Explorer 工具程式。

使用Expression Blend設計資料顯示樣版

前文提及第一次執行應用程式進行測試時,畫面中ListBox控制項顯示的不是員工的資訊,而是Employee型別,若要修正這個問題,你可以為ListBox設計資料顯示樣版(Data Template)。要設計資料顯示樣版,透過Microsoft Expression Blend工具會比使用Visual Studio 2010來的方便。底下我們將說明如何使用Expression Blend,為ListBox設計資料顯示樣版。

在「Solution Explorer」視窗中,點選MainPage.xaml,按滑鼠右鍵,從突顯式選單中,選取「Open in Expression Blend」選項,就會開啟Expression Blend設計工具,請參考圖9所示。

clip_image018

圖 9:開啟Expression Blend設計工具編輯MainPage.xaml檔。

選取Expression Blend Artboard上的ListBox,按滑鼠右鍵,從突顯式選單中,選取「Edit Additional Templates」-「Edit Generated Items (ItemTemplate)」-「Create Empty」選項,請參考圖10所示:

clip_image020

圖 10:建立空白資料樣版。

Expression Blend會自動將資料樣版設定成資源,請參考圖11所示,在「Create DataTemplate Resource」對話盒中,按下「OK」按鈕,便會進入到資料樣版設計畫面。

clip_image022

圖 11:建立資料樣版資源。

在資料樣版設計畫面中,選取左方工具箱上的StackPanel,然後雙擊之,將它加入資料樣版,請參考圖12所示。

clip_image024

圖 12:設計資料樣版,加入StackPanel。

選取左方工具箱上的TextBlock,然後雙擊之,將它加入資料樣版內StackPanel之中。按相同步驟,再加入一個TextBlock,目前的設計畫面看起來如下:

clip_image026

圖 13::設計資料樣版,加入TextBlock。

樣版設計畫面上的TextBlock將利用資料繫結技術來顯示ID與Name的資料。選取樣版設計畫面上的第一個TextBlock,在「Properties」頁-「Common Properties」區塊中找到Text屬性,點選屬性後方「Advanced Options」按鈕,從突顯式選單中,選取「Data Binding」設定資料繫結,請參考圖14所示。

clip_image028

圖 14:設定資料繫結。

請參考圖15所示,在「Create Data Binding」對話方塊中,選取「Data Context」頁,勾選下方的「Use a custom path expression」項目,在後方的文字方塊中輸入「ID」,然後按下「OK」按鈕。

clip_image030

圖 15:繫結到Employee類 ID屬性。

請參考圖16所示,重複上面的步驟,選取樣版設計畫面上的第二個TextBlock,在「Properties」頁-「Common Properties」區塊中找到Text屬性,點選屬性後方「Advanced Options」按鈕,從突顯式選單中,選取「Data Binding」。接著在「Create Data Binding」對話方塊中,選取「Data Context」頁,勾選下方的「Use a custom path expression」項目,在後方的文字方塊中輸入「Name」,然後按下「OK」按鈕:

clip_image032

圖 16:繫結到Employee類別 Name屬性。

設定完成之後,選取Expression Blend選單「File」-「Save All」,將所有檔案存檔。再回到Visual Studio,在Expression Blend工具中所進行的變動,會即時更新到Visual Studio之中,產生以下DataTemplate 的XAML標籤:

<phone:PhoneApplicationPage.Resources>
        <DataTemplate x:Key="DataTemplate1">
            <Grid>
                <StackPanel>
                    <TextBlock TextWrapping="Wrap" Text="{Binding ID}"/>
                    <TextBlock TextWrapping="Wrap" Text="{Binding Name}"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </phone:PhoneApplicationPage.Resources>

ListBox的XAML標籤如下,其中ItemTemplate屬性關聯到DataTemplate1:

<ListBox Height="539" HorizontalAlignment="Left" Margin="37,32,0,0" Name="listBox1" VerticalAlignment="Top" Width="383" ItemTemplate="{StaticResource DataTemplate1}" />

好了,現在再度測試應用程式測試,在Visual Studio 2010工具中,按F5或選取「Debug」-「Start Debugging」執行程式,此時會啟動Windows Phone Emulator,並看到畫面如下,ListBox會透過資料樣版顯示資料,將Employee的ID與Name屬性呈現在畫面上。

clip_image034

圖 17:透過資料樣版顯示資料。

使用Expression Blend設定Style

預設Windows Phone有許多內建了Style可以套用到資料樣版中的項目,以便你修改文字顯示使用的字型、大小、顏色等等外觀。舉例來說,請參考圖18所示,在資料樣版設計畫面中,從「Objects and Timeline」區塊中選取要套用Style的TextBlock,然後從Expression Blend的「Object」-「Edit Style」-「Apply Resource」,選取一個喜好的樣式:

clip_image036

圖 18:設定Style。

Expression Blend會產生以下的Style定義:

<phone:PhoneApplicationPage.Resources>
        <DataTemplate x:Key="DataTemplate1">
            <Grid>
                <StackPanel>
                    <TextBlock TextWrapping="Wrap" Text="{Binding ID}" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                    <TextBlock TextWrapping="Wrap" Text="{Binding Name}" Style="{StaticResource PhoneTextSubtleStyle}"/>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </phone:PhoneApplicationPage.Resources>

按F5或選取「Debug」-「Start Debugging」執行程式,此時會啟動Windows Phone Emulator,並看到畫面如下,ID的字型看起來比較大一些。

clip_image038

圖 19:套用Style展示資料。

資料篩選

使用LINQ的好處是透過簡單類似SQL的語法,就可以用來查詢物件模型中的資料。讓我們回到Visual Studio 2010進行畫面設計,在ListBox上方加入一個TextBox與一個TextBlock:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
            <ListBox Height="453" HorizontalAlignment="Left" Margin="37,118,0,0" Name="listBox1" VerticalAlignment="Top" Width="383" ItemTemplate="{StaticResource DataTemplate1}" />
            <TextBox Height="66" HorizontalAlignment="Left" Margin="179,28,0,0" Name="textBox1" Text="" VerticalAlignment="Top" Width="214" />
            <TextBlock Height="30" HorizontalAlignment="Left" Margin="56,46,0,0" Name="textBlock1" Text="員工編號:" VerticalAlignment="Top" />
        </Grid>

TextBlock 顯示提示字串「員工編號:」;TextBox則用來輸入篩選條件,我們要將員工ID與 TextBox Text屬性相符的Employee資料取出。

下一步是修改程式碼,將建立DB物件的程式碼移到類別階層:

DB db = new DB("Data Source=isostore:/mydb.sdf");
        private void PhoneApplicationPage_Loaded ( object sender , RoutedEventArgs e )
        {
            DB.CreateDB();
         
            var empList = from Employee emp in db.EmployeeTable
                          select emp;
            listBox1.ItemsSource = empList;
        }

Windows Phone應用程式也支援事件處理的機制,您可以攔截事件,然後撰寫對應的事件處理常式。例如,當使用者在用來輸入篩選條件文字方塊(TextBox)輸入資料之後,您馬上要自動取出員工資料,你可以攔截TextChanged事件,這個事件會在每回文字方塊的內容變動時,自動地觸發。使用Visual Studio 2010工具程式,選取畫面上的TextBox,在「Properties」視窗,選取「Events」來設定事件 ,雙擊TextChanged事件,就會自動產生事件處理常式。Visual Studio 2010會自動在TextBox的XAML標籤中新增事件註冊語法:

<TextBox Height="66" HorizontalAlignment="Left" Margin="179,6,0,0" Name="textBox1" Text="" VerticalAlignment="Top" Width="214" TextChanged="textBox1_TextChanged" />

在textBox1的TextChanged事件處理常式加入以下程式:

private void textBox1_TextChanged ( object sender , TextChangedEventArgs e )
{
     if  (string.IsNullOrEmpty(textBox1.Text))
         return;

     int i = 0;
     int.TryParse(textBox1.Text , out i);           
    var empList = from Employee emp in db.EmployeeTable
                  where emp.ID== i
                  select emp;
    listBox1.ItemsSource = empList;
}

先利用String類別的IsNullOrEmpty方法判斷文字方塊的內容是否有輸入,接著使用int類別的TryParse方法,將文字方塊的內容轉型成int型別。接著透過LINQ查詢where運算子,設定篩選條件,再將符合條件的查詢結果呈現在ListBox之中。

按F5或選取「Debug」-「Start Debugging」執行程式,此時會啟動Windows Phone Emulator,並看到畫面如下,只要在文字方塊中輸入員工編號,符合查詢條件的員工資料馬上會出現在清單方塊之中。

clip_image040

圖 20:篩選資料。

修改資料

最後,我們來談談如何修改資料。我們先在設計畫面中,加入TextBlock、TextBox與Button,畫面看起來如圖21:

clip_image042

圖 21:設計編輯畫面。

參考的XAML標籤如下所示:

  <TextBlock Height="30" HorizontalAlignment="Left" Margin="56,385,0,0" Name="textBlock2" Text="員工編號:" VerticalAlignment="Top" />
            <TextBox Height="71" HorizontalAlignment="Left" Margin="170,437,0,0" Name="textBox2" Text="" VerticalAlignment="Top" Width="250" />
            <TextBlock Height="30" HorizontalAlignment="Left" Margin="56,454,0,0" Name="textBlock3" Text="姓名:" VerticalAlignment="Top" />
            <TextBlock Height="46" HorizontalAlignment="Left" Margin="179,385,0,0" Name="textBlock4" Text="" VerticalAlignment="Top" Width="227" />
<Button Content="儲存" Height="72" HorizontalAlignment="Left" Margin="123,514,0,0" Name="button1" VerticalAlignment="Top" Width="160" />

直接修改XAML檔案,設定右上方TextBlock的Text屬性,它的值應該來自於ListBox選到的那一個Employee物件的ID屬性:

 

<TextBlock Height="46" HorizontalAlignment="Left" Margin="179,385,0,0" Name="textBlock4" Text="{Binding ElementName=listBox1, Path=SelectedItem.ID}" VerticalAlignment="Top" Width="227" />

修改TextBox的Text屬性,設定它繫結到ListBox選到的那一個Employee物件的Name屬性,且Mode為TwoWay,代表支援雙向繫結:

<TextBox Height="71" HorizontalAlignment="Left" Margin="170,437,0,0" Name="textBox2" Text="{Binding ElementName=listBox1, Path=SelectedItem.Name, Mode=TwoWay}" VerticalAlignment="Top" Width="250" />

使用Visual Studio 2010工具程式,選取畫面上用來存檔的Button,在「Properties」視窗,選取「Events」來設定事件 ,雙擊Click事件產生事件處理常式。在其中,叫用DataContext類別的SubmitChanges方法,將異動的資料儲存到本機資料庫。

private void button1_Click ( object sender , RoutedEventArgs e )
        {
            db.SubmitChanges();
        }

總結

在Windows Phone應用程式之中,可以在Isolated Storage建立本機資料庫以存放關聯式資料庫的資料。然後使用LINQ to SQL來存取本機資料庫。不過在使用上有一些限制需要注意,在Windows Phone應用程式之中,不可以直接執行T-SQL,或資料定義語法(Data Definition Language (DDL);也不可以執行資料模型語法(Data Modeling Language,DML)。大部分的ADO.NET物件都不支援,如DataReader。

範例下載: 2_PhoneApp2.rar (76.71 kb)

Tags:

.NET Magazine國際中文電子雜誌 | WindowsPhone | 許薰尹Vivid Hsu

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List