轉自:http://blog.csdn.net/lisenyang/article/details/18312199
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類型對象。因為XAML支持對內容屬性的簡寫,可以省略<GridView.Columns>這層標簽,直接在GridView內部定義<GridViewColumn>對象,GridViewColumn中最重要的一個屬性是DisplayBinding(類型是BindingBase),使用這個屬性可以指定這一列使用什么樣的Binding去關聯數據-----這與ListBox有些不同,ListBox使用的是DisplayMemberPath屬性(類型是String)。如果想用更復雜的結構來表示這一標題或數據,則可為GridViewColumn設置Head Template和Cell Template,它們的類型都是DataTemplate
接下來下后台代碼
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屬性。
6.使用RelativeSource當綁定源
當一個Binding有明確的來源的時候,我們可以通過Source或者ElementName賦值的辦法讓Binding與之關聯。有些時候我們不能確定作為Source對象叫什么名字,但是我們知道它與做為Binding目標對象在UI上的相對關系,比如控件自己關聯自己的某個數據,關聯自己某級容器的數據,這時候就需要用到Binding的RelativeSource屬性。
RelativeSource屬性的類型是RelativeSource類,通過這個類的幾個靜態或者非靜態的屬性我們可以控制它搜索相對數據源的方式。
<StackPanel x:Name="stackPanel1" Margin="10"> <StackPanel x:Name="stackPanel2" Margin="10"> <Grid Name="grid2"> <TextBox Name="txtBox1" Width="100" Height="23" HorizontalAlignment="Left"></TextBox> </Grid> </StackPanel> </StackPanel>
我們看下上面結構 這個XAML結構為 StackPanel1----StackPanel2---Grid2---TextBox 。然后以相對路徑來為TextBox進行綁定
RelativeSource relative = new RelativeSource(RelativeSourceMode.FindAncestor); //設置要查找的上級級別 relative.AncestorLevel = 1; //設置要查找的類型 relative.AncestorType = typeof(StackPanel); Binding binding = new Binding("Name") {//設置相對綁定的數據源 RelativeSource = relative }; this.txtBox1.SetBinding(TextBox.TextProperty, binding);
然后再觀察以上代碼。相對綁定源類為RelativeSource類,這個類構造可以放一個RelativeSourceMode枚舉,這個枚舉描述與綁定目標的位置相對的綁定源位置。枚舉值有四個
- PreviousData:當前顯示向列表的上一個數據項
- TemplateParent:引用應用了模板的元素,其中此模板中存在數據綁定元素。
- Self:引用正在綁定的元素,允許你該元素的一個屬性綁定到同一元素的其他屬性上。
- FindAncestor:引用數據綁定元素的父鏈中的上級。 可用於綁定到特定類型的上級或其子類
在這里設置為了FindAncestor. 然后為RelativeSource設置查找的級別和查找的類型。這里是先篩選類型,再篩選級別,比如現在查找的是深度為1的StackPanel。所以選中的是StackPanel2 而不是Grid2 。最后將RelativeSource對象設置為Binding的數據源
注意:設置數據源使用的是RelativeSource而不是Source