Freezable ---探索WPF中Freezable承载数据的原理

这篇具有很好参考价值的文章主要介绍了Freezable ---探索WPF中Freezable承载数据的原理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引言

在之前写的一篇文章【WPF --- 如何以Binding方式隐藏DataGrid列】中,我先探索了 DataGridTextColumn 为什么不在可视化树结构内?又给出了解决方案,使用 Freezable ,该抽象类是 DependencyObject 的子类,能使用依赖属性在 Xaml 进行绑定,它承载了 DataContext 且有属性变化通知功能,触发 VisibilityConverter转换器,实现了预期功能。

然后有群友问了这样一个问题:

这里有两个问题:

  1. 非可视化树中的元素不能通过 RelativeSource 或者 ElementName 访问到可视化树中的数据,为何可以通过 resource 的方式访问?
  2. Freezable 类为何能够中转数据,DependencyObject 不行?

那么本篇文章就来探索一下 Freezable实现了上述功能的原理是什么?

原理探索

准备

我们还是使用上一篇文章中的示例,让后为了便于剖析源码,做了部分改动。

首先,准备自定义 Freezable 类:

public class CustomFreezable : Freezable
{
    public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(CustomFreezable));

    public object Value
    {
        get => (object)GetValue(ValueProperty);
        set => SetValue(ValueProperty, value);
    }

    protected override void OnChanged()
    {
        base.OnChanged();
    }
    
    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
        base.OnPropertyChanged(e);
    }

    protected override Freezable CreateInstanceCore()
    {
        return new CustomFreezable();
    }
}

然后准备界面,但是这回跟之前不一样的是所有 DataGridTextColumn 列不在 XAML 中绑定,我们放在后台绑定:

<Window.Resources>
    <local:VisibilityConverter x:Key="VisibilityConverter" />
    <local:CustomFreezable x:Key="customFreezable" Value="{Binding IsVisibility, Converter={StaticResource VisibilityConverter}}" />
</Window.Resources>
<Grid>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>
        <DataGrid
            x:Name="dataGrid"
            AutoGenerateColumns="False"
            CanUserAddRows="False"
            ItemsSource="{Binding Persons}"
            SelectionMode="Single">
        </DataGrid>
        <CheckBox
            Grid.Column="1"
            Content="是否显示年龄列"
            IsChecked="{Binding IsVisibility, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    </Grid>
</Grid>

然后准备 Code-Behind 代码,增加 InitDataGrid() ,手动绑定所有列。

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;

    public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public MainWindow()
    {
        InitializeComponent();
        Persons = new ObservableCollection<Person>() { new Person() { Age = 11, Name = "Peter" }, new Person() { Age = 19, Name = "Jack" } };
        DataContext = this;

        InitDataGrid();
    }

    private void InitDataGrid()
    {
        DataGridTextColumn columen1 = new DataGridTextColumn();
        columen1.Header = "年龄";
        columen1.Binding = new Binding("Age");
        columen1.Width = new DataGridLength(1, DataGridLengthUnitType.Star);

        Binding binding = new Binding("Value");
        binding.Source = FindResource("customFreezable");

        BindingOperations.SetBinding(columen1, DataGridTextColumn.VisibilityProperty, binding);

        dataGrid.Columns.Add(columen1);

        DataGridTextColumn columen2 = new DataGridTextColumn();
        columen2.Header = "姓名";
        columen2.Binding = new Binding("Name");
        columen2.Width = new DataGridLength(1, DataGridLengthUnitType.Star);

        dataGrid.Columns.Add(columen2);

    }

    private bool isVisibility = true;
    public bool IsVisibility
    {
        get => isVisibility;
        set
        {
            isVisibility = value;
            OnPropertyChanged(nameof(IsVisibility));
        }
    }

    private ObservableCollection<Person> persons;

    public ObservableCollection<Person> Persons
    {
        get { return persons; }
        set { persons = value; OnPropertyChanged(); }
    }
}

源码剖析

在源码剖析之前,如果大家还不会如何使用VS调试.Net源码,建议先阅读我的另一篇文章【编程技巧 --- VS如何调试.Net源码】,学习如何调试源码。

接下来,在程序启动之前,我们在 CustomFreezable 的重载方法 OnChanged() 设置断点,然后使用VS调试源码,查看调用堆栈:

可以看到,从 InitDataGrid() 开始,到属性变化触发变化事件,整个流程都可以在调用堆栈中看到,我们可以逐帧分析,来解决开篇的两个问题。

剖析步骤

我们将上述调用链编号,逐步分析:

  1. 编号1:FindResource(...)
  1. 编号2:FrameworkElement.FindResourceInternal(...)
  1. 编号3:FindResourceInTree(...)
  1. 编号4:FetchResource(...)
  1. 编号5~6:GetValue(...),在这里已经获取到字典中资源了。
  1. 编号7~8 OnGettingValue(...)
  1. 编号9~10 AddInheritanceContext(...)
  1. 编号11~12 ProvideSelfAsInheritanceContext(...)

  2. 编号13 AddInheritanceContext(...)

后面的就不用看了,后面的就是因为 Freezable 更换了 InheritanceContext 触发了OnInheritanceContextChanged()后又触发了 NotifyPropertyChange

接下来看看为什么当 IsVisibility 变化时,能通知到 Freezable

  1. NotifySubPropertyChange(...)
  1. FireChanged(...)
  1. GetChangeHandlersAndInvalidateSubProperties(...)

可以看到从1~9仅仅是 FindResource("customFreezable"); 这一个方法所作的事情,主要是从资源字典中查询想要的对象,如果该对象是 Freezable类型的,则将当前资源的 DataContentVisual 绑定为 FreezableInheritanceContext ,然后10~12,是该上下文在当前资源的 DataCobtent 触发 PropertyChanged时,去InheritanceContext 中找出关联的 CallHandle 强制刷新,触发变化事件,达到联动效果。

那么从解析源码的过程中看,开篇的两个问题就都有了答案

  1. 非可视化树中的元素不能通过 RelativeSource 或者 ElementName 访问到可视化树中的数据,为何可以通过 resource 的方式访问?

    原因就是 FindResource 方法中,如果要查询的资源是Freezable类型的,则会将当前资源的 DataContentVisual 绑定到 InheritanceContext,所以Freezable 也就可以访问到可视化树中的数据了。

  2. Freezable 类为何能够中转数据,DependencyObject 不行?

    从代码中,编号11~12 ProvideSelfAsInheritanceContext(...)也可以看出,绑定 InheritanceContext 时有一个必要条件就是该资源必须为 Freezable 类型的才可以,我猜测这可能跟这个类的定义有关系,Freezable 类为 WPF 中的对象提供了不可变性和性能优化的功能,同时也为动画、资源共享和跨线程安全性等方面提供了便利。 该类是更好地管理和优化 WPF 应用程序中的对象和资源的,所以可能不想让开发者随意使用吧,所以就仅提供该类能够拥有 InheritanceContext 而没法使用 DependencyObject

小结

Freezable 类除了上文示例中的用法,其实它这种间接绑定的方式可以解决很多场景,比如某个元素的属性并不是依赖属性,但是你就是想使用 Binding 的方式,让它动态变化,也可以使用上文示例的方式进行绑定。

好了,源码解析的过程其实还是比较复杂的,本文中其实也省略了一些源码阅读过程中细节,若大家阅读有疑问的地方,欢迎找我解疑,建议不明白的点,优先自行进行一下源码调试。

有错误之处,还请大家指正。文章来源地址https://www.toymoban.com/news/detail-777102.html

到了这里,关于Freezable ---探索WPF中Freezable承载数据的原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python数据分析基础《用数据讲故事》#0 引言

    在学校里,我们学习了很多关于语言和数学的知识。在语言方面,我们学习如何将单词组合成句子和故事。数学方面,我们学习如何理解数字。但很少有人将这两方面结合起来:没有人教我们如何用数字讲故事。更严峻的是,很少有人觉得自己天生就擅长这个领域。 这让我们

    2024年02月12日
    浏览(41)
  • 【机器学习】数据驱动的未来:机器学习的原理与算法探索

    个人名片: 🐼 作者简介:一名大三在校生,喜欢AI编程🎋 🐻‍❄️ 个人主页🥇: 落798. 🐼 个人WeChat:hmmwx53 🕊️ 系列专栏:🖼️ 零基础学Java——小白入门必备🔥 重识C语言——复习回顾🔥 计算机网络体系———深度详讲 HCIP数通工程师-刷题与实战🔥🔥🔥 微信小程

    2024年04月14日
    浏览(45)
  • 探索微软的XamlBehaviorsWpf:强大的WPF UI交互库

    项目地址:https://gitcode.com/microsoft/XamlBehaviorsWpf 在.NET框架中,Windows Presentation Foundation(WPF)是一个用于构建桌面应用程序的强大平台,而微软的XamlBehaviorsWpf项目则为WPF开发者提供了一组灵活、易于使用的UI行为库,极大地丰富了用户体验的设计和实现。 XamlBehaviorsWpf是微软开源

    2024年04月13日
    浏览(28)
  • 【Python数据结构与算法】--- 递归算法的应用 ---[乌龟走迷宫] |人工智能|探索扫地机器人工作原理

    🌈个人主页: Aileen_0v0 🔥系列专栏:PYTHON数据结构与算法学习系列专栏 💫\\\"没有罗马,那就自己创造罗马~\\\"  目录 导言  解决过程  1.建立数据结构 2.探索迷宫: 算法思路 递归调用的“基本结束条件” 3.乌龟走迷宫的实现代码: 运行过程: 拓展: 📝全文总结:  乌龟探索迷宫这个问

    2024年02月05日
    浏览(49)
  • 探索 WPF 的 ITabletManager.GetTabletCount 在 Win11 系统的底层实现

    本文将和大家介绍专为 WPF 触摸模块提供的 ITabletManager 的 GetTabletCount 方法在 Windows 11 系统的底层实现 本文属于 WPF 触摸相关系列博客,偏系统底层介绍,更多触摸博客请看 WPF 触摸相关 大家都知道在 Windows 7 系统,有专门的笔和触摸服务提供触摸消息的支持。而 WPF 是从 Vis

    2024年02月08日
    浏览(29)
  • 制作自己的ORBSLAM2数据集,并实现三维重建(代码自己写的)

    2 ORBSLAM2 测试自己拍摄的数据集         使用手机、摄像机等设备拍摄视频,对应我们只能使用单目 (Monocular)。 2.1对相机标定         首先我们要对相机进行标定,使用 MATLAB 里面的标定工具包。标定好之 后创建一个单目模式下的 yaml,复制 TUM1.yaml 修改标定参数即可

    2023年04月20日
    浏览(42)
  • MySQL 处理大数据表的 3 种方案,写的太好了,建议收藏!!

    作者:马佩 链接:https://juejin.cn/post/7146016771936354312 当我们业务数据库表中的数据越来越多,如果你也和我遇到了以下类似场景,那让我们一起来解决这个问题 数据的插入,查询时长较长 后续业务需求的扩展 在表中新增字段 影响较大 表中的数据并不是所有的都为有效数据 需

    2023年04月17日
    浏览(38)
  • ArcGIS笔记13_利用ArcGIS制作岸线与水深地形数据?建立水动力模型之前的数据收集与处理?

    在 利用MIKE建立水动力模型 ( 详见【MIKE水动力笔记】系列 )之前,需要收集、处理和制作诸多数据和资料,主要有 岸线数据、水深地形数据、开边界潮位驱动数据、风场数据、潮位和海流观测资料和站点潮汐调和常数资料 等。本篇主要介绍这些资料的获取与处理。 其中岸

    2024年02月08日
    浏览(40)
  • 云承载网方式构建新型网络云专线

    随着云计算的兴起,第一个阶段为互联网IT上云,现阶段最重要的是企业上云,企业将本身的it系统搬迁到云计算数据中心内部,以VPC的形式租用公有云,或者行业云等,此时由于企业IT系统为企业内部服务,涉及到订单系统,办公系统等核心系统,对于网络,以及云计算的可

    2024年02月04日
    浏览(35)
  • 探索FTP:原理、实践与安全优化

    在正式开始讲解之前,首先来了解一下文件存储的类型有哪些。 DAS、SAN和NAS是三种不同的存储架构,分别用于解决不同场景下的数据存储需求。 DAS (Direct Attached Storage 直接附加存储) :DAS 是指将存储设备(如硬盘)直接连接到服务器的内部总线或通过外部接口(如USB、SATA、

    2024年01月19日
    浏览(16)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包