日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

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

發布時間:2024/1/5 asp.net 36 coder
生活随笔 收集整理的這篇文章主要介紹了 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 的方式,讓它動態變化,也可以使用上文示例的方式進行綁定。

好了,源碼解析的過程其實還是比較復雜的,本文中其實也省略了一些源碼閱讀過程中細節,若大家閱讀有疑問的地方,歡迎找我解疑,建議不明白的點,優先自行進行一下源碼調試。

總結

以上是生活随笔為你收集整理的Freezable ---探索WPF中Freezable承载数据的原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。