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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

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

發(fā)布時(shí)間:2024/1/5 asp.net 40 coder
生活随笔 收集整理的這篇文章主要介紹了 Freezable ---探索WPF中Freezable承载数据的原理 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

引言

在之前寫的一篇文章【W(wǎng)PF --- 如何以Binding方式隱藏DataGrid列】中,我先探索了 DataGridTextColumn 為什么不在可視化樹(shù)結(jié)構(gòu)內(nèi)?又給出了解決方案,使用 Freezable ,該抽象類是 DependencyObject 的子類,能使用依賴屬性在 Xaml 進(jìn)行綁定,它承載了 DataContext 且有屬性變化通知功能,觸發(fā) VisibilityConverter轉(zhuǎn)換器,實(shí)現(xiàn)了預(yù)期功能。

然后有群友問(wèn)了這樣一個(gè)問(wèn)題:

這里有兩個(gè)問(wèn)題:

  1. 非可視化樹(shù)中的元素不能通過(guò) RelativeSource 或者 ElementName 訪問(wèn)到可視化樹(shù)中的數(shù)據(jù),為何可以通過(guò) resource 的方式訪問(wèn)?
  2. Freezable 類為何能夠中轉(zhuǎn)數(shù)據(jù),DependencyObject 不行?

那么本篇文章就來(lái)探索一下 Freezable實(shí)現(xiàn)了上述功能的原理是什么?

原理探索

準(zhǔn)備

我們還是使用上一篇文章中的示例,讓后為了便于剖析源碼,做了部分改動(dòng)。

首先,準(zhǔn)備自定義 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();
    }
}

然后準(zhǔn)備界面,但是這回跟之前不一樣的是所有 DataGridTextColumn 列不在 XAML 中綁定,我們放在后臺(tái)綁定:

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

然后準(zhǔn)備 Code-Behind 代碼,增加 InitDataGrid() ,手動(dòng)綁定所有列。

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(); }
    }
}

源碼剖析

在源碼剖析之前,如果大家還不會(huì)如何使用VS調(diào)試.Net源碼,建議先閱讀我的另一篇文章【編程技巧 --- VS如何調(diào)試.Net源碼】,學(xué)習(xí)如何調(diào)試源碼。

接下來(lái),在程序啟動(dòng)之前,我們?cè)?CustomFreezable 的重載方法 OnChanged() 設(shè)置斷點(diǎn),然后使用VS調(diào)試源碼,查看調(diào)用堆棧:

可以看到,從 InitDataGrid() 開(kāi)始,到屬性變化觸發(fā)變化事件,整個(gè)流程都可以在調(diào)用堆棧中看到,我們可以逐幀分析,來(lái)解決開(kāi)篇的兩個(gè)問(wèn)題。

剖析步驟

我們將上述調(diào)用鏈編號(hào),逐步分析:

  1. 編號(hào)1:FindResource(...)

  1. 編號(hào)2:FrameworkElement.FindResourceInternal(...)

  1. 編號(hào)3:FindResourceInTree(...)

  1. 編號(hào)4:FetchResource(...)

  1. 編號(hào)5~6:GetValue(...),在這里已經(jīng)獲取到字典中資源了。

  1. 編號(hào)7~8 OnGettingValue(...)

  1. 編號(hào)9~10 AddInheritanceContext(...)

  1. 編號(hào)11~12 ProvideSelfAsInheritanceContext(...)

  2. 編號(hào)13 AddInheritanceContext(...)

后面的就不用看了,后面的就是因?yàn)?Freezable 更換了 InheritanceContext 觸發(fā)了OnInheritanceContextChanged()后又觸發(fā)了 NotifyPropertyChange

接下來(lái)看看為什么當(dāng) IsVisibility 變化時(shí),能通知到 Freezable?

  1. NotifySubPropertyChange(...)

  1. FireChanged(...)

  1. GetChangeHandlersAndInvalidateSubProperties(...)

可以看到從1~9僅僅是 FindResource("customFreezable"); 這一個(gè)方法所作的事情,主要是從資源字典中查詢想要的對(duì)象,如果該對(duì)象是 Freezable類型的,則將當(dāng)前資源的 DataContentVisual 綁定為 FreezableInheritanceContext ,然后10~12,是該上下文在當(dāng)前資源的 DataCobtent 觸發(fā) PropertyChanged時(shí),去InheritanceContext 中找出關(guān)聯(lián)的 CallHandle 強(qiáng)制刷新,觸發(fā)變化事件,達(dá)到聯(lián)動(dòng)效果。

那么從解析源碼的過(guò)程中看,開(kāi)篇的兩個(gè)問(wèn)題就都有了答案

  1. 非可視化樹(shù)中的元素不能通過(guò) RelativeSource 或者 ElementName 訪問(wèn)到可視化樹(shù)中的數(shù)據(jù),為何可以通過(guò) resource 的方式訪問(wèn)?

    原因就是 FindResource 方法中,如果要查詢的資源是Freezable類型的,則會(huì)將當(dāng)前資源的 DataContentVisual 綁定到 InheritanceContext,所以Freezable 也就可以訪問(wèn)到可視化樹(shù)中的數(shù)據(jù)了。

  2. Freezable 類為何能夠中轉(zhuǎn)數(shù)據(jù),DependencyObject 不行?

    從代碼中,編號(hào)11~12 ProvideSelfAsInheritanceContext(...)也可以看出,綁定 InheritanceContext 時(shí)有一個(gè)必要條件就是該資源必須為 Freezable 類型的才可以,我猜測(cè)這可能跟這個(gè)類的定義有關(guān)系,Freezable 類為 WPF 中的對(duì)象提供了不可變性和性能優(yōu)化的功能,同時(shí)也為動(dòng)畫、資源共享和跨線程安全性等方面提供了便利。 該類是更好地管理和優(yōu)化 WPF 應(yīng)用程序中的對(duì)象和資源的,所以可能不想讓開(kāi)發(fā)者隨意使用吧,所以就僅提供該類能夠擁有 InheritanceContext 而沒(méi)法使用 DependencyObject

小結(jié)

Freezable 類除了上文示例中的用法,其實(shí)它這種間接綁定的方式可以解決很多場(chǎng)景,比如某個(gè)元素的屬性并不是依賴屬性,但是你就是想使用 Binding 的方式,讓它動(dòng)態(tài)變化,也可以使用上文示例的方式進(jìn)行綁定。

好了,源碼解析的過(guò)程其實(shí)還是比較復(fù)雜的,本文中其實(shí)也省略了一些源碼閱讀過(guò)程中細(xì)節(jié),若大家閱讀有疑問(wèn)的地方,歡迎找我解疑,建議不明白的點(diǎn),優(yōu)先自行進(jìn)行一下源碼調(diào)試。

總結(jié)

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

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。