上方廣告

Lady Kukki 手作糖霜餅乾

2011年9月1日 星期四

Silverlight初探(四) - DataGrid資料繫結篇

前面一篇文章有大概說明如何使用WCF來傳遞資料,可是沒有明確的說明如何使用,這篇文章就是要說明如何真正的在Silverlight上運用WCF來繫結資料。

首先我們要先在Silverlight Page上拉一個DataGrid,在Grid當中放了兩個Template 【DataGridTemplateColumn.CellEditingTemplate】、【DataGridTemplateColumn.CellTemplate】,這兩個Template的目的是為了讓在編輯模式和顯示模式呈現不同的元件,其中單擊資料欄位會出現CheckBox的編輯模式。


    
        
            
                
                
                    
                        
                            
                        
                    
                
            
            
                
                
                    
                
            
        
        
            
                
                    
                        
                        
                    
                
            
        
        
        
        
        
    



再來我們看.cs檔的部分,為了讓資料可以正確的繫結我們需要的欄位,我們需要再後端定義一個類別來設定要顯示的欄位(如下語法),然後接下來是WCF的資料繫結(如下語法),在下文中可以看到必須先將資料放入先前定義好Customer陣列當中,才將資料Bind到DataGrid當中。
//資料欄位定義
public class Customer
{
    public string Number { get; set; }
    public string ID { get; set; }
    public string IP { get; set; }
    public string Url { get; set; }
    public string Date { get; set; }
    public string ImgUrl { get; set; }
    public string Country { get; set; }
    public string Title { get; set; }
}

//必須要加入WCF 的Web Server參考
MyWcf.UserIPListClient proxy = new MyWcf.UserIPListClient();

//此方法等同下列方法
//proxy.GetUserIPListCompleted += new EventHandler(proxy_GetUserIPListCompleted);
proxy.GetUserIPListCompleted += (s2, e2) =>
{
    if (e2.Error == null)
    {
        Dispatcher.BeginInvoke(delegate()
        {
            List data = (from item in e2.Result.Nodes[1].Descendants("IPLOG")
                                    select new Customer()
                                    {
                                        Number = item.Element("number").Value,
                                        ID = item.Element("id").Value,
                                        Title = item.Element("title").Value,
                                        IP = item.Element("ip").Value,
                                        Url = item.Element("url").Value,
                                        ImgUrl = item.Element("imgurl").Value,
                                        Country = item.Element("countrycode").Value,
                                        Date = DateTime.Parse(item.Element("initdate").Value).ToString("yyyy/MM/dd HH:mm:ss"),
                                    }).ToList();
            this.DataGrid1.ItemsSource = data;
        });
    }
};

//以非同步方式呼叫WCF服務功能
proxy.GetUserIPListAsync();
proxy.CloseAsync();

這裡我要特別說明一下,原本我打算把資料先傳進來在運用ASP.NET GridView的DataBound,在去存取另外一個WCF來改變特定資料欄位的資料,發現了幾個小問題。


第一 : DataGrid似乎沒有DataBound這個事件,網路有說到LoadingRow可以達到相同的功能,我試了一下不太會用,所以我用了另外一種方式(參考下面語法),先在DataGridTemplate當中放一個Label物件,且使用Loaded的個事件來達到這個功能。

        
            
                
            
        


void Label_IP_Loaded(object sender, RoutedEventArgs e)
{
    Label label = sender as Label;
    //在呼叫另外的WCF來改變特定欄位的值
    MyWcf1.PGeolocationClient proxy = new MyWcf1.PGeolocationClient();

    proxy.GetLocationCompleted += (s2, e2) =>
    {
        if (e2.Error == null)
        {
            Dispatcher.BeginInvoke(delegate()
            {
                Grid grid = new Grid();

                Image image = new Image();
                image.Width = 16;
                image.Height = 11;
                image.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                image.Margin = new Thickness(5, 0, 0, 0);
                image.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri("http://lawrence.serveblog.net/images/Country/" + e2.Result.ToString().Split(',')[0] + ".png", UriKind.RelativeOrAbsolute));
                grid.Children.Add(image);

                Label label_new = new Label();
                label_new.Content = e2.Result.ToString();
                label_new.Margin = new Thickness(25, 0, 0, 0);
                label_new.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
                grid.Children.Add(label_new);

                label.Content = grid;
                label.Visibility = System.Windows.Visibility.Visible;
            });
        }
    };

    //以非同步方式呼叫WCF服務功能
    proxy.GetLocationAsync(label.Content.ToString());
    proxy.CloseAsync();
}


第二 : 我使用了剛剛那種方式完成後,卻發現DataGrid 的Header有提供Sort的功能,可是有個資料欄就是不會跟著排序。沒錯就是剛剛回去Link WCF的那個欄位,我在想可能是因為我沒用用正規的方法所導致的,原本加上Loaded的時候這個功能也是Work的,改成再去撈另外一個WCF的資料一改下去就掛了,於是我是只好改成直接從第一次接資料的時候就把資料串好(這樣效能也比較OK,只是,一開始我本來要測試看看DataBound的功能才偶然發現的),在這裡證明了Loaded的方法是可以正常使用的,只是不能在Loaded當中再去撈一次資料。

接下來就來介紹WCF的部分,請自行參考以下語法。
[OperationContract]
public DataSet GetUserIPList()
{
    DataSet result = new DataSet();

    #region 定義DataTable
    DataTable dt = new DataTable("IPLOG");

    DataColumn column = new DataColumn();
    column.ColumnName = "id";
    column.DataType = System.Type.GetType("System.String");
    dt.Columns.Add(column);
    column = new DataColumn();
    column.ColumnName = "ip";
    column.DataType = System.Type.GetType("System.String");
    dt.Columns.Add(column);
    column = new DataColumn();
    column.ColumnName = "initdate";
    column.DataType = System.Type.GetType("System.String");
    dt.Columns.Add(column);
    column = new DataColumn();
    column.ColumnName = "number";
    column.DataType = System.Type.GetType("System.String");
    dt.Columns.Add(column);
    column = new DataColumn();
    column.ColumnName = "countrycode";
    column.DataType = System.Type.GetType("System.String");
    dt.Columns.Add(column);
    column = new DataColumn();
    column.ColumnName = "url";
    column.DataType = System.Type.GetType("System.String");
    dt.Columns.Add(column);
    column = new DataColumn();
    column.ColumnName = "imgurl";
    column.DataType = System.Type.GetType("System.String");
    dt.Columns.Add(column);
    column = new DataColumn();
    column.ColumnName = "title";
    column.DataType = System.Type.GetType("System.String");
    dt.Columns.Add(column);
    #endregion

    using (DataBase.Connection conn = new DataBase.Connection())
    {
        conn.Open();
        conn.CommandText = @"select * from table1";

        using (DataReader dr = conn.ExecuteReader())
        {
            //為什麼要特別使用DataReader再將資料塞到DataTable的原因就是這裡
            //因為我要去取得登入者IP的國家代碼,這裡使用的是網路上常見的Maxmind IP資料庫
            //原先的作法是先Bind一次資料,再透過DataBound呼叫另外一個WCF,這樣就不用使用DataReader塞到DataTable
            //但是就是有問題,所以這裡才改成這樣使用
            CountryLookupProj.CountryLookup cl = new CountryLookupProj.CountryLookup(@"c:\GeoIP.dat");

            while (dr.Read())
            {
                String ip = dr["ip"].ToString().Split(',')[0];
                if (!string.IsNullOrEmpty(ip))
                {
                    DataRow row = dt.NewRow();
                    row["id"] = dr["id"].ToString();
                    row["title"] = dr["title"].ToString();
                    row["ip"] = dr["ip"].ToString();
                    row["url"] = dr["url"].ToString();
                    row["number"] = dr["number"].ToString();
                    row["initdate"] = dr["initdate"].ToString();
                    row["countrycode"] = cl.lookupCountryCode(ip) + "," + cl.lookupCountryName(ip);
                    row["imgurl"] = "http://lawrence.serveblog.net/images/Country/" + cl.lookupCountryCode(ip) + ".png";
                    dt.Rows.Add(row);
                }
            }
        }

        result.Tables.Add(dt);
    }

    return result;
}

下面就是本範例的執行畫面,另外從最近的執行歷程看來有個來自美國的朋友不眠不休的研究我的網誌,幾乎是每篇都看,真是不知道是我的超級粉絲還是不懷好意,真的是又愛又恨阿 ^^

PS.
Maxmind 程式&資料庫下載點
阿六仔示範如何使用Maxmind