WPF中的数据绑定--元素绑定

1. 元素绑定

数据绑定最简单的形式是源对象是WPF元素而且源属性是依赖项属性。依赖项属性具有内置的更改通知支持。因此当源对象中改变依赖属性的值时,会立即更新目标对象中的绑定属性。

绑定表达式

当使用绑定表达式时,不必对源对象做任何改动,只需配置源对象使其属性具有正确的值范围。

<Slider Grid.Row="0" Name="sliderFontSize" Margin="3" Minimum="1" Maximum="40" Value="10" TickFrequency="1" TickPlacement="TopLeft"> 
</Slider>
<TextBlock Grid.Row="1" Margin="10" Text="Simple Text" Name="lblSimpleText"FontSize="{Binding ElementName=sliderFontSize,Path=Value}">   
</TextBlock>

元素绑定的方式是:属性 = "{Binding ElementName=绑定源,Path=属性}"  

绑定错误

WPF不会引发异常来通知与数据绑定相关的问题。如果指定的元素或属性不存在,那么不会收到任何的指示;相反,只是不能在目标属性中显示数据。

调试可通过Visual Studio的OutPut窗口查看,WPF会输出绑定细节的跟踪信息。

绑定模式

名称 说明
OneWay 当源属性变化时更新目标属性。
TwoWay 当源属性变化时更新目标属性,并且当目标属性变化时更新源属性。
OneTime 最初根据源属性设置目标属性,然而,其后的所有改变都会被忽略。通常如果知道属性不会变化,可通过这种模式降低开销。
OneWayToSource 与OneWay相反,当目标属性变化时更新源属性。
Default 此类绑定依赖于目标属性,既可以是双向的,也可以是单向的。除非明确指定了一种绑定模式,否则所有绑定使用该方法

使用代码创建绑定

Binding binding = new Binding();
binding.Source = sliderFontSize;
binding.Path = new PropertyPath("Value");
binding.Mode = BindingMode.TwoWay;
lblSimpleText.SetBinding(TextBlock.FontSizeProperty,binding);

多绑定

可以设置TextBlock元素从文本框中获取文本,从单独的颜色列表中选择当前前景色和背景色,等等。

<TextBlock Grid.Row="1" Margin="10" Name="lblSimpleText"
   FontSize="{Binding ElementName=sliderFontSize,Path=Value}"
   Text="{Binding ElementName=txtContent,Path=Text}"
   Foreground="{Binding ElementName=lstColors,Path=SelectedItem.Tag}"> 
</TextBlock>

还可链接数据绑定。例如TextBox.Text属性创建表达式以链接到TextBlock.FontSize属性,而TextBlock.FontSize又链接到Slider.Value属性的绑定表达式。

绑定更新

源的变化会立即影响目标,然而反向的传递从目标到源未必会立即发生。他们的行为由Binding.UpdateSourceTrigger属性控制。

名称

说明

PropertyChanged 当目标属性变化时立即更新源。
LostFocus 当目标属性变化并且目标属性失去焦点时跟新源。
Explicit 除非调用BindingExpression.UpdateSource()方法,否则无法更新源。
Default 根据目标属性的元素确定更新行为。大多数属性的默认行为是PropertyChanged,但TextBox.Text属性的默认行为是LostFocus

表中列出的值不影响目标的更新方式,他们仅控制OneWay或OneWayToSource模式的绑定中源的更新方式。

绑定延迟

用户停止输入500毫秒后更新源对象

<TextBox Text="{Binding ElementName=txtSampleText,Path=FontSize,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,Delay=500}" Name="txtFontSize">
</TextBox>

绑定到非元素对象

WPF数据绑定基础结构不能获取私有信息或公有字段,必须是公有属性中。

绑定到非元素对象时,需要放弃Binding.ElementName属性,并使用以下属性之一。

  • Source:该属性是指向源对象的引用,提供数据的对象。
  • RelativeSource:这是引用,使用RelativeSource对象指向源对象。有了这个附加层,在在当前元素的基础上构建引用。这似乎增加了复杂程度,

但实际上RelativeSource属性是一种特殊工具,当编写控件模版及数据模版时是很方便的。

  • DataContext:如果没有使用Source或RelativeSource属性指定源,WPF就从当前元素开始在元素树中向上查找。检查每个属性的DataContext属性,并使用第一个飞空的DataContext属性。

当将同一个对象的多个属性绑定到不同的元素时,DataContext属性是非常有用的,因为可在更高层次的容器对象上,设置DataContext属性。

Source

绑定到静态对象

<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},Path=Source}"></TextBlock>

2.绑定到资源

<Window.Resources>
 <FontFamily x:Key="CoustomFont">Calibri</FontFamily>
</Window.Resources>
<Grid>
 <TextBlock Text="{Binding Source={StaticResource CoustomFont},Path=Source}"></TextBlock>
</Grid>

RelativeSource

<TextBlock Text="{Binding Path=Title,RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Window}}}"></TextBlock>

RelativeSource对象使用FindAncestor模式,该模式告知查找元素树知道发现AncestorType属性定义的元素类型。
FindAncestor模式有4种。

名称                 

说明

Self 表达式绑定到同一元素的另一个属性上。
FindAncestor 表达式绑定到父元素。WPF将查找元素树直至发现期望的父元素。为了指定父元素,还必须设置AncestorType属性以指示希望查找的父元素类型。此外,还可以用AncestorLevel属性略过发现的一定的数量的特定元素。例如当一棵树中查找时,如果希望绑定到第三个ListBoxItem类型的元素,应当使用如下设置,AncestorType={x:Type ListBoxItem};并且AncestorLevel=3,从而略过前连个ListBoxItem元素。默认值是1,并在找到第一个匹配的元素停止查找。
PreviousData 表达式绑定到数据绑定列表中前一个数据项。在列表中使用这种模式。
TemplateParent 表达式绑定到应用模版的元素。只有当绑定位于控件模版或数据模版内部时,这种模式才能工作。

DataContext属性

大量元素绑定到同一对象。

<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},Path=Source}"></TextBlock>
 <TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},Path=Source}"></TextBlock>
 <TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},Path=Source}"></TextBlock>

可以改成如下

<StackPanel DataContext="x:Static SystemFonts.IconFontFamily">
  <TextBlock Text="{Binding Path=Source}"></TextBlock>
  <TextBlock Text="{Binding Path=Source}"></TextBlock>
  <TextBlock Text="{Binding Path=Source}"></TextBlock>
 </StackPanel>

具体例程讲解:

第一个例子:

第二个例子:

先讲一下第一个例子:

    XMAL文件中的代码如下所示:

    <Grid Name="mygrid">
        <StackPanel>
            <TextBox Text="{Binding mytext}" Width="100" Margin="5"></TextBox>
            <TextBlock Text="{Binding mytext}" Margin="5,0,5,5" Width="100" Background="NavajoWhite"></TextBlock>
            <Button Width="100">click</Button>
        </StackPanel>
    </Grid>

如上代码所示,TextBox控件的依赖项属性Text绑定到mytext,TextBlock的依赖项属性Text也是绑定到mytext (这就相当于告诉大家,同一个绑定源可以同时绑定到多个绑定目标), mytext是MainWindows中的一个自定义的myClass对象mcl的string类型的属性,其代码如下所示:
 

    public partial class MainWindow : Window
    {
        public myClass mcl { get; set; } = new myClass();
        public MainWindow()
        {
            InitializeComponent();
            mcl.mytext = "hello";
            mygrid.DataContext = mcl;//需要给mygrid控件指定DataContext为mcl,这是告诉Grid控件以及它子控件绑定源是谁
        }
    }
 
    public class myClass:INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private string _mytext;
        public string mytext {
            get
            { return _mytext; }
            set
            {
                _mytext = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("mytext"));
            }
        }
    }

这个例子下,用户在TextBox框中输入字符串,只有当用户接着点击一下按钮或者将输入焦点改变到其他地方,TextBlox的字符串才会改变成和TextBox输入框的字符串一样。

    下面在进入第二个例子前,先讲一些有非常用的绑定知识:

    如上图所示,设置DataContext属性是为了告知绑定源对象,TextBox和TextBlock控件可以继承了来自Grid的绑定源。XMAL代码中的

Text="{Binding mytext}"

是告知两个控件绑定的路径,至此我们一定要牢记绑定的四大组成部分(一定要牢记):

绑定源,这里例子是mcl;记住绑定源指的是对象;
绑定路径,可以完全理解为绑定源的某个属性,在本例子里就是myClass类型的mytext属性。记住绑定路径指的是绑定源对象里的某个属性;
绑定目标对象,这里例子是TextBox和TextBlock对象;
绑定目标属性,这里的例子是TextBox和TextBlock对象的Text属性;记住目标属性一定是依赖项属性。
        根据微软官方文件,对于这个例子,需要了解的理论知识:

 (1)XMAL代码中出现的Binding扩展标记内的Path路径指定应当是指向一个对象的public 属性,Binding扩展标记中第一个参数可以省略Path:

Text="{Binding mytext}"
等价于:
Text="{Binding Path=mytext}"

(2)记住绑定目标属性一定是依赖项属性。这是第二遍强调了。绝大部分控件的属性都是依赖项属性。这也是告诉你如果你想自己实现一个自定义控件的属性,并且希望这个属性也可以像WPF官方控件一样绑定一个绑定源,那么你也要按照依赖项属性形式实现一个属性,关于依赖项属性的概念可以查看其它官网或百度资料,未来有空也会写一下这方面的知识,这里仅需要大家牢记的是绑定目标的属性必须是一个依赖项属性,不可辩驳!重要的事情说三遍。本篇文章看了能记住这一条你就比昨天进步了一点。
(3)绑定流方向,绑定流方向是由Binding类对象的Mode属性设置的,它共有四种方向:
OneWay,绑定源的属性值可以影响绑定目标属性的值;
TwoWay,绑定源的属性值可以影响绑定目标属性的值,同样的绑定目标属性的值改变了也会影响绑定源属性的值;
OneWayToSource,绑定目标属性的值改变可以影响绑定源属性的值;
OneTime,绑定源属性的值在初始时绑定到绑定目标属性的值上,后面不再有任何影响。
(4)但凡源想改变目标:也就是方向Mode属性是OneWay和TwoWay这两种类型来说,如果希望绑定源的每一次改变都能反映到绑定目标的属性值的改变上,那么您需要实现INotifyPropertyChanged接口。重新说一遍:如果你希望绑定源属性值(本例子为mytext属性)的更新能够自动反应到控件当中(也就是绑定源属性的值的改变能够同步改变到绑定目标属性的值),那么抱歉,WPF框架下,您必须得自己来实现INotifyPropertyChanged接口,这句话也是需要您牢记的,反正您只要按照这个接口实现了,您的属性值改变就可以自动影响界面的变化了,神奇的原因在于WPF框架内部都做好了。
控件的每个依赖项属性都有默认的绑定方向,一一记住是不可能的,大致可以通过是否可编辑来大致判断属性值的绑定方向,如果是可编辑的那么相应的属性值是TwoWay方向,如果是不可编辑的一般来讲是OneWay。您也可以通过方法查询特定控件特定属性的默认绑定方向,这里不做详细说明。因此,该例子的TextBox的Text属性默认方向是TwoWay,而TextBlock的Text属性默认方向是OneWay。
也就是目标反过来想改变源:也就是方向Mode属性是OneWayToSource或者TwoWay,这种情况下还涉及到Binding类型对象的UpdateSourceTrigger属性,它就是表示源影响目标的触发方式。UpdateSourceTrigger触发类型又分为三种:
LostFocus,当控件失去焦点时改变源属性值;
PropertyChanged,每当控件的属性值改变都立即改变源属性值;
Explicit,用户自己通过调用UpdateSource方法来改变源属性的值,控制权在用户手上。
   该例子中,TextBox的UpdateSourceTrigger属性是默认是LostFocus,因此例子一当中不需要明确设置UpdateSourceTrigger值。

      至此我们就可以完全理解例子一的绑定效果了,它的过程时这样子的:

     TextBox和TextBlock都绑定到实现了INotifyPropertyChanged接口的myClass类的mytext属性上,由于TextBox绑定方向是TwoWay的,因此在TextBox输入框中改变值之后,然后点击按钮让TextBox输入框失去焦点,这个时候mytext值立即被改变,由于与TextBlock的绑定方向是OneWay,mytext值的改变立即影响了TextBlock,至此说明了整个绑定的基本细节。

    好,说了这么多,大家可以猜到如何在例子一上做改动实现例子二了吧。很好,有些同学可能猜到了,只需要改XMAL中的一处即可:

    <Grid Name="mygrid">
        <StackPanel>
            <TextBox Text="{Binding mytext,UpdateSourceTrigger=PropertyChanged}" Width="100" Margin="5"></TextBox>
            <TextBlock Text="{Binding mytext}" Margin="5,0,5,5" Width="100" Background="NavajoWhite"></TextBlock>
            <Button Width="100">click</Button>
        </StackPanel>
    </Grid>

如上所示,在TextBox的Text属性的Binding扩展标记中添加UpdateSourceTrigger=PropertyChanged即可,这个时候只要用户在TextBox的输入框中改变内容就会立即反应到TextBlock当中。

猜你喜欢

转载自blog.csdn.net/u014453443/article/details/89706007
今日推荐