WPF 數(shù)據(jù)綁定Binding

自定義Binding

當(dāng)為Binding設(shè)置了繼承System.ComponentModel.INotifyPropertyChanged的數(shù)據(jù)源類,Binding會自動偵聽來自這個接口的PropertyChanged事件。

<TextBox x:Name="textbox" Width="200" Height="100"></TextBox>
<Button Content="click me" Click="Button_Click"></Button>
...
using System.ComponentModel;
public partial class Window1 : Window
{
    Student stu;
    public Window1()
    {
        InitializeComponent();
        stu = new Student();
        Binding binding = new Binding("Name") { Source = stu };
        //Binding binding = new Binding(){ Path = new PropertyPath("Name"), Source = stu };
        textbox.SetBinding(TextBox.TextProperty, binding);
        //BindingOperations.SetBinding(textbox, TextBox.TextProperty, binding);
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        stu.Name += "Name";
    }
}
class Student : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private string name;
    public string Name
    {
        get { return name; }
        set
        {
            name = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
        }
    }
}

控件間的Binding

<TextBox x:Name="textbox2" Width="200" Text="{Binding Path=Value,ElementName=slider}"></TextBox>
<!--<TextBox x:Name="textbox2" Width="200" Text="{Binding Value,ElementName=slider}"></TextBox>-->
<Slider x:Name="slider" Maximum="100" Minimum="0" Width="100"></Slider>

等價于

<TextBox x:Name="textbox2" Width="200"></TextBox>
<Slider x:Name="slider" Maximum="100" Minimum="0" Width="100"></Slider>
...
textbox2.SetBinding(TextBox.TextProperty, new Binding() { Path = new PropertyPath("Value"), ElementName = "slider" });
//textbox2.SetBinding(TextBox.TextProperty, new Binding("Value") { ElementName = "slider" });

此處ElementName = "slider"Source = slider等價

Binding方向與數(shù)據(jù)更新

Mode

決定數(shù)據(jù)綁定的方向,其值為BindingMode枚舉

public enum BindingMode
{
  TwoWay = 0,
  OneWay = 1,//只讀
  OneTime = 2,
  OneWayToSource = 3,//只寫
  Default = 4
}
UpdateSourceTrigger

決定數(shù)據(jù)更新的時機,其值為UpdateSourceTrigger枚舉

public enum UpdateSourceTrigger
{
  Default = 0,
  PropertyChanged= 1,
  LostFocus = 2,
  Explicit = 3
}

Binding的路徑 (Path)

由于Binding的重載,Path值可以作為new Binding()實例的入?yún)⒁部梢宰鳛榭杖雲(yún)嵗膶傩?。以下語法等效:

{Binding Text}
{Binding Path=Text}
...
new Binding("Text"){}
new Binding(){Path=new PropertyPath("Text")}

Path屬性決定需要暴露的屬性值,其值可以為PropertyPath實例,且支持多級路徑

Path = new PropertyPath("Text.Length")
  • 當(dāng)Source本身就是數(shù)據(jù)(如stringint),不需要Path指明時,Path的值可設(shè)為".",在XAML中可直接省略Path屬性

Binding的Source

使用DataContext作為Binding的源

UI樹的每一個節(jié)點都有DataContext,當(dāng)一個Binding只有Path沒有Source時會從當(dāng)前元素開始向上逐級查找具有該Path屬性的DataContext(其實本質(zhì)上是DataContext作為一個依賴屬性,當(dāng)標(biāo)簽不存在某屬性時會從其父級元素繼承)

<WrapPanel x:Name="WrapPanel" VerticalAlignment="Center">
  <WrapPanel.DataContext>
    <local:Man Age="20"></local:Man>
  </WrapPanel.DataContext>
  <TextBox Text="{Binding Age}" Width="200"></TextBox>
</WrapPanel>
//或
WrapPanel.DataContext=new Man(){Age="20"};
  • DataContext屬性是直接采用其中的對象,而不是把對象作為其一個屬性
  • 更常用的是直接對頂層的DataContext賦值為當(dāng)前實例
<Button Content="{Binding A}" Width="50"></Button>
...
public partial class Dependency : Window
{
    public string A
    {
        get; set;
    }
    public Dependency()
    {
        InitializeComponent();
        A = "Click Me";
        DataContext = this;
    }
}

注意通過DataContext只能初始化數(shù)據(jù)到視圖并單向綁定,雙向綁定還是需要事件機制

列表控件與ItemsSource

通常使用System.ComponentModel.ObservableCollection類型代替List,因其實現(xiàn)了INotifyPropertyChanged接口,其變化可以通知到視圖界面。

<ListBox x:Name="myListBox" Height="100"></ListBox>
...
myListBox.ItemsSource = new ObservableCollection<string>() { "A","B","C" };
//或
myListBox.ItemsSource = new ObservableCollection<object>() { new { name = "apple", diam = 4 },new { name = "banana", diam = 5 } };
myListBox.DisplayMemberPath = "name";

若不設(shè)置DisplayMemberPath,也可通過對ItemTemplate屬性賦值(DataTemplate類型)可以詳細(xì)設(shè)置列表內(nèi)容

<ListBox x:Name="myListBox" Height="100">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <StackPanel>
        <TextBox Text="{Binding name}" Width="200"></TextBox>
        <TextBox Text="{Binding diam}" Width="200"></TextBox>
      </StackPanel>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>
導(dǎo)入XML作為Binding的數(shù)據(jù)源

使用XPath而非Path指定來源。

<?xml version="1.0" encoding="utf-8"?>
<StudentList>
  <Student Id="1">
    <Name>Tom</Name>
  </Student>
  <Student Id="2">
    <Name>Jack</Name>
  </Student>
</StudentList>
...
<ListView x:Name="myListView" Height="100">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Id" Width="80" DisplayMemberBinding="{Binding XPath=@Id}"></GridViewColumn>
            <GridViewColumn Header="Name" Width="80" DisplayMemberBinding="{Binding XPath=Name}"></GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>
...
XmlDocument doc = new XmlDocument();
doc.Load(@"../../student.xml");
XmlDataProvider xdp = new XmlDataProvider()
{
    Document = doc,
    XPath = @"/StudentList/Student"
};

myListView.DataContext = xdp;
myListView.SetBinding(ListView.ItemsSourceProperty, new Binding());
  • XmlDataProvider用于將XML作為數(shù)據(jù)源提供給Binding
  • @的是XML的屬性,不加的是其子級元素。
  • 也可不聲明doc,改為xdp.Source=new Uri(@"../../student.xml")
內(nèi)聯(lián)XML作為Binding的數(shù)據(jù)源
<Grid.Resources>
    <XmlDataProvider x:Key="ExpenseDataSource" XPath="Expenses">
        <x:XData>
            <Expenses xmlns="">
                <Person Name="Mike" Department="Legal">
                    <Expense ExpenseType="Lunch" ExpenseAmount="50" />
                    <Expense ExpenseType="Transportation" ExpenseAmount="50" />
                </Person>
                <Person Name="Lisa" Department="Marketing">
                    <Expense ExpenseType="Document printing"
    ExpenseAmount="50"/>
                    <Expense ExpenseType="Gift" ExpenseAmount="125" />
                </Person>
                <Person Name="John" Department="Engineering">
                    <Expense ExpenseType="Magazine subscription" 
    ExpenseAmount="50"/>
                    <Expense ExpenseType="New machine" ExpenseAmount="600" />
                    <Expense ExpenseType="Software" ExpenseAmount="500" />
                </Person>
                <Person Name="Mary" Department="Finance">
                    <Expense ExpenseType="Dinner" ExpenseAmount="100" />
                </Person>
            </Expenses>
        </x:XData>
    </XmlDataProvider>
    <DataTemplate x:Key="nameItemTemplate">
        <Label Content="{Binding XPath=@Name}"/>
    </DataTemplate>
</Grid.Resources>
...
<ListBox Name="peopleListBox" Grid.Column="1" Grid.Row="2" 
    ItemsSource="{Binding Source={StaticResource ExpenseDataSource}, XPath=Person}"
    ItemTemplate="{StaticResource nameItemTemplate}">
</ListBox>
ObjectDataProvider

將對象/方法返回值作為數(shù)據(jù)源提供給 Binding

RelativeSource 通過標(biāo)簽位置關(guān)系進行綁定

ElementName通過元素名綁定,Source通過元素對象/數(shù)據(jù)對象綁定,RelativeSource則通過標(biāo)簽的位置關(guān)系進行綁定

Binding 的數(shù)據(jù)校驗

ValidationRules屬性添加ValidationRule類型對象

Binding 的數(shù)據(jù)轉(zhuǎn)換

Converter屬性創(chuàng)建繼承IValueConverter的類

多路Binding

MultiBinding類可以將一組Binding對象(可以分別擁有自己的校驗與轉(zhuǎn)換機制)聚合起來,最后共同決定傳出的數(shù)據(jù)。
以下demo為textBox1和textBox2數(shù)據(jù)一致且textBox3和textBox4數(shù)據(jù)一致時button為可點擊狀態(tài) :

<StackPanel>
    <TextBox x:Name="textBox1" Height="23" Margin="5"></TextBox>
    <TextBox x:Name="textBox2" Height="23" Margin="5"></TextBox>
    <TextBox x:Name="textBox3" Height="23" Margin="5"></TextBox>
    <TextBox x:Name="textBox4" Height="23" Margin="5"></TextBox>
    <Button x:Name="button" Content="click me" Width="80" Margin="5"></Button>
</StackPanel>
...
public partial class Multi : Window
{
    public Multi()
    {
        InitializeComponent();
        SetMultiBinding();
    }

    private void SetMultiBinding()
    {
        Binding b1 = new Binding("Text") { Source = textBox1 };
        Binding b2 = new Binding("Text") { Source = textBox2 };
        Binding b3 = new Binding("Text") { Source = textBox3 };
        Binding b4 = new Binding("Text") { Source = textBox4 };
        MultiBinding mb = new MultiBinding() { Mode=BindingMode.OneWay };
        mb.Bindings.Add(b1);//順序敏感
        mb.Bindings.Add(b2);
        mb.Bindings.Add(b3);
        mb.Bindings.Add(b4);

        mb.Converter = new myConverter();
        button.SetBinding(Button.IsEnabledProperty, mb);
    }
}

public class myConverter : IMultiValueConverter
{
    public object Convert(object[] values,Type targetType,object param,CultureInfo culture)
    {
        if(!values.Cast<string>().Any(text=>string.IsNullOrEmpty(text))
            && values[0].ToString()==values[1].ToString()
            && values[2].ToString() == values[3].ToString())
        {
            return true;
        }
        return false;
    }
    public object[] ConvertBack(object value,Type[] targetType, object param, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容