WPF中Binding的多种写法介绍

小占时光 2024-09-03 15:21:51 1837


一、 简介

       WPF(Windows Presentation Foundation)中的数据绑定(Binding)是连接 UI 和数据源的关键机制。它允许界面元素直接绑定到数据源(如对象、集合等),并根据数据的变化自动更新 UI,而无需手动操作。这种机制极大地提高了开发效率和代码的可维护性,是 MVVM(Model-View-ViewModel)模式的核心之一。

Binding 的基本概念

数据源(Source):这是绑定的源数据,通常是一个对象或集合。它可以是 UI 控件、后台代码中的属性、静态资源或外部数据源。

目标(Target):绑定目标是要显示或使用数据的 UI 元素的属性,如 `TextBox.Text`、`Slider.Value` 等。

路径(Path):路径指定从数据源中获取值的属性路径,如 `Person.Name`、`Order.TotalAmount`。

绑定模式(Mode):
       OneWay:数据从源到目标单向流动。
       TwoWay:数据在源与目标之间双向流动,源和目标的变化都会同步更新对方。
       OneWayToSource:数据从目标到源单向流动。
       OneTime:绑定在初始化时从源到目标传递一次,以后不再更新。
       Default:由目标属性的默认绑定行为决定,通常为 `OneWay` 或 `TwoWay`。

更新触发器(UpdateSourceTrigger):该属性决定了目标属性何时更新绑定的源属性。常见的触发方式包括:
       PropertyChanged:当目标属性发生变化时立即更新源属性。
       LostFocus:当目标控件失去焦点时更新源属性。
       Explicit:只有在手动调用 `UpdateSource()` 方法时才更新源属性。

INotifyPropertyChanged 接口

INotifyPropertyChanged 是 WPF 数据绑定中至关重要的接口,它允许对象在其属性值发生变化时通知 UI 进行更新。

实现 INotifyPropertyChanged:
       通过实现 `INotifyPropertyChanged` 接口的 `PropertyChanged` 事件,在属性值改变时触发事件,通知绑定系统更新 UI。这是实现 `TwoWay` 和 `OneWay` 数据绑定的基础。通过这种方式,UI 可以在后台数据改变时自动更新,无需手动刷新。

public class Person : INotifyPropertyChanged
  {
      private string name;
      public string Name
      {
          get { return name; }
          set
          {
              if (name != value)
              {
                  name = value;
                  OnPropertyChanged(nameof(Name));
              }
          }
      }

      public event PropertyChangedEventHandler PropertyChanged;

      protected void OnPropertyChanged(string propertyName)
      {
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
  }

二、Binding的多种写法

1. 简单属性绑定

<TextBlock Text="{Binding Path=Name}" />

绑定到当前数据上下文(DataContext)的 Name 属性。

2. 绑定到子属性

<TextBlock Text="{Binding Path=Person.Name}" />

绑定到 Person 对象的 Name 子属性。

3. 使用 ElementName 绑定

<TextBlock Text="{Binding Path=Text, ElementName=OtherTextBox}" />

绑定到同一个视图中名为 OtherTextBox 的元素的 Text 属性。此种写法收到世界树和命名空间的影响,可以写成

<TextBlock Text="{Binding Path=Text, Source={x:Reference Name=OtherTextBox}}" />

4. 使用 Source 绑定

<TextBlock Text="{Binding Path=Name, Source={StaticResource MyDataSource}}" />

绑定到静态资源 MyDataSource 的 Name 属性。

5. 使用 RelativeSource 绑定

 <!-- 绑定到自身 -->
 <TextBlock Text="{Binding Path=ActualWidth, RelativeSource={RelativeSource Self}}" />
 <!-- 绑定到父元素 -->
 <TextBlock Text="{Binding Path=DataContext.Title, RelativeSource={RelativeSource AncestorType=Window}}" />
 <!-- 绑定到模板父级 -->
 <ControlTemplate TargetType="Button">
     <Border Background="{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}}">
         <ContentPresenter />
     </Border>
 </ControlTemplate> 绑定兄弟的文字(绑定到同一个父级下面的元素)
 <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=StackPanel},Path=Children[0].Text}"></TextBlock>

Self: 绑定到元素自身。
AncestorType: 绑定到指定类型的祖先元素。
TemplatedParent: 绑定到模板父级。

6. 绑定静态属性

<TextBlock Text="{Binding Path=StaticProperty, Source={x:Static local:MyClass.StaticProperty}}" />

绑定到静态属性 MyClass.StaticProperty。

7. 多重绑定(MultiBinding)

<TextBlock>
    <TextBlock.Text>
        <MultiBinding StringFormat="{}{0} {1}">
            <Binding Path="FirstName" />
            <Binding Path="LastName" />
        </MultiBinding>
    </TextBlock.Text>
</TextBlock>

使用多个绑定的值生成一个字符串。

8. 优先绑定(PriorityBinding)

<TextBlock>
    <TextBlock.Text>
        <PriorityBinding>
            <Binding Path="FirstChoice" />
            <Binding Path="SecondChoice" />
        </PriorityBinding>
    </TextBlock.Text>
</TextBlock>

绑定优先使用第一个可用的绑定源。

9. 带转换器的绑定

<TextBlock Text="{Binding Path=IsChecked, Converter={StaticResource BoolToTextConverter}}" />

使用 IValueConverter 将数据源的值转换为目标值。

10. 绑定模式设置

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

Mode=TwoWay: 数据源和目标属性双向绑定。
UpdateSourceTrigger=PropertyChanged: 每当目标属性变化时更新数据源。

11. 绑定默认值(FallbackValue, TargetNullValue)

<TextBlock Text="{Binding Path=Name, FallbackValue='Unknown', TargetNullValue='N/A'}" />

FallbackValue: 当绑定失败时使用的值。
TargetNullValue: 当绑定值为 null 时使用的值。

12. 绑定表达式

<TextBlock Text="{Binding Path=Amount, StringFormat={}{0:C}}" />

使用 StringFormat 格式化绑定结果,例如货币格式。

13. 异步绑定

<TextBlock Text="{Binding Path=Data, IsAsync=True}" />

IsAsync=True: 绑定异步获取数据,避免阻塞 UI 线程。

14. 绑定到集合的索引

<TextBlock Text="{Binding Path=Names[0]}" />

绑定到集合的第一个元素。

15. 绑定到 Dictionary 的键

<TextBlock Text="{Binding Path=MyDictionary[KeyName]}" />

绑定到字典中特定键的值。

16. 绑定命令

<Button Command="{Binding SaveCommand}" Content="Save" />

绑定到 ViewModel 中的命令。

17. 绑定到动态资源

<Window x:Class="DynamicResourceExample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="DynamicResource Example" Height="200" Width="300">
    <Window.Resources>
        <!-- 定义动态资源 -->
        <System:String x:Key="MyDynamicText">Initial Text</System:String>
    </Window.Resources>
    <StackPanel>
        <!-- 使用 DynamicResource 绑定到资源字典中的 MyDynamicText -->
        <TextBlock Text="{DynamicResource MyDynamicText}" FontSize="24" Margin="20"/>
        <!-- 按钮点击时修改动态资源 -->
        <Button Content="Change Text" Width="100" Margin="20" Click="Button_Click"/>
    </StackPanel>
</Window>

C#代码

using System.Windows; 
namespace DynamicResourceExample
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        { 
            InitializeComponent();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        { 
            // 修改动态资源的值
            this.Resources["MyDynamicText"] = "Updated Text";
        }
    }
}

  绑定到一个动态资源,支持资源的实时更新。

18. 扩展写法(多行 XAML 语法)

<TextBlock>
    <TextBlock.Text>
        <Binding Path="Name" Mode="OneWay" UpdateSourceTrigger="PropertyChanged" />
    </TextBlock.Text>
</TextBlock>

使用 元素展开写法,便于在复杂场景下配置多个绑定属性。

三、TemplateBinding 使用

      TemplateBinding 是 WPF 中一种特殊的绑定方式,用于在控件模板(ControlTemplate、DataTemplate 等)中绑定控件自身的属性。它主要用于将控件的属性值直接传递给模板中的元素,从而使模板中的元素能够自动反映控件属性的变化。时绑定到父级的一种缩写。

<!-- 绑定到模板父级 -->
<ControlTemplate TargetType="Button">
    <Border Background="{Binding Path=Background, RelativeSource={RelativeSource TemplatedParent}}">
        <ContentPresenter />
    </Border>
</ControlTemplate>
<!---同样效果-->
<ControlTemplate TargetType="Button">
    <Border Background="{TemplateBinding Background}">
        <ContentPresenter Content="{TemplateBinding Content}" />
    </Border>
</ControlTemplate>

尽管 TemplateBinding 是一个高效且常用的绑定方式,但它确实有一些限制。以下是 TemplateBinding 的一些主要限制:

1. 只能在控件模板中使用
        TemplateBinding 只能用于 ControlTemplate、DataTemplate 和 ItemsPanelTemplate 等模板中,不能在普通的 XAML 代码中使用。
示例:TemplateBinding 通常用于在 ControlTemplate 中绑定到控件的 DependencyProperty。

<ControlTemplate TargetType="Button">
    <Border Background="{TemplateBinding Background}">
        <ContentPresenter Content="{TemplateBinding Content}" />
    </Border>
</ControlTemplate>

2. 只能绑定到依赖属性(DependencyProperty)

       TemplateBinding 只能绑定到依赖属性,而不能绑定到普通的 CLR 属性。因为 TemplateBinding 是一种轻量级的绑定,它直接与依赖属性的机制结合,无法处理非依赖属性。
例如,TemplateBinding 可以绑定到 Background 或 Content 等依赖属性,但不能绑定到普通的 C# 属性

3. 单向绑定

       TemplateBinding 默认是单向绑定(OneWay),即控件的属性变化会反映到模板元素上,但模板元素的属性变化不会反过来更新控件的属性。
如果需要双向绑定,需要使用普通的 Binding 而不是 TemplateBinding。

<ControlTemplate TargetType="Button">
    <TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content, Mode=TwoWay}" />
</ControlTemplate>

4. 不能使用 Converter 或其他绑定功能

      TemplateBinding 是简化版的绑定,不能使用绑定中的 Converter、StringFormat 或 FallbackValue 等功能。

如果需要这些功能,就必须使用常规的 Binding,例如:

<ControlTemplate TargetType="Button">
    <TextBlock Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource MyConverter}}" />
</ControlTemplate>

5. 不支持异步绑定

       TemplateBinding 不支持异步绑定,因为它不能设置 IsAsync 属性。如果需要异步绑定,必须使用常规的 Binding。

6. 性能优势但功能受限

     虽然 TemplateBinding 的性能通常比常规 Binding 更高,但正因为它是简化版的绑定,其功能也相对受限。因此,在一些复杂的绑定场景中,你可能需要权衡性能与功能之间的关系。

四, Binding Source 和Relativesource 的区别

     在WPF中,Binding 是用于将UI元素绑定到数据源的核心功能,而 Source 和 RelativeSource 是 Binding 的两个常用选项,分别用于指定数据源的来源。它们的主要区别如下:

Source:

       指定固定的数据源: 使用 Source 时,你明确指定一个固定的对象作为绑定的数据源。例如,你可以绑定到一个对象的属性,甚至是一个静态资源。
       典型用法: 当数据源是一个明确的对象,并且不依赖于元素的层次结构时,使用 Source 是最直接的方式。

<TextBlock Text="{Binding Path=Name, Source={StaticResource MyDataSource}}" />

RelativeSource:

       相对于当前元素的层次结构指定数据源: 使用 RelativeSource 时,数据源是相对于当前元素在视觉树中的位置。例如,可以绑定到元素的父级、祖先,或者绑定到元素本身。
       典型用法: 当数据源是相对于绑定目标元素的位置或层次关系时(如绑定到父元素、同级元素或自身属性),使用 RelativeSource 更为方便。
常见模式:
RelativeSource.Self: 绑定到元素本身。
RelativeSource.FindAncestor: 绑定到某类型的祖先元素。
RelativeSource.TemplatedParent: 绑定到元素的模板父级(例如 ControlTemplate 或 DataTemplate)。

<TextBlock Text="{Binding Path=Width, RelativeSource={RelativeSource AncestorType=Window}}" />

Source 是用于绑定到一个明确指定的对象,而 RelativeSource 则是用于基于元素的层次结构来选择数据源。选择使用哪个取决于你的数据源是固定的还是依赖于UI元素的位置。

 

最后一次修改 : 2025/1/23 上午3:34:43

优秀
123
分享
123
奶茶
123
文章版权声明:版权归原作者(小占时光)所有。未经明确书面许可,严禁任何个人或组织以任何形式进行商业性或非商业性的使用、转载或抄袭。
评论