WPF Binding學習(四) 綁定各種數據源
1.集合作為數據源
首先我們先創建一個模型類
public class Student { public int ID { get; set; } public String Name { get; set; } }
然後我們創建我們的頁面布局
<StackPanel Width="300" Height="300" HorizontalAlignment="Left"> <ListView Name="listView1"> <ListView.View> <GridView> <GridViewColumn Header="編號" DisplayMemberBinding="{Binding ID}" Width="100"></GridViewColumn> <GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}" Width="100"></GridViewColumn> </GridView> </ListView.View> </ListView> </StackPanel>
在這裏我們使用了ListView控件和GridView控件來顯示數據,這兩個控件從表面來看應該屬於同一級別的控件。實際上並非如此!ListView是ListBox的派生類,而GridView是ViewBase的派生類,ListView中的View是一個ViewBase對象,所以,GridView可以做為ListView的View來使用而不能當作獨立的控件來使用。這裏使用理念是組合模式,即ListView由一個View,但是至於是GridVIew還是其它類型的View,由程序員自己選擇。其次,GridView的內容屬性是Columns,這個屬性是GridViewColumnCollection
接下來下後臺代碼
IList<Student> list = new ObservableCollection<Student>() { new Student(){ID=1,Name="狗娃"}, new Student(){ID=2,Name="狗剩"}, new Student(){ID=3,Name="鐵蛋"} }; this.listView1.ItemsSource = list;
只需在構造函數中創建對象並綁定到ListView上即可,然後運行就可以看到已經綁定完畢
接下來看一下ObservableCollection這個集合,我們可以看到在這裏使用的是ObservableCollection集合而並非平常的List集合,那麽為什麽呢,因為ObservableCollection集合實現了INotifyCollectionChanged接口,也就是可以雙向綁定。
2.ADO.NET中DataTable對象做為數據源
在wpf中,是允許將DataTable直接做為Binding的數據源的,下面以一個例子做為參考
控件還可以用上面的控件,只需該數據源即可
首先先創建一個用於創建DataTable的方法
public DataTable CreateDt() { DataTable dt = new DataTable(); DataColumn[] dc = new DataColumn[] { new DataColumn("ID"), new DataColumn(){ColumnName="Name"} }; dt.Columns.AddRange(dc); return dt; }
然後再構造函數中創建DataTable,賦予值並綁定即可
DataTable dt = CreateDt(); DataRow dr = dt.NewRow(); dr[0] = 1; dr[1] = "狗娃"; dt.Rows.Add(dr); dr = dt.NewRow(); dr[0] = 2; dr[1] = "狗剩"; dt.Rows.Add(dr); dr = dt.NewRow(); dr[0] = 1; dr[1] = "鐵蛋"; dt.Rows.Add(dr); //將數據源設置為Dt的視圖 this.listView1.ItemsSource = dt.DefaultView;
3.使用XML數據作為數據源
WPF提供了兩套處理XML的類庫:
1.符合DOM(Document Object Model 文檔對象模式)標準類庫:XmlDocument.XmlElement,XmlNode等類,這套類型特點中規中矩,功能強大,但也背負了太多的XML傳統和復雜
2.以LINQ(Language-Intergrated Query 語言集成查詢)為基礎的類庫,包括:XDocument,XElement,XNode,XAttribute等類,這套類庫特點是可以通過LINQ進行查詢和操作,方便快捷
首先使用第一種方案
先創建一個XML文件
<?xml version="1.0" encoding="utf-8" ?> <StudentList> <Student id="1"> <Name>狗娃</Name> </Student> <Student id="2"> <Name>狗剩</Name> </Student> <Student id="3"> <Name>鐵蛋</Name> </Student> </StudentList>
然後創建XAML
<StackPanel Width="300" Name="stackPanel1"> <ListView Name="listView1"> <ListView.View> <GridView> <GridViewColumn Header="編號" DisplayMemberBinding="{Binding XPath=@id}"> </GridViewColumn> <GridViewColumn Header="姓名" DisplayMemberBinding="{Binding XPath=Name}"> </GridViewColumn> </GridView> </ListView.View> </ListView> </StackPanel>
註意:XML綁定不是使用Path,而是XPath
XmlDocument doc = new XmlDocument(); doc.Load(@"C:\資料\f盤\代碼\c#\WPF\TreeView\TestSource\Students.xml"); //通過XmlDataProvider進行綁定數據 XmlDataProvider dp = new XmlDataProvider(); dp.Document = doc; dp.XPath = @"StudentList/Student"; this.listView1.SetBinding(ListView.ItemsSourceProperty, new Binding() { Source=dp});
綁定XMl使用到了XmlDataProvider,這個類是將XMl做為數據源源的一種快捷方式.XmlDataPrivider有個Source屬性,可以使用它直接指定XML文檔地址(無論XML文檔是存儲在本地還是網絡位置),所以也可以這麽寫
XmlDataProvider dp = new XmlDataProvider(); dp.Source = new Uri(@"C:\資料\f盤\代碼\c#\WPF\TreeView\TestSource\Students.xml"); dp.XPath = @"StudentList/Student"; this.listView1.SetBinding(ListView.ItemsSourceProperty, new Binding() { Source=dp});
4.使用LINQ做為數據源
從3.0版本,.NET Framework開始支持LINQ,使用LINQ,可以方便的操作集合對象,LINQ查詢結果是一個IEnumerable<T>類型對象,而IEnumerable<T>又派生自IEnumerable,所以可以作為列表控件的Items Source使用。
現在還還用剛開始創建的那個Student模型類和XAML代碼,
public class Student { public int ID { get; set; } public String Name { get; set; } }
<StackPanel Height="300" Width="300" HorizontalAlignment="Left"> <ListView Name="listView1"> <ListView.View> <GridView> <GridViewColumn Header="編號" DisplayMemberBinding="{Binding ID}" Width="100"></GridViewColumn> <GridViewColumn Header="姓名" DisplayMemberBinding="{Binding Name}" Width="100"></GridViewColumn> </GridView> </ListView.View> </ListView> </StackPanel>
我們只需更改數據源即可
List<Student> stus = new List<Student>() { new Student(){ID=1,Name="狗娃"}, new Student(){ID=2,Name="鐵蛋"}, new Student(){ID=3,Name="狗剩"} }; this.listView1.ItemsSource = from stu in stus where stu.Name.StartsWith("狗") select stu;
還可以將數據放在DataTable中
DataTable dt = CreateDt(); DataRow dr = dt.NewRow(); dr[0] = 1; dr[1] = "狗娃"; dt.Rows.Add(dr); dr = dt.NewRow(); dr[0] = 2; dr[1] = "狗剩"; dt.Rows.Add(dr); dr = dt.NewRow(); dr[0] = 1; dr[1] = "鐵蛋"; dt.Rows.Add(dr); this.listView1.ItemsSource = from row in dt.Rows.Cast<DataRow>() where (row["Name"] as String).StartsWith("狗") select new Student { ID = Convert.ToInt32(row["ID"]), Name = row["Name"] as String };
又或者使用XML
XDocument xd = XDocument.Load(@"C:\資料\f盤\代碼\c#\WPF\TreeView\TestSource\Students.xml"); this.listView1.ItemsSource = from ele in xd.Descendants("Student") where (ele.Elements().First()).Value.StartsWith("狗") select new Student { ID = Convert.ToInt32(ele.Attribute("id").Value), Name = ele.Elements().First().Value
5.ObjectDataProvider做為數據源
理想情況下,上遊程序員將類設計好,使用屬性把數據暴露出來,下遊程序員將這些類作為Binding的Source,把屬性做為Binding的Path來消費。但很難保證一個類的屬性都暴露出來,例如需要的數據可能是方法的返回值。而重新設計底層類的風險會比較高,況且有可能引用的類庫情況我們不可能更改已經便宜好的類,這時候就需要使用ObjectDataProvider來包裝做為Binding源的數據對象。
ObjectDataProvider顧名思義就是把對對象做為數據源提供給Binding。上面使用的XmlDataProvider,這兩個類的父類都是DataSourceProvider抽象類。
現在做一個這樣例子。
有一個Calculator類,它具有一個加法方法
class Caculate { public string Add(string arg1, string arg2) { double x = 0; double y = 0; double z = 0; if (double.TryParse(arg1, out x) && double.TryParse(arg2, out y)) { z = x + y; return z.ToString(); } return "Iput Error"; } }
然後在XAML中創建三個TextBox框
<StackPanel Height="300" Width="300" HorizontalAlignment="Left"> <TextBox Name="txtBox1" Width="120" HorizontalAlignment="Left"></TextBox> <TextBox Name="txtBox2" Width="120" Margin="0 10" HorizontalAlignment="Left"></TextBox> <TextBox Name="txtBox3" Width="120" Margin="0 10" HorizontalAlignment="Left"></TextBox> </StackPanel>
要實現的需求就是通過Caculate方法實現第三個文本框是前兩個之和,也就是我們需要將前兩個文本框綁定到Add方法的兩個參數,第三個綁定到返回值上。
然後我們在構造函數中編寫後臺代碼
ObjectDataProvider odp = new ObjectDataProvider(); //設置用於綁定源的對象 odp.ObjectInstance = new Caculate(); //設置調用方法的名稱 odp.MethodName = "Add"; //添加方法參數 odp.MethodParameters.Add("0"); odp.MethodParameters.Add("0"); //綁定參數到txtBox1和txtBox2 this.txtBox1.SetBinding(TextBox.TextProperty, new Binding("MethodParameters[0]") { Source = odp, BindsDirectlyToSource = true, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); this.txtBox2.SetBinding(TextBox.TextProperty, new Binding("MethodParameters[1]") { Source = odp, BindsDirectlyToSource = true, UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); //綁定結果 this.txtBox3.SetBinding(TextBox.TextProperty, new Binding(".") { Source = odp});
先來分析一下上面代碼。ObjectDataProvider類的作用是包裝一個以方法暴露數據的對象,這裏就先創建一個ObjectDataProvider的對象.然後用一個Caculate對象做為ObjectInstance對象復制。這就是把Caculate對象包裝在了ObjectDataProvider裏面。接著使用MethodName屬性指定調用的Caculate對象中Add的方法。問題來了,如果Caculator有多個構造器參數的方法Add應該如何區分?我們知道,重載方法的區別在於參數列表,緊接著兩句就是向MethodParameter屬性裏面加入兩個string類型的參數,這就相當於告訴ObjectDataProvider對象去調用Caculator對象中具有兩個string類型參數的Add方法,換句話說,MethodParameter對於參數的感應是非常敏感的。
準備好數據源之後,我們準備創建Binding。前面我們已經講過使用索引器作為Binding的Path,第一個Binding它的Source是一個ObjectDataProvider對象,Path是ObjectDataProvider中MethodParameters所引用的第一個元素。BindsDirectlyToSource這句話是告訴Binding只是將UI上的值傳遞給源而不是被ObjectDataProvider包裝的Caculator,同時UpdateSourceTrigger設置為UI只要一有變化就更新Source。第二個Binding只是對第一個的翻版,只是把Path屬性指向了第二個元素。第三個binding仍然使用ObjectDataProvider作為Source,但使用“.”作為Path----前面講過,當數據源本身就是數據的時候就用“.”來做為Path,在XAML中"."可以不寫。
註意: 在ObjectDataProvider對象作為Binding的Source的時候,這個對象本身就代表了數據,所以這裏的Path使用的“.”,而不是Data屬性。
WPF Binding學習(四) 綁定各種數據源