C# Wpf Binding 使用详解

C# Wpf Binding (元素绑定)使用详解

简单的说 数据绑定 是一个关系,该关系告诉WPF 从源对像提取一些信息,并用这些信息设置目标对像的属性。
在目标对像中,被设置绑定的属性必须是 依赖项属性,通常在WPF 元素中。

注意:
尽管从元素到元素的绑定是最简单的方,但是在真正的项目开发中,大量的数据绑定是将元素x绑定到数据对像。从而可以显示 从 数据库或者文件 中提取的数据。
从元素到元素的绑定通常应用于元素的交互方式自动化,用户自定义控件。

一、 绑定到元素

1 绑定表达式

使用 xaml 标记拓展(也就是用{}语法)。

2 弄清 源对像、目标对像 、目标属性

 <TextBox x:Name="sourceTBox" />
 <TextBlock x:Name="tb" Text="{Binding ElementName=sourceTBox,Path=Text}" />
        

在上面的代码中,我们就用到了绑定。但是我个人建议,为了代码简洁,默认的属性和默认的属性配制不需要在代码中重写设置。
所有上面的代码可以优化成下面代码

<TextBox x:Name="sourceTBox" />
<TextBlock x:Name="tb" Text="{Binding Text,ElementName=sourceTBox}" />

绑定表达试中 Path 属性被省略, Path=Text 简写成 Text。为什么可以这样简写,是因为 Binding 类中有一个 带Path 的构造方法。

C# Wpf Binding 使用详解_第1张图片

  • 源对像
    指 绑定表达式中 ElementName 属性所批向的元素,即 TextBox 为源对像(注:Binding的Source属性 和 Binding的RelativeSource属性指向的对像,也是源对像)
  • 目标对像
    指 包含绑定表达式的元素 ,即 TextBlok 为目标对像
  • 目标属性
    指 属性的设置了绑定表达式,这个属性就是 目标属性

3 绑定错误

Wpf 不会引发异常来通知与数据绑定相关的问题,如果绑定的属性不存在,不会收到任何提示,也不能在目标属性显示任何值。
WPF 绑定失败细节的跟踪信息会在 输出窗口 显示
WPF 绑定失败细节的跟踪信息会在 输出窗口 显示
WPF 绑定失败细节的跟踪信息会在 输出窗口 显示

如果要在控件中 显示 参考 Binding 的 TargetNullValue 属性

<TextBlock x:Name="tb" Text="{Binding Text,ElementName=sourceTBox,Mode=TwoWay},TargetNullValue='has error'" />

4 绑定模式

当设置 Binding.Mode 属性时。WPF 允许使用 5 个 system.windows.data.BindingMode 枚举值中的任何一个。代码如下,其中的 Mode=TwoWay 就是设置绑定模式

<TextBlock x:Name="tb" Text="{Binding Text,ElementName=sourceTBox,Mode=TwoWay}" />

system.windows.data.BindingMode 枚举值:

枚举名称 说明
OneWay 当源属性变化时更新目标属性
TwoWay 当源属性变化时更新目标属性 ,当目标属性 变化时更新源属性
OneTime 最初设定源属性时更新目标属性,后期的变化都被忽略 ,如果知道源属性不会变化,可以使用这种模式,降低开销
OneWayToSource 与OneWay 类似,但方向相反,当目标属性变化时更新源属性,有点向后传递,但目标属性永远不会被更新
Default 此类绑定依赖于目标属性,即可以是双向(对于用户可以设置的属性,如TextBlock.Text,) 也可以是单向,除非明确指明一种模式,否则都采用此种模式

5 CS 代码中使用 Binding


  Binding binding = new Binding();
  binding.Source = this.sourceTBox; //指向源对像 this 指向的当前的Window 对像 的 sourceTBox 对像 ,sourceTBox 是对像的名称,即(x:Name="**")
   //binding.Source = this; //指向源对像 this 指向的当前的Window 对像 ,也可
  binding.Path = new PropertyPath("Text");
  this.tb.SetBinding(TagProperty, binding);
  

使用 BindingOperations

Binding binding = new Binding();
binding.Path = new PropertyPath("Text");
//BindingOperations.SetBinding(this, TitleProperty, binding);         
BindingOperations.SetBinding(this,TitleProperty,binding);

使用代码 获取绑定

Binding binding = BindingOperations.GetBinding(this.tb, TextBlock.TextProperty);
//获取绑定以后可以修改 bindig 的属性
//binding.Mode = BindingMode.TwoWay;
//...
//修改过后可以重先设置绑定
//BindingOperations.SetBinding(this.tb,TextProperty,binding); 

更实用的 BindingExpression 对像


BindingExpression expression = BindingOperations.GetBindingExpression(this.tb, TextBlock.TextProperty);

//get source element 获取源对像
TextBox textBox =(TextBox) expression.ResolvedSource;

//get any data you need from the source element

string name = textBox.Name;

//  expression.ResolvedSource 获取绑定对像的引用

BindingExpression 更加实用的信息,去源码里查看

6 多绑定 (多属性绑定)

<CheckBox x:Name="cb1"  
	Visibility="Collapsed" 
	IsChecked="False"  
	HorizontalAlignment="Center" 
	VerticalAlignment="Center" 
	Width="50" Height="30" 
	BorderThickness="0" 
	IsVisibleChanged="cb1_IsVisibleChanged" />

<TextBox x:Name="sourceTBox" />

<TextBlock x:Name="tb" 
           Text="{Binding Text,ElementName=sourceTBox,Mode=TwoWay}" 
           Tag="{Binding Content ,ElementName=cb1,Mode=OneWay}"
           Visibility="{Binding IsChecked,ElementName=cb1,Converter={StaticResource Boolean2VisibilityConverter}}"
          />
  • TextBlockTextTagVisibility 属性都设置绑定表达式,这就是多绑定
  • Visibility 属性民的绑定表达式 用到了 转换器,转换器的相关知识,另行学习。

7 绑定更新

使用代码

 <TextBlock x:Name="tb" Text="{Binding Text,ElementName=sourceTBox,Mode=TwoWay,UpdateSourceTrigger=Default}" 

UpdateSourceTrigger 枚举值

名称 说明
PropertyChanged 属性发生变化时更新
LostFous 失去焦点时更新
Explicit 调用 BindingExpress.UpdateSource()时更新
Defeat 根据属性的元数据确定更新行为(FrameworkPropertyMetadata.DefaultUpdateSourceTriggere) 大多数默认行为 是PropertyChanged 但TextBox.Text 默认行为是 LostFocus

8 延迟绑定

作用 :避免过分频繁的触发操作,导致频繁更新UI。

使用 Binding 对像的 Delay 属性。等待数毫秒之后再提交更新。

使用代码

<TextBlock x:Name="tb" Text="{Binding Text,ElementName=sourceTBox,Mode=TwoWay,Delay=500}" />

500 毫秒后再更新。

二、 绑定到非元素对像

在项目开发中,大量的数据绑定是将元素达到到数据对像。从而可以显示 从 数据库或者文件 中提取的数据,从而需要绑定到非元素的对象中。

  • 绑定到非元素的对象时,需要 放弃 TargetElement 属性。
  • 绑定到非元素的对象时,使用以下属性的其中一个。
    Soure:该属性是指向源对像的引用(数据的对像)
    RelativeSource:这是引用,使用RelativeSource 对像指向源对像 RelativeSource是一种特殊的工具,在编写控件模板和数据模板是很方便。
    DataContent:如果没有使用Source和RelativeSource 指定源,Wpf就从当前元素开始在元素树中向上查找。检查每一个元素的DataContent属性,并使用 第一个非空的DataContent 属性。当我们在要同一个对像绑定不同元素时 DataContent属性 是非常有用的。

Source 属性

使用代码

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

第一个 Source 是 Path 属性的值

RelativeSource 属性

RelativeSource 属性可以根据相对目标对像的关系指向源对像,例如,可以使用RelativeSource 属性将元素绑定 自身父元素

设置 Binding.RelativeSource 属性 需要 RelativeSource 对像。语法会变得更加复杂。

  • 绑定到父元素 Mode=FindAncestor
            <TextBlock >
                <TextBlock.Text>
                    <Binding Path="Title">
                        <Binding.RelativeSource>
                            <RelativeSource Mode="FindAncestor" AncestorType="{x:Type  Window}" AncestorLevel="1" />
                        </Binding.RelativeSource>
                    </Binding>
                </TextBlock.Text>                
            </TextBlock>
            <!--以上代码可以简写成如下-->
            <TextBlock  Text="{Binding Title,RelativeSource={RelativeSource Mode=FindAncestor,AncestorLevel=1,AncestorType={x:Type Window}}}" />
            <!--一般一个窗口只有一个Window 对像,可以再次简写-->
            <TextBlock  Text="{Binding Title,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}}}" />
  • 绑定到自身 Mode="Self"
     <TextBlock >
        <TextBlock.ToolTip>
            <Binding Path="Text">
                 <Binding.RelativeSource>
                      <RelativeSource Mode="Self" />
                 </Binding.RelativeSource>
             </Binding>
        </TextBlock.ToolTip>                
    </TextBlock>
    <!--以上代码可以简写成如下-->
    <TextBlock  Text="{Binding Text,RelativeSource={RelativeSource Mode=Self}}" />
  • 绑定到列表数据中的前一个数据项 Mode="PreviousData"
           <TextBlock >
                <TextBlock.ToolTip>
                    <Binding Path="Text">
                        <Binding.RelativeSource>
                            <RelativeSource Mode="PreviousData" />
                        </Binding.RelativeSource>
                    </Binding>
                </TextBlock.ToolTip>                
            </TextBlock>
            <!--以上代码可以简写成如下-->
            <TextBlock  Text="{Binding Text,RelativeSource={RelativeSource Mode=PreviousData}}" />

此时 Path=Text ,Text 是数据列表中的前一个对像的属性,不一定是依赖项属性,这种模式只会在列表中使用
以上代码不是真实使用时的代码,只是说明 写法

  • 绑定到应用模板元素 Mode="TemplatedParent"

只有当绑定位于控件模板()数据模板() 内部时,这种模式才能工作。

     <Button x:Name="Btn1" Content="Button" Background="Blue" Tag="/themes/img/snend.png">
                <Button.Style>
                    <Style TargetType="Button">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="Button">
                                    <Border x:Name="PART_border" Background="{Binding Background,RelativeSource={RelativeSource Mode=TemplatedParent}}">
                                        <StackPanel Orientation="Horizontal">
                                             <Image x:Name="img"  Source="{Binding Tag,RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
                                              <TextBlock x:Name="txt"  Text="{Binding Content,RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
                                        </StackPanel>
                                    </Border>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
            </Button.Style>
     </Button>

TemplateBindingExtension 对像

我们可以如下理解:

  • TemplateBindingExtension 对像RelativeSource.Mode=TemplatedParent ,简化写法拓展。
  • TemplateBinding 和 binding 有一个共同的祖先 MarkupExtension,只是在使用
    TemplateBindingExtension 对像是 结尾的Extension 是可以省去。这只是一个命名的问题,binding
    继承 BindingBase ,BindingBase 又继承自 MarkupExtension,binding 对像的命名也可以命名成
  • BindingExtension。 TemplateBinding 和 binding 都是用来做数据绑定的。
  • TemplateBindingExtension 就是 TemplateBinding 。
          <Button x:Name="Btn1" Content="Button" Background="Blue" Tag="/themes/img/snend.png">
                <Button.Style>
                    <Style TargetType="Button">
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="Button">
                                    <Border x:Name="PART_border" Background="{TemplateBinding Background}">
                                        <StackPanel Orientation="Horizontal">
                                             <Image x:Name="img"  Source="{TemplateBinding Tag}"/>
                                            <TextBlock x:Name="txt"  Text="{TemplateBinding Content}"/>
                                        </StackPanel>
                                    </Border>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Button.Style>
            </Button>

绑定表达式的代码明显简洁了很多。

DataContent属性

 <TextBlock  Text="{Binding Version}" />

Wpf就从当前元素开始在元素树中向上查找。检查每一个元素的DataContent属性,并使用 第一个非空的DataContent 属性 所引用的对像的 Version 属性。
如果 TextBlock的Text 直到 Window 对像DataContent 属性引用一个对像不为空,TextBlock的Text 就绑定到 Window 对像DataContent 属性引用对像的 Version 属性上。

//Window 对像DataContent 属性引用对像  SystemVersion
       public class SystemVersion{
            public int Code { get; set; }
            public String Version { get; set; }
        }

//设置 Window 对像DataContent 属性
     this.DataContext = new SystemVersion()
            {
                Code = 103,
                Version = "V1.0.3"
            };


绑定代码

<TextBlock  Text="{Binding Version}" />

三、 总结

  • 文章中许多话语是根据自己在项目开发时使用的理解,可能表述不当或者错误,希望得到指出。
  • 数据绑定是开发一个应用必不可少的东西。不用数据绑定也可开发一些应用程序。但在有些情况,不用数据绑定可以就不好实现,甚至无法实现 ,如:列表渲染,自定义控件,组件开发等。

你可能感兴趣的:(WPF,c#)