WPF 数据绑定Binding

自定义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();
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容