• 8 WPF元素绑定


    数据绑定是一个关系,告诉WPF从一个来源对象抽取一些信息,并且使用数据绑定设置一个目标对象中的属性。目标属性总是一个依赖属性,并且它通常是WPF元素—毕竟,WPF数据绑定的终极目标是在用户界面上显示一些信息。但是,源对象能是任何事物,范围从另一个WPF元素到一个ADO.NET数据对象或仅是一个自定义的数据对象。

    捆绑几个元素在一起

    数据绑定的最简单场景:源对象是WPF元素并且源属性是依赖属性。依赖属性内建支持改变通知,当源对象的依赖属性值改变时,目标对象的绑定属性被立即更新。

    看一个例子,这个例子用滑杆控制字体的大小。

    运行

    绑定表达式

    当使用数据绑定时,不需要修改源对象。在这个例子中是滑杆,仅仅像往常一样,设置正确的范围值就行。

    <Slider Name="sliderFontSize" Margin="3"
     Minimum="1" Maximum="40" Value="10"
     TickFrequency="1" TickPlacement="TopLeft">
    </Slider>

    绑定定义在TextBlock元素上,不是使用字面值设置FontSize,而是使用绑定表达式。

    <TextBlock Margin="10" Text="Simple Text" Name="lblSampleText"
     FontSize="{Binding ElementName=sliderFontSize, Path=Value}" >
    </TextBlock>

    数据绑定表达式使用XAML标记扩展,因此有花括号。表达式以Binding开始,表明正在构造System.Windows.Data.Binding类的一个实例。尽管你可以用几种方法配置绑定对象,目前,你只需要设置两个属性:ElementName指明源元素。Path指明源元素的属性。

    之所以命名为Path而不是Property,是因为路径可能指向属性的属性(例如,FontFamily.Source),或者使用索引器(Content.Children[0])。可以使用多重点号深入属性的属性的属性,等等。

    如果希望引用一个附加属性,需要为属性名字加上括弧。例如,如果你绑定放置在网格中的一个元素,路径"(Grid.Row)”取回它所放置的行数。

    如果数据源本身是数组则使用点号索引如.[\d+]

    绑定错误

    见229页。

     

    绑定模式

    可以设置绑定对象的Mode参数,使绑定变成双路的。修改TextBlock的FontSize如下:

    FontSize="{Binding ElementName=sliderFontSize, Path=Value, Mode=TwoWay}"

    System.Windows.Data.BindingMode枚举有五个值:

    名称 描述
    OneWay 源变化,则目标变化
    TwoWay 源变化,则目标变化;目标变化,则源变化
    OneTime 在初始化时,目标根据源的值设置一次。此后源的变化与目标没有关系。
    OneWayToSource 目标变化,则源变化
    Default 如果目标是用户可设置的属性(如TextBox.Text)是TwoWay,其余的为OneWay。

    OneWayToSource

    因为目标属性必须是依赖属性,所以当真正的目标属性不是依赖属性,而真正的源是依赖属性时,可以应用这个选项。

    Default

    这一段讨论为什么默认值有时双路,有时单路。详见231页。

    用代码创建绑定

    用代码创建前面例子中的绑定:

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

    用代码移除绑定的方法是,使用BindingOperations类的两个静态方法。ClearBinding()方法引用一个依赖属性,拥有你希望移除的绑定,而ClearAllBindings()移除一个元素所有的数据绑定:

    BindingOperations.ClearAllBindings(lblSampleText);

    ClearBinding()和ClearAllBindings()都使用ClearValue()方法。从DependencyObject基类继承而来的方法。ClearValue()简单地移除一个属性的局部值(本例中,是一个数据绑定表达)。

    应尽量使用标记绑定元素,但是有时最好或者必须使用代码:

    • 创造动态绑定:基于运行时信息修改绑定,或根据情况创造另一个绑定。
    • 移除绑定:借助于ClearBinding()或ClearAllBindings()方法移除绑定,用常规办法设置属性。
    • 创建自定义控件:略。

    用代码取回绑定

    有二个办法获得绑定信息。第一个办法是使用静态的BindingOperations.GetBinding()方法取回相应的绑定对象。提供二参数:被绑定的元素,和拥有绑定表达式属性。

    例如,如果你有像这样的一个绑定:

    <TextBlock Margin="10" Text="Simple Text" Name="lblSampleText"
     FontSize="{Binding ElementName=sliderFontSize, Path=Value}" >
    </TextBlock>

    可以用下面代码获得绑定:

    var binding = BindingOperations.GetBinding(lblSampleText, TextBlock.FontSizeProperty);

    一旦获得了绑定对象,就可以使用各种属性了如:Binding.ElementName、Binding.Path、Binding.Path.Path、和Binding.Mode等等。

    调用BindingOperations.GetBindingExpression()方法可以获得BindingExpression对象,参数与GetBinding()方法一样:

    var expression = BindingOperations.GetBindingExpression(lblSampleText, TextBlock.FontSizeProperty);
    
    // 获得源元素
    var boundObject = (Slider)expression.ResolvedSource;
    
    // 从源元素获得你想要的数据,包括它的被绑定属性。
    string boundData = boundObject.FontSize;

    这个技术适用于,开始绑定数据对象,然后,使用ResolvedSource属性获得被绑定数据对象的一个引用。

    多重绑定

    同一个控件的几个属性都使用绑定。接本章开始的例子,可以增加一个文本框,用于设置TextBlock的Text属性:

    <TextBlock Margin="3" Name="lblSampleText"
      FontSize="{Binding ElementName=sliderFontSize, Path=Value}"
      Text="{Binding ElementName=txtContent, Path=Text}">
    </TextBlock>

    你也可以链接数据绑定。TextBox的Text属性链接到TextBlock.FontSize属性,后者又包含一个绑定到Slider.Value的绑定表达式。

    绑定表达式要尽可能直接绑定真正的数据源

    如何将目标属性绑定到多个源?有几种方法:

    最简单的方法是改变数据绑定模式。最后设置的属性值生效。

    <Slider Margin="3"
            TickFrequency="1" TickPlacement="TopLeft" Minimum="1" Maximum="40" 
            Value="{Binding ElementName=lblSampleText, Path=FontSize, Mode=TwoWay}">
    </Slider>
    
    <TextBox Margin="3" 
             Text="{Binding ElementName=lblSampleText, Path=FontSize, Mode=TwoWay}">
    </TextBox>
    
    <TextBlock Margin="3" Name="lblSampleText" FontSize="10">
        Sample Text
    </TextBlock>

    这个例子,既可以通过Slider,也可以通过TextBox控制文本的大小。

    双路绑定使绑定表达式的位置非常灵活。但是,绑定的方法应该符合逻辑。在每个源元素设置数据表达式。在目标元素上设置初始值。

    绑定更新

    当使用单路或双路绑定时,改变的值会立即从源到目标传播。反方向从目标到源却不一定立即发生。值回传的行为取决于Binding.UpdateSourceTrigger属性:

    名字 描述
    PropertyChanged 当目标属性改变时,源被立即更新。
    LostFocus 当目标属性改变并且目标失去焦点时,源被更新
    Explicit 源不被更新除非调用BindingExpression.UpdateSource()方法。
    Default 更新行为取决于目标属性的元数据(从技术上,它的FrameworkPropertyMetadata.DefaultUpdateSourceTrigger属性)。大多数属性的默认行为是PropertyChanged,而TextBox.Text属性的默认行为是LostFocus。

    记住这个表的值对于目标值如何更新是无效的,他们只在TwoWay或OneWayToSource绑定中控制源是如何更新的。

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

    也可以使用UpdateSourceTrigger.Explicit模式,添加一个按钮更新源元素,按钮的代码如下:

    // 获得文本框的绑定对象
    var binding = txtFontSize.GetBindingExpression(TextBox.TextProperty);
    
    // 更新链接的源(TextBlock)
    binding.UpdateSource();

    绑定延迟

    绑定对象有个Delay属性,表示提交改变以后等待若干毫秒更新源对象。

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

    绑定到非元素对象

    数据绑定对数据源对象仅有的要求是要显示的信息必须是公有属性。

    要绑定到非元素对象,你要去掉ElementName属性,用下面属性之一代替:

    Source:这是直接指向源对象的一个引用,换句话说,提供数据的对象。

    RelativeSource:这是使用RelativeSource对象指向源对象的一个引用。RelativeSource对象允许你相对于当前元素(持有绑定表达式的元素)指定数据源。RelativeSource属性是一个特殊的工具。常用于控件模板和数据模板。

    DataContext:如果你没有指定一个数据源,WPF从当前元素开始向上搜索元素树。检测每个元素的DataContext属性,使用第一个不为空。DataContext属性适用于绑定相同对象的几个属性到不同的元素,因为你可以在更上层容器对象上设置DataContext属性,而不是直接在目标元素上设置它。

    Source

    获得数据最简单的方法是,设置Source为静态对象。

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

    另一个办法是绑定数据源到事先定义的资源。

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

    RelativeSource

    RelativeSource属性允许你基于它和目标的相对关系指向数据源。例如,你能使用RelativeSource属性绑定一个元素到它自己,或绑定到父元素,或父元素的父元素。

    设置RelativeSource属性需要创建一的RelativeSource对象,除了使用扩展标记,还可以通过属性元素设置它。下面代码是如何在TextBlock中显示窗口的标题:

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

    用扩展标记完成同样的功能,这里需要结合使用Binding和RelativeSource扩展标记:

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

    RelativeSourceMode 枚举值:

    名字 描述
    Self 表达式绑定到同一个元素的另一个属性
    FindAncestor 表达式绑定到祖先元素。WPF沿着元素树向上搜索,直到发现要找的祖先元素。为了指定祖先元素,你必须也设置AncestorType属性,指出要找的祖先元素类型。可选地,你使用AncestorLevel属性,忽略一定数量的指定元素。例如,如果你希望绑定到沿树向上第三个ListBoxItem类型的元素,你将设置AncestorType={x:Type ListBoxItem}和AncestorLevel=3,因此掠过前两个ListBoxItem。默认,AncestorLevel是1,搜索发现第一次匹配时就停止。
    PreviousData 表达式绑定到数据绑定列表的前一个数据项,你将会在列表项目使用。
    TemplatedParent 表达式绑定到应用模板的元素。这个模式只应用于控件模板或者数据模板的内部。

    源对象和目标对象在标记不同部分,在控件模板和数据模板时。例如,如果你建立一个数据模板,改变项目在列表呈现的方式,你可能需要访问顶层的列表框对象读取一个属性。

    DataContext

    设置上下文属性,可以通过属性元素,静态属性,或者资源,同Binding.Source的设置方式。使用上下文的示例:

        <StackPanel DataContext="{x:Static SystemFonts.IconFontFamily}">
            <TextBlock Text="{Binding Path=Source}"></TextBlock>
            <TextBlock Text="{Binding Path=LineSpacing}"></TextBlock>
            <TextBlock Text="{Binding Path=FamilyTypefaces[0].Style}"></TextBlock>
            <TextBlock Text="{Binding Path=FamilyTypefaces[0].Weight}"></TextBlock>
        </StackPanel>

    当绑定表达式的source信息缺失,WPF检查元素的DataContext属性。如果它为空,沿元素树向上搜索,寻找第一个非空数据上下文。(所有元素的DataContext属性初始化为空)如果WPF发现一个非空数据上下文,使用它到绑定。如果没有找到,绑定表达式不应用任何值到目标属性。

  • 相关阅读:
    Python】ufunc 'subtract' did not contain a loop with signature matching types dtype
    AttributeError: module 'mediapipe.python.solutions.holistic' has no attribute 'UPPER_BODY_POSE_CONNECTIONS'
    __init__() got an unexpected keyword argument 'upper_body_only'
    解决:Every derived table must have its own alias
    VUE 利用 xlsx 和 filesaver 实现 Excel 表格的导入与导出
    Opencv基础运用
    Opencv图像处理
    Opencv图像简单操作
    js逆向破解之Hook Cookie
    更友好的格式化数据提取方案
  • 原文地址:https://www.cnblogs.com/cuishengli/p/3033881.html
Copyright © 2020-2023  润新知