WPF新手村教程(五)— 附魔教学(绑定)

WPF个人文档(五)------ 绑定


!IMPORTANT

在开始之前,我觉得我们非常有必要要先了解一下ViewModel

  • ViewModel:专门给界面(View)使用的数据对象

shell 复制代码
# ViewModel = 专门给界面(View)使用的数据对象
如果只讲绑定,可以简单理解为数据源对象
在这里先留一个简单的印象,后面会详细讲解,在看完本篇随笔之后,你也会对这个东西有一个较为深刻的印象

# 常用于MVVM架构(此架构我们以后再详细讲解)
Model  →  ViewModel  →  View
数据        UI数据        界面

!NOTE

  • WPF中,绑定的本质实际上就是在找东西

  • 换句话就是:**WPF的一切绑定,本质都是在找 数据源 **

  • 只不过 ------ 数据源到底是 对象里的数据 ,还是 界面里的控件,这个就得看你的代码了

    shell 复制代码
    # 根据数据源的位置,WPF绑定通常会被分成两大类
    
    绑定
    ├─ 元素绑定(Element Binding)
    └─ 非元素绑定(Non-Element Binding)

一.元素绑定

  • !NOTE

    WPF ------ 绑定

    这里,我们来看看官方对于绑定的解释

    • WPF 元素绑定:将UI元素属性与数据源对象建立连接的机制,能在数据变化时自动更新界面,或在界面修改时同步数据源
      • 它支持 .NET 对象XML集合 等多种数据源,并可通过 Binding 对象灵活配置
      • 🌱**Binding = 在 UI 属性 和 数据源 之间建立连接**

    示例:将按钮背景色绑定到数据对象的属性

    • 此处 Background 是绑定目标属性,ColorName 是绑定源属性,通过 Path 指定
    xml 复制代码
    <DockPanel
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:c="clr-namespace:SDKSample">
         <DockPanel.Resources>
             <c:MyData x:Key="myDataSource" ColorName="Red"/>
         </DockPanel.Resources>
         <Button Background="{Binding Source={StaticResource myDataSource}, Path=ColorName}"
                 Width="150" Height="30">
             我会变成红色!
         </Button>
    </DockPanel>

    绑定的核心要素

    • 目标对象与属性:必须是依赖属性(DependencyProperty)
    • 源对象与路径 :可为对象、集合、XML等,通过 PathXPath 指定
    • 数据上下文(DataContext):未显式指定源时,从父元素继承
    • 模式(Mode)
      • OneWay:源 → 目标
      • TwoWay:双向同步
      • OneWayToSource:目标 → 源
      • OneTime:初始化一次
    • 触发器(UpdateSourceTrigger) :如 PropertyChangedLostFocus 控制何时更新源

    集合绑定与视图

    • 绑定到集合时使用 ItemsSource
    xaml 复制代码
    <ListBox ItemsSource="{Binding MyItems}" />
    • 若需排序、筛选、分组,可用 CollectionViewSource
    xaml 复制代码
    <CollectionViewSource x:Key="view" Source="{Binding MyItems}" />
    <ListBox ItemsSource="{Binding Source={StaticResource view}}" />

    数据转换与验证

    • 类型不匹配时可实现 IValueConverter 转换值
    • 可通过 ValidationRule 添加验证逻辑,并结合 ErrorTemplate 提供视觉反馈

    注意事项

    • 源对象应实现 INotifyPropertyChanged,集合应实现 INotifyCollectionChanged 以支持动态更新
    • 合理选择绑定模式和触发器可优化性能与交互体验

    这样,WPF 数据绑定不仅能减少手动更新 UI 的代码量,还能保持业务逻辑与界面的清晰分离


  • 元素绑定:让一个 UI 控件的属性直接依赖另一个 UI 控件的属性,即:一个 UI 控件属性绑定到另一个 UI 控件属性

    • 元素指UI控件,数据源也是控件属性
    • 非常很多博主在他们的教程中都说的是,绑定就是控件绑定什么什么数据源,实际上他们说的控件是值控件属性
复制代码
  <!-- 绑定语法:{Binding ElementName=源控件名, Path=源属性, Mode=绑定模式} -->

!CAUTION

当你手动给一个绑定属性赋值时,WPF 会把这个 Binding 直接移除

这里用一个实例代码演示一下,如果你无法看明白,可以当作元素绑定的一个引题,在看完绑定模式后,会看明白的

  • 现在有一张图片,还有两个滑块,还有一个TextBox用于显示slider.Value的数值

    shell 复制代码
    # 两个元素绑定
    slider.Value
          ↓
    Image.Opacity  # 图片透明度

    slider.Value

    TextBox.Text

    关于两个按钮

    第一个按钮:点击按钮后,滑块值变化

    复制代码
      <Button Content="滑块value变变变" Click="Button_Click"/>
      slider.Value = 0.2;

    第二个按钮:点击按钮后,图片透明度变化

    复制代码
      <Button Content="图片Opacity变变变" Click="Button_Click_1"/>
      img.Opacity = 0.8;
    复制代码
  • ❓为什么当我点击第二个按钮后,两个按钮都无法改变图片的透明度了呢?

    • 由于Opacity 是被绑定控制的:

      复制代码
      img.Opacity ← slider.Value
    • 而我们直接:

      复制代码
      img.Opacity = 0.8
    • 这会触发 WPF 的一个规则:

      复制代码
      直接设置属性 = 覆盖绑定
    • 于是系统直接变成了:

      复制代码
      img.Opacity = 0.8   (本地值)
    • 最后滑块与图片透明度之间的绑定就被移除了

      c# 复制代码
      slider.Value   ❌   img.Opacity
      
      # 于是乎,按钮仍然在工作,只是 UI 不再联动
  • 会出现这种情况的本质原因:

    • WPF 的依赖属性内部其实有一个 值优先级系统

      shell 复制代码
      Animation		# 优先级最高
      LocalValue
      Binding
      Style
      Default			# 优先级最低
    • 当我们写:

      复制代码
      img.Opacity = 0.8
    • 就产生了 LocalValue(本地值),而 LocalValue 的优先级 高于 Binding,于是 Binding 就被覆盖了


  • MainWindow.xaml

    xaml 复制代码
    <Window x:Class="Binding.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Binding"
            mc:Ignorable="d"
            SizeToContent="Height"
            Title="MainWindow" Height="470" Width="800">
        <Grid>
            <!-- slider(源属性) 绑定到 img(目标属性) -->
            <StackPanel>
                <Image x:Name="img" Source="/Images/1.png" 
                       Opacity="{Binding ElementName=slider, Path=Value, Mode=OneWay}"/>
    
                <TextBox HorizontalAlignment="Center" Text="{Binding ElementName=slider, Path=Value, Mode=Default}"/>
                <Slider x:Name="slider" Minimum="0" Maximum="1" Value="0.5"/>
    
                <Button Content="滑块value变变变" Margin="0, 3" Height="20" Width="120"
                        Click="Button_Click"/>
                <Button Content="图片Opacity变变变" Margin="0, 3" Height="20" Width="120"
                        Click="Button_Click_1"/>
            </StackPanel>
        </Grid>
    </Window>
  • MainWindow.xaml.cs

    c# 复制代码
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace Binding
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                slider.Value = 0.2;
            }
    
            private void Button_Click_1(object sender, RoutedEventArgs e)
            {
                img.Opacity = 0.8;
            }
        }
    }

二.绑定模式

在此之前,问一个问题,你觉得绑定是必须双向的吗,还是默认双向的?

答案都不是,你以为是搞嵌入式TX,RX,RS485数据可以双向传输吗,拜托,这是上位机
WPF 绑定不是必须双向,而且默认也不是双向

大多数绑定其实是 单向(OneWay) 默认绑定模式其实是"控件决定的"

默认模式不是 Binding 决定的,而是由 控件属性的 DependencyProperty 决定的

  • 绑定模式一共分为5种,1种自动挡,4种手动挡

    shell 复制代码
    BindingMode
    ├─ Default      	# 自动挡
    ├─ OneWay       	# 数据源 → UI
    ├─ TwoWay       	# 数据源 ⇿ UI
    ├─ OneWayToSource	# UI → 数据源
    └─ OneTime      	# 只初始化一次
    
    # 这只狐狸🦊还是这么喜欢树状图
    • 数据流行为 看:真正的模式只有 4 种,但从API 的角度 看:BindingMode 一共有 5 个枚举值

    绑定模式 数据流向
    OneWay 数据源 → UI
    TwoWay 数据源 ↔ UI
    OneWayToSource UI → 数据源
    OneTime 只初始化一次
    Default

1.🌱**OneWay --- 单向绑定**

  • 数据流向:数据 只从数据源流向 UI

    shell 复制代码
    ViewModel  →  UI
    
    # 如果数据改变
    VM.UserName 改变
            ↓
    TextBlock.Text 自动更新
    
    # 如果 UI 改变
    UI 改变
    不会写回 VM
    
    # 示例
    <TextBlock Text="{Binding UserName, Mode=OneWay}" />
  • 适用场景:显示数据,状态显示,只读 UI

    • 比如:温度显示,设备状态,日志信息

2.🌱**TwoWay --- 双向绑定**

  • 数据流向:数据 双向同步(数据源 ⇿ UI)

    shell 复制代码
    ViewModel  ⇿  UI
    
    # 数据改变
    VM → UI
    
    # 用户输入(UI改变)
    UI → VM
    
    # 示例
    <TextBox Text="{Binding UserName, Mode=TwoWay}" />
  • 适用场景:输入框,表单,参数设置

    • 比如:设备参数,用户名输入,数值调整

3.🌱**OneWayToSource --- 单向反向绑定**

  • 数据流向:数据 只从 UI 写回数据源

    shell 复制代码
    UI  →  ViewModel
    
    # 数据源改变
    不会更新 UI
    
    # 用户输入(UI改变)
    UI → VM
    
    # 示例
    <TextBlock Tag="{Binding WidthValue, Mode=OneWayToSource}" />
  • 适用场景(比较少见):获取 UI 尺寸,获取控件状态,UI 信息回传


4.🌱**OneTime --- 仅进行一次的绑定**

  • 数据绑定:只在初始化进行一次绑定

    shell 复制代码
    # 初始化
    Data → UI
    
    # 示例
    <TextBlock Text="{Binding Version, Mode=OneTime}" />
  • 适用场景:版本号,初始化数据,静态信息

    • 优点是性能更高,因为不监听变化

5.🌱**Default --- 自动档**

  • 本质:延迟决定绑定模式,它实际上是一个占位符

    • 当没有显式指定 Mode 时,Binding 的默认值就是 Default

    shell 复制代码
    # 你写的代码
    <TextBox Text="{Binding UserName}" />
    
    # 实际上等价于
    <TextBox Text="{Binding UserName, Mode=Default}" />
  • 工作方式

    • Mode = Default 时,WPF 会去查询这个属性的 DependencyProperty 元数据

      • 你可能会问DependencyProperty是什么鬼东西,这个鬼东西其实就是依赖属性

      shell 复制代码
      # DependencyProperty只是一种带规则的属性系统
      # 它并不能直接控制绑定模式,而是由它下面的BindsTwoWayByDefault决定的
      
      # 当控件定义一个 依赖属性 时,会注册一段 Metadata(元数据)
      # 元数据中有很多配置,其中有一个非常关键的标志BindsTwoWayByDefault
      # 这个标志决定了 Default 的 绑定模式
      DependencyProperty(依赖属性)
            │
            └─ Metadata(元数据)
                 │
                 └─ BindsTwoWayByDefault
                       │
                       └─ 决定 Default 绑定模式
      • 看不懂?那我们用代码来表示一下

      c# 复制代码
      if (BindsTwoWayByDefault == true)
          Mode = TwoWay
      else
          Mode = OneWay
    • BindingMode.Default 并不是一种新的数据流模式

      • 它只是告诉 WPF:去使用该依赖属性预设的默认绑定模式

6.示例代码

  • MainWindow.xaml

    xaml 复制代码
    <Window x:Class="Binding.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Binding"
            mc:Ignorable="d"
            SizeToContent="Height"
            Title="MainWindow" Height="470" Width="800">
        <Grid>
            <!-- slider(源属性) 绑定到 img(目标属性) -->
            <StackPanel>
                <!-- 🚩你可以修改这里的Mode枚举值来尝试上面讲述的5种数据模式 -->
                <!-- 绑定语法:{Binding ElementName=源控件名, Path=源属性, Mode=绑定模式} -->
                <Image x:Name="img" Source="/Images/1.png" 
                       Opacity="{Binding ElementName=slider, Path=Value, Mode=TwoWay}"/>           
    
                <TextBox HorizontalAlignment="Center" Text="{Binding ElementName=slider, Path=Value, Mode=Default}"/>
                <Slider x:Name="slider" Minimum="0" Maximum="1" Value="0.5"/>
    
                <Button Content="滑块value变变变" Margin="0, 3" Height="20" Width="120"
                        Click="Button_Click"/>
                <Button Content="图片Opacity变变变" Margin="0, 3" Height="20" Width="120"
                        Click="Button_Click_1"/>
            </StackPanel>
        </Grid>
    </Window>
  • MainWindow.xaml.cs

    c# 复制代码
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace Binding
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                slider.Value = 0.2;
            }
    
            private void Button_Click_1(object sender, RoutedEventArgs e)
            {
                img.Opacity = 0.8;
            }
        }
    }

    !WARNING

    当我们以上面的代码使用OneWay模式时,会出现一种特殊的情况:绑定失效

    这是单向绑定,当我们使用第一个按钮时,数据是正常单向传递的

    但是当我们使用第二个按钮时,数据反向传输后,使用其他控件,会发现:没有任何变化,绑定生效了

    为什么会出现这种情况呢?

    • 因为:当你手动给一个绑定属性赋值时,WPF 会把这个 Binding 直接移除

      c# 复制代码
      // 这里的代码干了一件很关键的事:删除了 Opacity 上的 Binding
      private void Button_Click_1(object sender, RoutedEventArgs e)
      {
        	img.Opacity = 0.8;
      }
    • 这里涉及到一个优先级的问题,也就是WPF 会把这个 Binding 直接移除的原因

      • 第一个按钮:slider.Value 改变 → img.Opacity 自动变化
      • 第二个按钮:img.Opacity = 0.8;
    • WPF 的依赖属性系统会执行一个优先级规则

      shell 复制代码
      Local Value(本地值)	>	Binding		>		Style	>	Default
    • 而这句代码:

      c# 复制代码
      // 设置的是 Local Value(本地值)
      // 本地值的优先级高于 Binding
      img.Opacity = 0.8;
      
      // 于是 WPF 做了一个很干脆的动作
      	// 移除 Binding
      	// 保留 Local Value

随笔参考:

1.WPF数据绑定深度解析:告别冗余事件,掌握5种绑定模式的精髓 - blfbuaa - 博客园

2.59.第8章_绑定模式_哔哩哔哩_bilibili


三.高级数据绑定:多绑定、绑定更新、延迟绑定

  • 很多开发文档将多绑定,绑定更新,延迟绑定统称为高级数据绑定(Advanced Data Binding),

    或者绑定行为控制(Binding Behavior Control),但是不管怎么说,他们都是在做三件事情

  • 数据从哪里来?什么时候更新?如何组合?

    功能 控制内容
    多绑定MultiBinding 控制 数据来源数量
    绑定更新UpdateSourceTrigger 控制 更新时机
    延迟绑定Delay 控制 更新节奏
shell 复制代码
高级绑定特性
├─ 多源绑定
│   └─ MultiBinding
│
├─ 绑定更新控制
│   └─ UpdateSourceTrigger
│
└─ 更新节流控制
    └─ Delay
    
======================================================================
# 这只小狐狸🦊永远忘不了他的树状图了
======================================================================
# 逻辑框架理解
# WPF 的 Binding 系统其实像一条 数据管道系统
# 不同机制负责不同控制点:

        数据源
          ↓
        Binding
          ↓
[ 🌱多绑定 MultiBinding ]  	  ← # 控制数据来源数量
          ↓
        Converter
          ↓
          UI
          ↓
[ 🌱UpdateSourceTrigger ] 	 ← # 控制更新时机
  		 ↓
	 [ 🌱Delay ]               ← # 控制更新节奏
		 ↓
	  ViewModel

1.🌱多绑定(MultiBinding)

  • 一般的绑定只有一个数据源,如果我们想要多个数据源便无法实现,于是就有了多绑定

  • 多绑定实际上就是将 多个数据源合成一个值

    shell 复制代码
    # 一般的绑定 => 只有一个数据源
    Source → Target
    
    
    # 多绑定 => 多个数据源合成一个值
    多个数据 → Converter → 一个UI值
    
    # 多个 Source → 合成一个 Target
    Source1
    Source2
    Source3
       ↓
    Converter
       ↓
    Target
  • 那么,下面这段代码是多绑定吗?

    • 严格意义上来说,这里并不是多绑定,只是2个独立的单绑定的同时存在而已

    xaml 复制代码
    TextBox.Text     	← 	slider.Value
    TextBox.FontSize 	← 	sliderSize.Value
    
    <!-- 绑定语法:{Binding ElementName=源控件名, Path=源属性, Mode=绑定模式} -->
    xaml 复制代码
    <StackPanel>
        <TextBox 
            Text="{Binding ElementName=slider, Path=Value, Mode=TwoWay}"
            FontSize="{Binding ElementName=sliderSize, Path=Value, Mode=OneWay}" />
            
        <Slider 
            x:Name="slider" Margin="0,20" 
            Minimum="0" Maximum="1" 
            Value="0.5" />
            
        <Slider 
            x:Name="sliderSize" Margin="0,20" 
            Minimum="10" Maximum="50" 
            Value="20" />
    </StackPanel>
  • 下面这段代码才是真正意义上的多绑定

    shell 复制代码
    # 这里的多绑定使用流程
    1. 准备数据源
    2. 创建转换器
    3. 注册转换器
    4. 编写 MultiBinding
    5. 在 Converter 中处理数据
    
    
    # 这里的整体结构
    Binding1
    Binding2
       ↓
    MultiBinding	# 多绑定
       ↓
    Converter
       ↓
    TextBlock.Text
    
    # 翻译一下
    sliderValue.Value
    sliderSize.Value
            ↓
    SliderInfoConverter
            ↓
    TextBlock.Text
    • MainWindow.xaml

    xaml 复制代码
    <Window x:Class="Binding_Advanced_features.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Binding_Advanced_features"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Window.Resources>
            <!-- 3.注册转换器 -->
            <local:SliderInfoConverter x:Key="SliderInfoConverter"/>
        </Window.Resources>
    
        <StackPanel Margin="20">
            <!-- 显示两个Slider组合后的结果 -->
            <!-- 这里是一个单绑定,用于控制字体大小 -->
            <TextBlock FontWeight="Bold"
                       FontSize="{Binding ElementName=sliderSize, Path=Value}"
                       HorizontalAlignment="Center">
                <TextBlock.Text>
                    <MultiBinding Converter="{StaticResource SliderInfoConverter}">
                        <!-- 4.多绑定 => 用于组成字符串(TextBlock.Text) -->
                        <Binding ElementName="sliderValue" Path="Value"/>
                        <Binding ElementName="sliderSize" Path="Value"/>
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
    
            <!-- 1.准备多个数据源 -->
            <!-- 控制数值 -->
            <Slider x:Name="sliderValue"
                    Minimum="0"
                    Maximum="100"
                    Value="50"
                    Margin="0,20"/>
    
            <!-- 控制字体大小 -->
            <Slider x:Name="sliderSize"
                    Minimum="10"
                    Maximum="40"
                    Value="20"
                    Margin="0,20"/>
    
        </StackPanel>
    </Window>
    • 转换器实现SliderInfoConverter.cs

      多绑定必须通过IMultiValueConverter进行数据转换

    c# 复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Data;
    using System.Globalization;
    
    namespace Binding_Advanced_features
    {
        // 2.创建多值转换器
        // 转换器必须实现 IMultiValueConverter 接口
        // 它与常规的转换器接口不同,因为 Convert 方法必须接受一个值数组,该数组的顺序必须与 XAML 中指定的顺序完全相同
        	// 即: values[0] 对应第一个 Binding
        	//	   values[1] 对应第二个 Binding
        public class SliderInfoConverter : IMultiValueConverter
        {
            // 多个值 → 一个值
            public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
            {
                double sliderValue = (double)values[0];
                double fontSize = (double)values[1];
    
                return $"当前数值: {sliderValue:F0}  |  字体大小: {fontSize:F0}";
            }
    
            // 反向转换(这里不用)
            public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    }
    shell 复制代码
    # 数据流
    # 当滑块变化时
    sliderValue.Value 改变
    sliderSize.Value 改变
            ↓
    MultiBinding 监听到变化
            ↓
      调用 Convert()
            ↓
    	返回字符串
            ↓
    TextBlock.Text 更新
    
    # 同时
    sliderSize.Value
          ↓
    TextBlock.FontSize 更新
  • 多绑定适用场景

    • MultiBinding 常用于 组合计算 UI 或者UI状态判断

      • MultiBinding 在真实项目里最常见的用途其实是做 UI 状态判断

      • 例如:

      shell 复制代码
      # 组合计算 UI
      宽 × 高 → 面积
      单价 × 数量 → 总价
      名字 + 姓氏 → 全名
      多个条件 → 控件是否可用
      
      # UI 状态判断
         用户名是否填写
          密码是否填写
         验证码是否填写
              ↓
           全部满足
              ↓
      登录按钮 Enable
      
      # 本质
      多个 Source → 一个 Target

随笔参考:

1.DataBinding:绑定多属性MultiBinding、IMultiValueConverter - 知乎

2.60.第8章_多绑定_绑定更新_绑定延迟_哔哩哔哩_bilibili

3.数据绑定概述 - WPF | Microsoft Learn


2.🌱绑定更新(UpdateSourceTrigger)

  • 所谓的绑定更新,实际上就是考虑了一件事情:

    • UI 改变后什么时候写回数据源
  • 绑定更新(UpdateSourceTrigger)常用枚举值

    说明
    PropertyChanged 目标属性一变化就立刻更新
    LostFocus 当目标属性发生变化,且失去焦点时才更新
    Explicit 手动触发更新
    Default 自动档 大多数默认行为是PropertyChanged 但是TextBox.Text属性的默认行为是LostFocus

(1)PropertyChanged ------ 实时更新

xaml 复制代码
<TextBox Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}" />
  • 适用于:实时搜索,实时计算,实时过滤等

复制代码
```shell
# 行为

输入一个字
	↓
立刻写回 ViewModel
```

(2)LostFocus (TextBox 默认) ------ 失去焦点才更新

xaml 复制代码
<TextBox Text="{Binding UserName, UpdateSourceTrigger=LostFocus}" />
复制代码
```shell
# 行为

用户输入
  ↓
离开 TextBox
  ↓
更新数据

# 优点:减少更新次数
```

(3)Explicit ------ 手动更新

shell 复制代码
# 数据流
UI改变
   ↓
什么都不会发生
   ↓
手动触发	# GetBindingExpression + UpdateSource()
   ↓
更新 Source

  • 代码示例:

    shell 复制代码
    # 逻辑交互
    TextBox 	→ 输入
    Button 		→ 提交
    TextBlock 	→ 显示 ViewModel 数据
  • MainViewModel.cs

    c# 复制代码
    using System.ComponentModel;
    
    namespace Binding_UpdateSourceTrigger
    {
        public class MainViewModel : INotifyPropertyChanged
        {
            private string _userName = string.Empty;
    
            public string UserName
            {
                get => _userName;
                set
                {
                    _userName = value;
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(UserName)));
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
        }
    }
  • MainWindow.xaml

    xaml 复制代码
    <Window x:Class="Binding_UpdateSourceTrigger.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Binding_UpdateSourceTrigger"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <StackPanel Margin="20">
    
            <TextBlock FontSize="16" Margin="0,0,0,10">
                输入用户名(不会立即更新):
            </TextBlock>
    
            <TextBox x:Name="tbUserName"
                     Text="{Binding UserName,
                     Mode=TwoWay,
                     UpdateSourceTrigger=Explicit}"
                     Height="30"/>
    
            <Button Content="提交数据"
                    Click="Submit_Click"
                    Margin="0,15,0,0"
                    Height="30"/>
    
            <TextBlock Margin="0,20,0,0"
                       FontSize="16"
                       Text="{Binding UserName}"/>
    
        </StackPanel>
    </Window>
  • MainWindow.xaml.cs

    c# 复制代码
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace Binding_UpdateSourceTrigger
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            MainViewModel vm = new MainViewModel();
    
            public MainWindow()
            {
                InitializeComponent();
                DataContext = vm;
            }
    
            private void Submit_Click(object sender, RoutedEventArgs e)
            {
                // 1.找到 TextBox.Text 的绑定
                BindingExpression be =
                    tbUserName.GetBindingExpression(System.Windows.Controls.TextBox.TextProperty);
    
                // 2.手动更新数据源
                be.UpdateSource();
            }
        }
    }

(4)Default ------ 自动档

  • Default 本质上不是一种策略,而是一个 占位符(占坑的家伙)

    • 即:Default = 让控件自己决定

    shell 复制代码
    # 下面两个等价
    <TextBox Text="{Binding UserName}" />
    <TextBox Text="{Binding UserName, UpdateSourceTrigger=Default}" />
  • 关于工作方式,我们可以查看绑定模式中的Default,非常类似 那我就copy了,我觉得我copy我自己的东西没毛病

  • 工作方式

    • UpdateSourceTrigger = Default 时,WPF 会去查询这个属性的 DependencyProperty 元数据

      • 你可能会问DependencyProperty是什么鬼东西,这个鬼东西其实就是依赖属性

      shell 复制代码
      # DependencyProperty只是一种带规则的属性系统
      # 它并不能直接控制绑定模式,而是由它下面的UpdateSourceTrigger决定的
      
      # 当控件定义一个 依赖属性 时,会注册一段 Metadata(元数据)
      # 元数据中有很多配置,其中有一个非常关键的标志UpdateSourceTrigger
      # 这个标志决定了 Default 的 绑定模式
      DependencyProperty(依赖属性)
            │
            └─ Metadata(元数据)  (FrameworkPropertyMetadata)
                 │
                 └─ UpdateSourceTrigger
                       │
                       └─ 决定 Default 绑定模式
    • UpdateSourceTrigger.Default 并不是一种新的数据流模式

      • 它只是告诉 WPF:去使用该依赖属性预设的默认绑定更新的方式
    • 大多数属性的默认行为都是PropertyChanged,但是也有例外:TextBox.Text 的默认更新方式是LostFocus


3.🌱延迟绑定 (Delay)

  • 有些 UI 更新太频繁,比如搜索框,如果每敲一个字都触发查询,服务器负担可能较大:

    shell 复制代码
    a → 查询
    aw → 查询
    aws → 查询
    awsl → 查询
  • 这时候可以使用 Delay 减少负担

    shell 复制代码
    用户停止输入 100ms
      	↓
    才更新数据源
    xaml 复制代码
    <TextBox Text="{Binding SearchText,
                    UpdateSourceTrigger=PropertyChanged,
                    Delay=1000}" />

四.非元素对象绑定

在此之前,我们来重新认识一下**Binging.elementName** 属性(元素名称

  • Binging.elementName属性

    • elementName,翻译一下就是:元素名称
    • 它的设计目标就是------绑定到"界面元素"
    • 如果数据源不是 UI 元素,这个属性基本就该退场了
shell 复制代码
# 再来回顾一下,元素绑定绑定语法:
{Binding ElementName=源控件名, Path=源属性, Mode=绑定模式}

# 示例
<TextBlock Text="{Binding ElementName=slider, Path=Value}" />
<Slider x:Name="slider" />

Slider.Value
      ↓
TextBlock.Text

这一小节,我们来讲解非元素绑定的三种方式,当然,你也可以根据你的理解

根据不同的分类方式分类,

  • 根据绑定的对象类型 ,可以分为 :
    • 1.DataContext对象(最常用,数据上下文对象)
    • s2.静态对象
    • 3.资源对象(Resource
  • 根据绑定的数据源获取途径 (入口),分为:
    • 1.Source(显式数据源)
    • 2.RelativeSource(相对对象)
    • 3.DataContext(默认数据源)

在本小节中,我们会根据数据源获取途径继续讲解

因为刚好有现成的Demo可以白嫖.......


1.🌱Source(显式数据源 ------ 直接指定)

  • 这种数据源用最直接的方式告诉你,我们使用的是什么数据源

    • !WARNING

      ❗特别注意

      • Source理论上可以用于 任何对象 和 任何属性

        • 静态对象,资源字典对象,普通对象,ObjectDataProviderx:Reference对象,代码对象 等
      • Source 更多是 特殊情况的精确绑定工具,而不是主力绑定方式

      shell 复制代码
      # Source本质结构
      Binding
         │
         └─ Source = object 		# 一切对象的母亲
               │
               └─ Path = 属性
  • 这里我们简单介绍5种数据源,最后我会将五种数据源的整合代码放出来

(1)静态绑定 & 静态资源绑定

xaml 复制代码
<Window x:Class="Non_element_binding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Non_element_binding"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <FontFamily x:Key="my_font">
            微软雅黑
        </FontFamily>
    </Window.Resources>
    
    <Grid>
        <StackPanel>
            <!-- 1.静态绑定 - 将系统默认字体格式绑定到TextBlock-Text -->
            <TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=Source}" 
                   Margin="50" HorizontalAlignment="Center"/>

            <!-- 2.绑定到资源对象 -->
            <TextBlock Text="{Binding Source={StaticResource my_font}, Path=Source}" 
                   Margin="10" HorizontalAlignment="Center"/>
        </StackPanel>
    </Grid>
</Window>

(2)普通对象

  • User.cs

    我们添加一个额外的类,仅做演示

    c# 复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Non_element_binding
    {
        public class User
        {
            public string Name { get; set; } = string.Empty;
        }
    }
  • MainWindow.xaml

    xaml 复制代码
    <Window x:Class="Non_element_binding.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:Non_element_binding"
            xmlns:sys="clr-namespace:System;assembly=mscorlib"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Window.Resources>
            <!-- 3.普通对象(最常见) -->
            <local:User x:Key="my_user" Name="史蒂夫"/>
        </Window.Resources>
        
        <Grid>
            <StackPanel>
                <!-- 3.普通对象(最常见) -->
                <TextBlock Text="{Binding Source={StaticResource my_user}, Path=Name}" 
                           Margin="10" HorizontalAlignment="Center"/>
            </StackPanel>
        </Grid>
    </Window>

(3)ObjectDataProvider(据说是一种老派 WPF 技术)

xaml 复制代码
<Window x:Class="Non_element_binding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Non_element_binding"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <!-- 4.ObjectDataProvider -->
        <ObjectDataProvider x:Key="now"
                            ObjectType="{x:Type sys:DateTime}"
                            MethodName="Now"/>
    </Window.Resources>
    
    <Grid>
        <StackPanel>
            <!-- 4.ObjectDataProvider -->
            <TextBlock FontSize="30" Margin="10"
                       HorizontalAlignment="Center"
                       Text="{Binding Source={x:Static sys:DateTime.Now}}"/>
        </StackPanel>
    </Grid>
</Window>
  • !WARNING

    • 这里有一个需要注意的点

      • DateTime.Now 不是方法,而是属性

      xaml 复制代码
      <Window.Resources>
          <!-- 4.ObjectDataProvider -->
          <ObjectDataProvider x:Key="now"
                              ObjectType="{x:Type sys:DateTime}"
                              MethodName="Now"/>
      </Window.Resources>
      
      <TextBlock Text="{Binding Source={StaticResource now}}"/>
    • 所以如果要正确使用,有两种修改方法(上面代码使用的是第二种)

      • 1.MethodName="Now" => MethodName="get_Now"

      xaml 复制代码
      <Window.Resources>
          <!-- 4.ObjectDataProvider -->
          <ObjectDataProvider x:Key="now"
                              ObjectType="{x:Type sys:DateTime}"
                              MethodName="get_Now"/>
      </Window.Resources>
      • 2.Binding Source={StaticResource now} => Binding Source={x:Static sys:DateTime.Now}

      xaml 复制代码
      <TextBlock Text="{Binding Source={x:Static sys:DateTime.Now}}"/>

(4)x:Reference标记扩展

  • !IMPORTANT

    • x:Reference
      • x:Reference 这个东西其实是 XAML 世界里的"指针"
      • 作用:拿到某个已经存在的对象实例,然后当作 绑定的资源
        • 和常见的 ElementName 很像,但机制不一样
      • x:Reference 属于 XAML 标记扩展 ,来自XAML体系
shell 复制代码
x:Reference → 找到某个对象实例
Binding     → 从这个实例读取属性

# 数据流
对象实例
   ↓
Binding
   ↓
目标属性
  • 这里我们使用一个滑块,让 TextBlock 实时显示它的值

xaml 复制代码
<Window x:Class="Non_element_binding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Non_element_binding"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">    
    <Grid>
        <StackPanel>
            <!-- 5.标记扩展 -->
            <Slider x:Name="slider" Value="50"
                    Minimum="0" Maximum="100"/>

            <TextBlock Text="{Binding Source={x:Reference slider}, Path=Value}"
                       FontSize="30" HorizontalAlignment="Center"/>
        </StackPanel>
    </Grid>
</Window>
  • !IMPORTANT

    ElementNamex:Reference的区别

    xaml 复制代码
    <!-- ElementName -->
    <TextBlock Text="{Binding ElementName=slider, Path=Value}"/>
    <!-- x:Reference -->
    <TextBlock Text="{Binding Source={x:Reference slider}, Path=Value}"/>
    • 主要是底层机制不同

      • ElementNameWPF Binding 自带功能,依赖 元素名字表(NameScope
      • x:ReferenceXAML 级别机制,它可以引用 任何带 x:Key / x:Name 的对象

(5)整体代码

c# 复制代码
// User.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Non_element_binding
{
    public class User
    {
        public string Name { get; set; } = string.Empty;
    }
}
xaml 复制代码
<Window x:Class="Non_element_binding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Non_element_binding"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <!-- 2.静态资源字典对象 -->
        <FontFamily x:Key="my_font">
            微软雅黑
        </FontFamily>

        <!-- 3.普通对象(最常见) -->
        <local:User x:Key="my_user" Name="史蒂夫"/>

        <!-- 4.ObjectDataProvider -->
        <ObjectDataProvider x:Key="now"
                            ObjectType="{x:Type sys:DateTime}"
                            MethodName="get_Now"/>
    </Window.Resources>
    
    <Grid>
        <StackPanel>
            <!-- 1.静态绑定 - 将系统默认字体格式绑定到TextBlock-Text -->
            <TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=Source}" 
                   Margin="50" HorizontalAlignment="Center"/>

            <!-- 2.绑定到资源字典对象 -->
            <TextBlock Text="{Binding Source={StaticResource my_font}, Path=Source}"
                       Margin="10" HorizontalAlignment="Center"/>

            <!-- 3.普通对象(最常见) -->
            <TextBlock Text="{Binding Source={StaticResource my_user}, Path=Name}" 
                       Margin="10" HorizontalAlignment="Center"/>

            <!-- 4.ObjectDataProvider -->
            <TextBlock FontSize="30" Margin="10"
                       HorizontalAlignment="Center"
                       Text="{Binding Source={StaticResource now}}"/>

            <!-- 5.标记扩展 -->
            <Slider x:Name="slider" Value="50"
                    Minimum="0" Maximum="100"/>

            <TextBlock Text="{Binding Source={x:Reference slider}, Path=Value}"
                       FontSize="30" HorizontalAlignment="Center"/>
        </StackPanel>
    </Grid>
</Window>

2.🌱RelativeSource(相对资源 ------ 从视觉树查找资源)

  • 我们先来翻译一下这个单词

    • Relative = 相对的,Source = 数据源
    • 所以直接翻译就是相对资源
  • 换句话说:数据源不是外部对象,而是"和当前控件有关系的对象"

    • 这种关系通常来自 UI 树(控件层级),WPF 的绑定系统会在控件树里找目标

    shell 复制代码
    当前控件
       │
       └─ 向某个方向查找
            │
            └─ 找到对象
                 │
                 └─ 读取属性
                 
    # 他还是忘不了他的树状图
  • RelativeSource 有四种模式,即:枚举体RelativeSourceMode有4种数值

    shell 复制代码
    Self
    FindAncestor
    TemplatedParent
    PreviousData

(1)Self --- 绑定自己

shell 复制代码
# 这两个绑一起了,然后永远都是一个正方形了,永远.....永远.....(海绵宝宝口音)
TextBox.Width
      ↓
TextBox.Height
xaml 复制代码
<TextBlock Height="50" Width="{Binding RelativeSource={RelativeSource Self}, Path=Height}"/>

(2)FindAncestor --- 寻找先祖控件

  • 欲先利其器,必然先翻译

    • Find = 寻找,Ancestor = 祖先
    • 所以直接翻译就是 寻找祖先,本地化一点就是寻找先祖 我们又不是国服游戏无良翻译,本地化都不搞就上线圈钱了
  • 这个感觉是最常用的一种,它会沿着 UI 树往上找指定类型

    shell 复制代码
    # 语法示例
     	# Path:表示 要读取祖先控件的哪个属性
    	# AncestorType:表示 要找哪种类型的祖先控件
    	# AncestorLevel:表示 第几个祖先(默认 1)
    	# Mode:绑定模式(OneWay / TwoWay 等)
     {Binding Path=源属性, RelativeSource={RelativeSource Mode=FindAncestor, 
     		 							AncestorType=祖先控件类型, 
     		 							AncestorLevel=祖先层级},
     		 Mode=绑定模式} 
  • 例如下面这段代码:

    shell 复制代码
    # 假设它的UI树是这样的
    Window
     └─ Grid (Background=OrangeRed)
         ├─ TextBlock
         │   └─ Width ← Height  # 之前的Self
         │
         └─ TextBlock 	# 现在的FindAncestor
             └─ Background ← Window.Background
    xaml 复制代码
    <TextBlock Height="100" Width="100" Grid.Row="1"
               Background="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                           Path=Background,
                           AncestorType={x:Type Window}}}"/>
  • 然后,我们再来看另一个例子:

    shell 复制代码
    # UI树
    Grid
     └─ StackPanel
         └─ Grid
             └─ TextBlock
             
     # 先祖层级
    Grid (第2个)  ← 绑定目标
       │
    StackPanel
       │
    Grid (第1个)
       │
    TextBlock
    xaml 复制代码
    RelativeSource={RelativeSource Mode=FindAncestor,
                                   AncestorType={x:Type Grid},
                                   AncestorLevel=2}

(3)TemplatedParent --- 模板父控件(绑定到应用模板的元素)

  • 先上翻译:

    • Templated:模板
    • TemplatedParent:模板父母
  • 这是控件模板专用模式,当你写 ControlTemplate 时,模板里的元素需要访问外部控件属性

    • 这里我对WPF中的模板不是特别了解,所以前面的内容以后再来探索吧

    复制代码
      Button.Content
           ↓
      TextBlock.Text
    xaml 复制代码
    <Button Content="Hello">
        <Button.Template>
            <ControlTemplate TargetType="Button">
                <Border Background="LightBlue">
                    <TextBlock
                        Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
                </Border>
            </ControlTemplate>
        </Button.Template>
    </Button>

(4)PreviousData --- 绑定到数据绑定列表中(集合)的前一个数据项(很少使用)

  • 集合里的上一个数据项

    • 常用于 数据对比,列表差值,趋势计算
xaml 复制代码
{Binding RelativeSource={RelativeSource PreviousData}}
  • 这里偷个懒,就不做过多的解释了,再解释,我感觉我资料查不完了啊

3.DataContext(数据上下文)

  • 在讲解数据上下文之前,我们先给出一个示例,来解释这个东西到底是个啥

    xaml 复制代码
    <Grid>
        <StackPanel>
            <TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=Source}"/>
            <TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, Path=LineSpacing}"/>
            <TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, 												Path=FamilyTypefaces[0].Style}"/>
            <TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily}, 												Path=FamilyTypefaces[0].Weight}"/>
        </StackPanel>
    </Grid>
  • 但是我们这样写非常麻烦,每次都要写绑定这个Source={x:Static SystemFonts.IconFontFamily}

  • 于是,我们就可以使用数据上下文(在上一级控件上声明数据上下文)减少代码量

    xaml 复制代码
    <Grid>
        <StackPanel DataContext="{x:Static SystemFonts.IconFontFamily}">
            <TextBlock Text="{Binding Path=Source}"/>
            <TextBlock Text="{Binding Path=LineSpacing}"/>
            <TextBlock Text="{Binding Path=FamilyTypefaces[0].Style}"/>
            <TextBlock Text="{Binding Path=FamilyTypefaces[0].Weight}"/>
        </StackPanel>
    </Grid>
    
    <!-- 当然,你还可以更加简洁 -->
    <Grid>
        <StackPanel DataContext="{x:Static SystemFonts.IconFontFamily}">
            <TextBlock Text="{Binding Source}"/>
            <TextBlock Text="{Binding LineSpacing}"/>
            <TextBlock Text="{Binding FamilyTypefaces[0].Style}"/>
            <TextBlock Text="{Binding FamilyTypefaces[0].Weight}"/>
        </StackPanel>
    </Grid>
    
    <!-- 当然的当然,你还可以在父控件的父控件上使用数据上下文 -->
    <Grid DataContext="{x:Static SystemFonts.IconFontFamily}">
        <StackPanel>
            <TextBlock Text="{Binding Source}"/>
            <TextBlock Text="{Binding LineSpacing}"/>
            <TextBlock Text="{Binding FamilyTypefaces[0].Style}"/>
            <TextBlock Text="{Binding FamilyTypefaces[0].Weight}"/>
        </StackPanel>
    </Grid>
    
    <!-- 当然的当然的当然(你还有完没完),只要是上层控件都可以使用,但是层级越高,性能消耗越高 -->
    <Window x:Class="DataContext.MainWindow"
            DataContext="{x:Static SystemFonts.IconFontFamily}"
            ......
        <Grid>
            <StackPanel>
                <TextBlock Text="{Binding Source}"/>
                <TextBlock Text="{Binding LineSpacing}"/>
                <TextBlock Text="{Binding FamilyTypefaces[0].Style}"/>
                <TextBlock Text="{Binding FamilyTypefaces[0].Weight}"/>
            </StackPanel>
        </Grid>
    </Window>
  • 所以此时此刻,我们可以得出结论

    • 数据上下文(DataContext)作用:给一片 UI 区域指定默认数据源

      • 学术一点就是:DataContext 理解为某片UI区域中默认绑定对象
      • 数据上下文在MVVM架构中使用的非常频繁
    • 为什么数据上下文只要是在上层控件就可以使用呢,因为它可以继承

      • 即:DataContext 会从父控件自动传给子控件

      • 如图: 树状图也是图!

      shell 复制代码
      Window  # 假设数据上下文写在这里
       └─ Grid			 	# 这里可以拿到 
           └─ StackPanel	 	# 这里也可以拿到
               └─ TextBlock	#这里还是可以拿到
  • 当然 有完没完啊你

  • 我们不仅可以在xaml代码中声明数据上下文,也可以在C#代码中声明数据上下文

    • MainWindow.xaml.cs

    c# 复制代码
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace DataContext
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.DataContext = new User { Name = "无名氏", Age = 100 };
            }
        }
    
        public class User
        {
            public string Name { get; set; } = string.Empty;
            public int Age { get; set; }
        }
    }
    • MainWindow.xaml

    xaml 复制代码
    <Window x:Class="DataContext.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:DataContext"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <StackPanel>
                <StackPanel DataContext="{x:Static SystemFonts.IconFontFamily}">
                    <TextBlock Text="{Binding Source}"/>
                    <TextBlock Text="{Binding LineSpacing}"/>
                    <TextBlock Text="{Binding FamilyTypefaces[0].Style}"/>
                    <TextBlock Text="{Binding FamilyTypefaces[0].Weight}"/>
                </StackPanel>
    
                <TextBlock Text="{Binding Name}" HorizontalAlignment="Center"/>
                <TextBlock Text="{Binding Age}" HorizontalAlignment="Center"/>
            </StackPanel>
        </Grid>
    </Window>

4.总结

sell 复制代码
非元素对象绑定
│
├─ 1.Source(显式数据源)
│     │
│     ├─ 静态对象
│     │     x:Static
│     │     └─ SystemFonts.IconFontFamily
│     │
│     ├─ 资源对象
│     │     StaticResource
│     │     └─ ResourceDictionary
│     │
│     ├─ 普通对象
│     │     C# 类实例
│     │     └─ User
│     │
│     ├─ ObjectDataProvider
│     │     └─ 调用对象方法 / 属性
│     │
│     └─ x:Reference
│           └─ 引用 XAML 中已有对象实例
│
├─ 2.RelativeSource(相对数据源)
│     │
│     ├─ Self
│     │     └─ 绑定自己
│     │
│     ├─ FindAncestor
│     │     └─ 向 UI 树上查找祖先控件
│     │
│     ├─ TemplatedParent
│     │     └─ 访问模板宿主控件
│     │
│     └─ PreviousData
│           └─ 访问集合中上一条数据
│
└─ 3.DataContext(默认数据源)
      │
      ├─ XAML 中设置
      │     Window.DataContext
      │     Grid.DataContext
      │
      ├─ C# 中设置
      │     this.DataContext = ViewModel
      │
      └─ 继承机制
            Window
             └─ Grid
                 └─ StackPanel
                     └─ TextBlock
                     
Binding 数据来源
│
├─ ElementName
│     └─ 绑定到指定控件
│
├─ Source
│     └─ 显式指定对象
│
├─ RelativeSource
│     └─ 从 UI 树寻找对象
│
└─ DataContext
      └─ 默认数据源(最常用)

随笔参考:

1.61.第8章_绑定到非元素_Source_哔哩哔哩_bilibili

2.62.第8章_绑定到非元素_RelativeSrouce_哔哩哔哩_bilibili

3.63.第8章_绑定到非元素_DataContext_哔哩哔哩_bilibili


哦吼吼吼!终于写完了,要死了要死了,最近这两篇博客,感觉写的实在是太久了,资料是越查越多,越查越多.....

多的让我以为世界快完蛋了,虽然是用来学习的同时,打发一下摸鱼时间

好吧,其实是打法摸鱼的时候顺便学一下架构.......

相关推荐
宝桥南山2 小时前
Microsoft Fabric - 试一下在Blazor应用中使用 GraphQL API去连接Lakehouse
microsoft·c#·asp.net·.netcore·fabric·db
gc_22993 小时前
C#调用Microsoft.ML.OnnxRuntime和YOLO5模型时的输入数据格式分析
yolo·c#·数据预处理
猹叉叉(学习版)3 小时前
【ASP.NET CORE】 11. SignalR
笔记·后端·c#·asp.net·.netcore
bugcome_com3 小时前
C# 匿名方法与 Lambda 表达式全解析
c#
格林威4 小时前
工业相机图像高速存储(C#版):直接IO(Direct I/O)绕过系统缓存,附堡盟相机实战代码!
开发语言·人工智能·数码相机·计算机视觉·缓存·c#·视觉检测
格林威4 小时前
工业相机图像高速存储(C#版):直接IO(Direct I/O)方法,附Basler相机实战代码!
开发语言·人工智能·数码相机·计算机视觉·c#·视觉检测·工业相机
顾温4 小时前
c# 多线程
开发语言·c#
格林威4 小时前
工业相机图像高速存储(C#版):直接IO存储方法,附海康相机C#实战代码!
开发语言·人工智能·数码相机·c#·工业相机·海康相机·堡盟相机
csdn_aspnet5 小时前
使用 Ollama,通过 C#、语义内核和 Google Gemma 3 构建本地 AI 代理
人工智能·ai·c#·ollama·gemma