网站首页 全球最实用的IT互联网站!

人工智能P2P分享Wind搜索发布信息网站地图标签大全

当前位置:诺佳网 > 软件工程 > 后端开发 > .Net >

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

时间:2026-03-16 11:05

人气:

作者:admin

标签:

导读:WPF个人文档(五)—— 绑定 [!IMPORTANT] 在开始之前,我觉得我们非常有必要要先了解一下ViewModel ViewModel:专门给界面(View)使用的数据对象 # ViewModel = 专门给界面(View)使用的数据对象 如果...

[!IMPORTANT]

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

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

  • # ViewModel = 专门给界面(View)使用的数据对象
    如果只讲绑定,可以简单理解为数据源对象
    在这里先留一个简单的印象,后面会详细讲解,在看完本篇随笔之后,你也会对这个东西有一个较为深刻的印象
    
    # 常用于MVVM架构(此架构我们以后再详细讲解)
    Model  →  ViewModel  →  View
    数据        UI数据        界面
    

[!NOTE]

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

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

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

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

一.元素绑定

  • [!NOTE]

    WPF —— 绑定

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

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

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

    • 此处 Background 是绑定目标属性,ColorName 是绑定源属性,通过 Path 指定
    <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
    <ListBox ItemsSource="{Binding MyItems}" />
    
    • 若需排序、筛选、分组,可用 CollectionViewSource
    <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的数值

    • # 两个元素绑定
      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   (本地值)
      
    • 最后滑块与图片透明度之间的绑定就被移除了

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

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

      Animation		# 优先级最高
      LocalValue
      Binding
      Style
      Default			# 优先级最低
      
    • 当我们写:

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


  • MainWindow.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

    • 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种手动挡

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

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

1.????OneWay — 单向绑定

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

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

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

2.????TwoWay — 双向绑定

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

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

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

3.????OneWayToSource — 单向反向绑定

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

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


4.????OneTime — 仅进行一次的绑定

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

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

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

5.????Default — 自动档

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

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

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

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

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

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

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

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

6.示例代码

  • MainWindow.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

    • 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 直接移除

      • // 这里的代码干了一件很关键的事:删除了 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 的依赖属性系统会执行一个优先级规则

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

      // 设置的是 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 控制 更新节奏
  • 高级绑定特性
    ├─ 多源绑定
    │   └─ MultiBinding
    │
    ├─ 绑定更新控制
    │   └─ UpdateSourceTrigger
    │
    └─ 更新节流控制
        └─ Delay
        
    ======================================================================
    # 这只小狐狸????永远忘不了他的树状图了
    ======================================================================
    # 逻辑框架理解
    # WPF 的 Binding 系统其实像一条 数据管道系统
    # 不同机制负责不同控制点:
    
            数据源
              ↓
            Binding
              ↓
    [ ????多绑定 MultiBinding ]  	  ← # 控制数据来源数量
              ↓
            Converter
              ↓
              UI
              ↓
    [ ????UpdateSourceTrigger ] 	 ← # 控制更新时机
      		 ↓
    	 [ ????Delay ]               ← # 控制更新节奏
    		 ↓
    	  ViewModel
    

1.????多绑定(MultiBinding)

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

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

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

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

    • TextBox.Text     	← 	slider.Value
      TextBox.FontSize 	← 	sliderSize.Value
      
      <!-- 绑定语法:{Binding ElementName=源控件名, Path=源属性, Mode=绑定模式} -->
      
    • <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>
      
  • 下面这段代码才是真正意义上的多绑定

    • # 这里的多绑定使用流程
      1. 准备数据源
      2. 创建转换器
      3. 注册转换器
      4. 编写 MultiBinding
      5. 在 Converter 中处理数据
      
      
      # 这里的整体结构
      Binding1
      Binding2
         ↓
      MultiBinding	# 多绑定
         ↓
      Converter
         ↓
      TextBlock.Text
      
      # 翻译一下
      sliderValue.Value
      sliderSize.Value
              ↓
      SliderInfoConverter
              ↓
      TextBlock.Text
      
    • MainWindow.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进行数据转换

    • 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();
              }
          }
      }
      
    • # 数据流
      # 当滑块变化时
      sliderValue.Value 改变
      sliderSize.Value 改变
              ↓
      MultiBinding 监听到变化
              ↓
        调用 Convert()
              ↓
      	返回字符串
              ↓
      TextBlock.Text 更新
      
      # 同时
      sliderSize.Value
            ↓
      TextBlock.FontSize 更新
      
  • 多绑定适用场景

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

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

      • 例如:

      • # 组合计算 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 —— 实时更新

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

    • # 行为
      
      输入一个字
      	↓
      立刻写回 ViewModel
      

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

  • <TextBox Text="{Binding UserName, UpdateSourceTrigger=LostFocus}" />
    
    • # 行为
      
      用户输入
        ↓
      离开 TextBox
        ↓
      更新数据
      
      # 优点:减少更新次数
      

(3)Explicit —— 手动更新

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

  • 代码示例:

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

    • 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

    • <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

    • 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 = 让控件自己决定

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

  • 工作方式

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

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

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

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


3.????延迟绑定 (Delay)

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

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

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

四.非元素对象绑定

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

  • Binging.elementName属性

    • elementName,翻译一下就是:元素名称
    • 它的设计目标就是——绑定到“界面元素”
    • 如果数据源不是 UI 元素,这个属性基本就该退场了
  • # 再来回顾一下,元素绑定绑定语法:
    {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 更多是 特殊情况的精确绑定工具,而不是主力绑定方式

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

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

  • <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

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

    • 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

    • <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 技术)

  • <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 不是方法,而是属性

      • <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"

      • <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}

      • <TextBlock Text="{Binding Source={x:Static sys:DateTime.Now}}"/>
        

(4)x:Reference标记扩展

  • [!IMPORTANT]

    • x:Reference
      • x:Reference 这个东西其实是 XAML 世界里的“指针”
      • 作用:拿到某个已经存在的对象实例,然后当作 绑定的资源
        • 和常见的 ElementName 很像,但机制不一样
      • x:Reference 属于 XAML 标记扩展,来自XAML体系
  • x:Reference → 找到某个对象实例
    Binding     → 从这个实例读取属性
    
    # 数据流
    对象实例
       ↓
    Binding
       ↓
    目标属性
    
  • 这里我们使用一个滑块,让 TextBlock 实时显示它的值

  • <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的区别

    • <!-- 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)整体代码

  • // 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;
        }
    }
    
  • <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 的绑定系统会在控件树里找目标

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

    Self
    FindAncestor
    TemplatedParent
    PreviousData
    

(1)Self — 绑定自己

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

(2)FindAncestor — 寻找先祖控件

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

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

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

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

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

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

  • 先上翻译:

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

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

    • Button.Content
           ↓
      TextBlock.Text
      
    • <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 — 绑定到数据绑定列表中(集合)的前一个数据项(很少使用)

  • 集合里的上一个数据项

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


3.DataContext(数据上下文)

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

    • <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}

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

    • <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 会从父控件自动传给子控件

      • 如图: 树状图也是图!

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

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

    • MainWindow.xaml.cs

    • 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

    • <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.总结

非元素对象绑定
│
├─ 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


哦吼吼吼!终于写完了,要死了要死了,最近这两篇博客,感觉写的实在是太久了,资料是越查越多,越查越多.....
多的让我以为世界快完蛋了,虽然是用来学习的同时,打发一下摸鱼时间
好吧,其实是打法摸鱼的时候顺便学一下架构.......

温馨提示:以上内容整理于网络,仅供参考,如果对您有帮助,留下您的阅读感言吧!
相关阅读
本类排行
相关标签
本类推荐

CPU | 内存 | 硬盘 | 显卡 | 显示器 | 主板 | 电源 | 键鼠 | 网站地图

Copyright © 2025-2035 诺佳网 版权所有 备案号:赣ICP备2025066733号
本站资料均来源互联网收集整理,作品版权归作者所有,如果侵犯了您的版权,请跟我们联系。

关注微信