【WPF学习】第二十九章 元素绑定——将元素绑定到一块儿

  数据banding的最简单情形是,源对象时WPF元素并且源属性是依赖性属性。前面章节解释过,依赖项属性具备内置的更改通知支持。所以,当在源对象中改变依赖项属性的值时,会当即更新目标对象中的绑定属性。这正是咱们所须要的行为——并且没必要为此构建任何额外的基础结构。express

  为理解如何将一个元素绑定到另外一个元素,下面建立一个简单的示例。该示例窗口包含了两个控件:一个Slider控件和一个具备单行文本的TextBlock控件。若是向右拖动滑动条上的滑块,文本字体的尺寸会当即随之增长。若是向左拖动滑块,字体尺寸会缩小。ide

 

 

   显然,使用代码建立这种哦弄个行为不是很难。可简单地响应Slider.ValueChanged事件,并将滑动条控件的当前值复制到TextBlock控件来实现这种行为。不过,经过数据绑定实现这种行为更简单。学习

1、绑定表达式测试

  当使用数据绑定时,没必要对源对象(在本例中是Slider控件)作任何改动。只须要配置源对象使其属性具备正确的值范围,一般进行以下配置:字体

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

  绑定时在TextBlock元素中定义的。在此没有使用字面值设置FontSize属性,而是使用了一个绑定表达式,以下所示:编码

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

  数据绑定表达式使用XAML标记扩展(所以具备花括号)。由于正在建立System.Windows.Data.Binding类的一个实例,因此绑定表达式以单词Binding开头。尽管可采用多种方式配置Binding对象,但本例中只须要设置两个属性:ElementName属性(指示源元素)和Path属性(指示源元素中的属性)。spa

  之因此使用名称Path而不是Property,是由于Path可能指向属性的属性(如FontFamily.Source),也可能指向属性使用的索引器(如Content.Children[0])。可构建具备多级层次的路径,使其指向水属性的属性的属性,一次类推。3d

  若是但愿引用附加属性(在另外一个类中定义但应用于绑定元素的属性),那么须要在圆括号中封装属性名称。例如,若是绑定到Grid控件中的某个元素,路径(Grid.Row)将检索放置元素的行的行号。双向绑定

2、绑定错误调试

  WPF不会引起所以来通知与数据绑定相关的问题。若是指定的元素或属性不存在,那么不会受到任何指示;相反,指示不能在目标属性中显示数据。

  咋一看,对调试而言这像是可怕的梦魇。幸运的是,WPF输出了绑定失败细节的跟踪信息。当调试应用程序时,该信息显示在Visual Studio的Output窗口中。

  当试图读取源属性时,WPF会忽略抛出的任何异常,并不加提示地丢弃因数据没法转换为目标属性的数据类型而引起的异常。然而,当处理这些问题时还有一种选择——可通知WPF改变源元素的外观以指示发生了错误。例如,当使用哦感叹号图标或红色轮廓标识非法输入。

3、绑定模式

  数据绑定的一个特性是目标会被自动更新,而不考虑源的修改方式。在这个示例中,源只能经过一种方式进行修改——经过用户与滑动条上滑动进行的交互。下面分析该例的一个稍经修改的版本:添加一个按钮,每一个按钮为滑动条应用一个预先设置的值:

 

 

   当单击Set to Large按钮时,会运行下面的代码:

private void cmd_SetLarge(object sender, RoutedEventArgs e)
        {
            sliderFontSize.Value = 30;
        }

  上面的代码设置滑动条的值,这会经过数据绑定强制改变字体大小。效果与移动滑动条上的滑块同样。

  然而,下面的代码不能正常工做:

private void cmd_SetLarge(object sender, RoutedEventArgs e)
        {
            lblSampleText.FontSize = 30;
        }

  上面的代码直接设置文本框的字体尺寸。所以,滑动条的位置未响应地更新。更糟的是,上面的代码破坏了字体尺寸的绑定,并用字面值代替了绑定。若是如今移动滑动条上的滑块,文本框根本不会响应地进行改变。

  有趣的是,可采用一种方式强制在两个方向传递数据:从源到目标以及从目标到源。技巧是设置Binding对象的Mode属性。下面的是修订后过的双向绑定,该绑定容许为源或目标应用变化,并使总体的其余部分自动更新自身:

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

  在这个示例中,没有理由使用双向绑定(这须要更大的开销),由于可经过使用正确的编码来解决问题。然而,考虑该例的一个变体,该变体包含一个可在其中精确设置字体尺寸的文本框。这个文本框须要使用双向绑定,从而当经过另外一个方法改变字体尺寸时,该文本框能够应用用户的改变,并显示最新的尺寸值。

  当设置Binding.Mode属性时,WPF容许使用5个System.Windows.Data.BindingMode枚举值中的任何一个。下表列出了所有枚举值:

表 BindingMode枚举值

 

 

   下图显示了他们之间的区别。前面已经介绍了OneWay和TwoWay模式。OneTime模式很是简单。下面对其余两种模式再进行一些分析。

 

 

 

 图 绑定两个属性的不一样方式

  一、OneWayToSource模式

  你可能会好奇,既然有了OneWay模式,为何还有OneWayToSource模式选项——毕竟这两个值都以相同方式建立单向绑定。惟一区别是绑定表达式的放置位置。本质行,OneWayToSource模式容许经过在一般被视为绑定源的对象中放置绑定表达式,从而翻转源和目标。

  使用这一技巧最多见的缘由是要设置非依赖项属性的属性。前面开始介绍过,绑定表达式只能用于设置依赖项属性。但经过使用OneWayToSource模式,可克服这一限制,但前提是提供数据的属性自己是依赖项属性。

  二、Default模式

  最后,除非明确指定其余模式,不然可能认为全部绑定都是单向的,这看起来像是符合逻辑的(毕竟,简单的滑动条示例使用的就是这种方式)。然而,状况并不是如此。为了自我验证这一事实,在此考虑具备可以改变字体尺寸的绑定文本框的示例。即便删除了Mode=TwoWay设置,这个示例也仍工做的很好。这是由于WPF使用了一种不一样的、默认状况下依赖于所绑定属性的模式(从技术角度看,在每一个依赖项属性中都有一个元数据——FrameworkPropertyMetadata.BindsTwoWayByDefault标志——该标志指示属性是使用单向绑定仍是双向绑定)。

  一般,默认绑定模式也可正是指望的模式。然而,可设想一个示例,该例具备一个只读的不容许用户改变的文本框。对于这种状况,经过将模式设置为单向绑定可稍微下降一些开销。

  做为一条经常使用的经验法则,明确设置绑定模式永远不是坏主意。即便在文本框示例中,也值得经过包含Mode属性来强调但愿使用双向绑定。

4、使用代码建立绑定

  在构建窗口时,在XAML标记中使用Binding标记扩展来声明绑定表达式一般最高效。然而,也可以使用代码建立绑定。

  下面的代码演示了上面示例中显示的TextBlock元素建立绑定:

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

  还可经过代码使用BindingOperation类的两个静态方法移除绑定。ClearBinding()方法使用依赖项属性(该属性具备但愿删除的绑定)的引用做为参数,而ClearAllBindings()方法为元素删除全部数据绑定:

BindingOperations.ClearAllBindings(lblSampleText);

  ClearBinding()和ClearAllBindings()方法都使用ClearValue()方法,每一个元素都从DependencyObject基类继承了ClearValue()方法。ClearValue()方法简单地移除属性的本地值(对于这种状况,是数据绑定表达式)。

  基于标记的绑定比经过代码建立的绑定更常见,由于基于脚本的绑定更清晰而且须要完成的工做更少。通常使用标记建立它们的绑定,但在一些特殊状况下,会但愿使用代码建立绑定:

  •   建立动态绑定:若是但愿根据其余运行时信息修改绑定,或者根据环境建立不一样的绑定,这时使用代码建立绑定一般更合理(此外,也可在窗口的Resource集合中定义可能但愿使用的每一个绑定,并只添加是使用合适的绑定对象调用SetBinding()方法的代码)。
  •   删除绑定:若是但愿删除绑定,从而能够经过普通方式设置属性,须要借助ClearBinding()或ClearAllBindings()方法。仅为属性应用新值是不够的——若是正在使用双向绑定,设置的值会传播到连接的对象,而且两个属性保持同步。
  •   建立自定义控件:为让他人能更容易地修改你构建的自定义控件的外观,须要将特定细节(如事件处理程序和数据绑定表达式)从标记移到代码中。

5、使用代码检索绑定

  可以使用代码检索绑定并检查其属性,而没必要考虑绑定最初是用代码仍是标记建立的。

  可采用两种方式来获取绑定信息。第一种方式是使用静态方法BindingOperations.GetBinding()来检索相应的Binding对象。这须要提供两个参数:绑定元素以及具备绑定表达式的属性。

  例如,若是具备以下绑定:

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

  可以使用以下代码来获取绑定:

Binding binding=BindingOperations.GetBinding(lblSampleText,TextBlock.FontSize);

  一旦拥有绑定对象,就能够检查其属性。例如,绑定元素名Binding.ElementName提供了绑定表达式的值(这里是sliderFontSize)。Binding.Path提供的PropertyPath对象从绑定对象提取绑定值,Binding.Path.Path获取绑定属性的名称(这里是Value)。还有Binding.Mode属性,用于告知绑定合适更新目标元素。

  若是必须在测试时添加诊断代码,绑定对象会有趣一些。但WPF还容许经过调用BindingOperations.GetBindingExpression()方法得到更实用的BindingExpression对象,该方法的参数与GetBinding()方法的参数相同:

BindingExpression expression = BindingOperations.GetBindingExpression(lblSampleText, TextBlock.FontSize);

  BindingExpression对象包括一些属性,用于复制Binding对象提供的信息。但迄今为止,最有趣的是ResolvedSource属性,该属性容许计算绑定表达式并得到其结果——传递的本地数据。下面举一个例子:

//Get the source element
Slider boundObject=(Slider)expression.ResolvedSource;

//Get any data you need from the source element,including it's bound property
string boundData=boundObject.FontSize;

6、多绑定

  上面的示例仅包含一个绑定,但若有必要,可设置TextBlock元素从文本框中获取其文本,从单独的颜色列表中选择当前前景色和背景色等等,下面是一个示例:

<Window x:Class="DataBinding.MultipleBindings"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MultipleBindings" Height="300" Width="300">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>

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

        <TextBox Name="txtContent" Margin="3" Grid.Row="2">Sample Content</TextBox>

        <ListBox Margin="3" Grid.Row="3" Name="lstColors">
            <ListBoxItem Tag="Blue">Blue</ListBoxItem>
            <ListBoxItem Tag="DarkBlue">Dark Blue</ListBoxItem>
            <ListBoxItem Tag="LightBlue">Light Blue</ListBoxItem>
        </ListBox>

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

    </Grid>
</Window>
MultipleBindings

  最终效果以下图所示:

 

 7、绑定更新

  下面一个简单的示例,当用户在文本框中输入字体大小时,发现文本字体大小并无当即变化,而是须要失去当前控件的焦点才会触发。

 

   会发生此问题的愿意,是由于他们的行为由Binding.UpdateSourceTrigger属性控制,该属性可以使用下表列出的某个值。当从文本框中取得文本并用于更新TextBlock.FontSize属性时,看到的正式使用UpdateSourceTrigger.LostFocus方法从目标向源进行更新的例子:

表  UpdateSourceTrigger枚举值

 

   请记住,上表列出的值不印象目标的更新方式。他们仅控制TwoWay或OneWayToSource模式的绑定中源的更新方式。

  根据上面介绍的内容,可改进文本框示例,从而当用户在文本框中输入内容时将变化应用于字体尺寸。方式以下:

<TextBox Name="txtBound" Text="{Binding ElementName=lblSampleText, Path=FontSize, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Width="100"></TextBox>

  要彻底控制源对象的更新时机,可选择UpdateSourceTrigger.Explicit模式。若是在文本框示例中使用这种方法,当文本框失去焦点后不会发生任何事情。反而,由编写代码手动触发更新。例如,可添加Apply按钮,调用BindingExpression.UpdateSource()方法,触发当即刷新行为并更新字体尺寸。

  固然,在调用BindingExpression.UpdateSource()以前,须要一种方法来获取BindingExpression对象。BindingExpression对象仅是将两项内容封装到一块儿的较小组装包,这两项内容是:已经学习过的Binding对象(经过BindingExpression.ParentBinding属性提供)和由源绑定的对象(BindingExpression.DataItem)。此外,BindingExpression对象为触发当即更新绑定的一部分提供了两个方法:UpdateSource()和UdateTarget()方法。

  为获取BindingExpression对象,须要使用GetBindingExpression()方法,并传入具备绑定的目标属性,每一个元素都从FrameworkElement基类继承了该方法。下面的示例根据当前文本框中的文本改变TextBlock的字体大小:

BindingExpression binding=txtFontSize.GetBindingExpression(TextBox.TextProperty);

binding.UpdateSource();

8、绑定延迟

  在极少数状况下,须要防止数据绑定触发操做和修改源对象,至少在某一时刻是这样的。例如,可能想在从文本框复制信息以前暂停,而不是在每次按键后获取。或者,源对象在数据绑定属性变化时执行处理器密集型操做。在此状况下,可能更添加短暂的延迟时间,避免过度频繁地触发操做。

  在这些特殊状况下,可以使用Binding对象Delay属性。等到数毫秒,以后再提交更改。下面是文本框示例的修改版本,会在用户中止输入500毫秒后更新源对象:

<TextBox Name="txtBound" Text="{Binding ElementName=lblSampleText, Path=FontSize, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, Delay=500}" Width="100"></TextBox>
相关文章
相关标签/搜索