1. 程式人生 > >WPF Binding學習(四) 綁定各種數據源

WPF Binding學習(四) 綁定各種數據源

uri inf 位置 雙向 mar 3.0 合作 裏的 rate

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屬性。

WPF Binding學習(四) 綁定各種數據源