WPF 数据绑定Binding

2019-11-12  本文已影响0人  李霖弢

自定义Binding

当为Binding设置了继承System.ComponentModel.INotifyPropertyChanged的数据源类,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方向与数据更新

Mode

决定数据绑定的方向,其值为BindingMode枚举

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

决定数据更新的时机,其值为UpdateSourceTrigger枚举

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

Binding的路径 (Path)

由于Binding的重载,Path值可以作为new Binding()实例的入参也可以作为空入参实例的属性。以下语法等效:

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

Path属性决定需要暴露的属性值,其值可以为PropertyPath实例,且支持多级路径

Path = new PropertyPath("Text.Length")

Binding的Source

使用DataContext作为Binding的源

UI树的每一个节点都有DataContext,当一个Binding只有Path没有Source时会从当前元素开始向上逐级查找具有该Path属性的DataContext(其实本质上是DataContext作为一个依赖属性,当标签不存在某属性时会从其父级元素继承)

<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"};
<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只能初始化数据到视图并单向绑定,双向绑定还是需要事件机制

列表控件与ItemsSource

通常使用System.ComponentModel.ObservableCollection类型代替List,因其实现了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";

若不设置DisplayMemberPath,也可通过对ItemTemplate属性赋值(DataTemplate类型)可以详细设置列表内容

<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>
导入XML作为Binding的数据源

使用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());
内联XML作为Binding的数据源
<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

将对象/方法返回值作为数据源提供给 Binding

RelativeSource 通过标签位置关系进行绑定

ElementName通过元素名绑定,Source通过元素对象/数据对象绑定,RelativeSource则通过标签的位置关系进行绑定

Binding 的数据校验

ValidationRules属性添加ValidationRule类型对象

Binding 的数据转换

Converter属性创建继承IValueConverter的类

多路Binding

MultiBinding类可以将一组Binding对象(可以分别拥有自己的校验与转换机制)聚合起来,最后共同决定传出的数据。
以下demo为textBox1和textBox2数据一致且textBox3和textBox4数据一致时button为可点击状态 :

<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();
    }
}
上一篇下一篇

猜你喜欢

热点阅读