美文网首页
WPF 数据绑定Binding

WPF 数据绑定Binding

作者: 李霖弢 | 来源:发表于2019-11-12 17:15 被阅读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")
    
    • Source本身就是数据(如stringint),不需要Path指明时,Path的值可设为".",在XAML中可直接省略Path属性

    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"};
    
    • DataContext属性是直接采用其中的对象,而不是把对象作为其一个属性
    • 更常用的是直接对顶层的DataContext赋值为当前实例
    <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());
    
    • XmlDataProvider用于将XML作为数据源提供给Binding
    • @的是XML的属性,不加的是其子级元素。
    • 也可不声明doc,改为xdp.Source=new Uri(@"../../student.xml")
    内联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();
        }
    }
    

    相关文章

      网友评论

          本文标题:WPF 数据绑定Binding

          本文链接:https://www.haomeiwen.com/subject/dqufictx.html