Dependency Property 和 DataContext 赋值的顺序

问题描述 投票:0回答:1

根据我的测试,如果我仅通过 XAML 设置

DataContext
Dependency Property
一次(请参阅下面的示例),则似乎总是先分配
DataContext
,然后再分配
Dependency Property

但这总是正确的吗?是否有一个 C# 语言规范说——不,保证——这个?我需要这个,因为我的程序恰恰依赖于这种行为。

这是我可以用来说明这一点的代码:

查看型号

public class MainWindowVM
{
    public SimpleTextBoxVM SimpleText => new SimpleTextBoxVM();
}
public class SimpleTextBoxVM
{
}

public class Updater
{
}

用户界面

public class Updater
{
}

public partial class SimpleTextBoxExt : UserControl
{
    public SimpleTextBoxExt()
    {
        InitializeComponent();
        DataContextChanged += OnDataContextChanged;
    }

    private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue != null)
        {
            Console.WriteLine("DataContext Assigned."); //this seems always executed first

        }
    }

    public static readonly DependencyProperty FractionalNumberProperty = DependencyProperty.Register(
        nameof(FractionalNumber), typeof(Updater), typeof(SimpleTextBoxExt), new PropertyMetadata(default(Updater), PropertyChangedCallback));

    private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is SimpleTextBoxExt simpleTextBox)
        {
            Console.WriteLine("Dependency property set"); //this seems always executed later
        }
    }



    public Updater FractionalNumber
    {
        get { return (Updater)GetValue(FractionalNumberProperty); }
        set { SetValue(FractionalNumberProperty, value); }
    }
}

<Window x:Class="DependencyPropertiesUI.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:DependencyPropertiesUI"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
   <Window.DataContext>
       <local:MainWindowVM/>
   </Window.DataContext>
    <local:SimpleTextBoxExt DataContext="{Binding SimpleText}">
        <local:SimpleTextBoxExt.FractionalNumber>
            <local:Updater/>
        </local:SimpleTextBoxExt.FractionalNumber>
    </local:SimpleTextBoxExt>
</Window>
wpf
1个回答
0
投票

一般来说,自定义控件不得依赖于其

DataContext
或其他元素的
DataContext
。这是一种反模式。
DataContext
用于外部绑定或数据模板。
DataContext
应该是预期的,并允许根据控件所在的位置进行外部更改。

如果自定义控件依赖于外部数据,则它必须定义可以将这些数据分配给的依赖属性,例如通过数据绑定。就像任何其他框架控件一样。
自定义控件由客户端通过属性(而不是通过内部视图模型)进行初始化和配置。

例如,

Button
ICommand
属性获取它执行的
Button.Command
,而不是从
DataContext
。因为它内部没有定义
DataContext
,所以您可以轻松地将
Button:Command
绑定到您自己的数据上下文。
Button
内部并不知道它的
DataContext

这导致了下一个反模式:每个控件一个视图模型。现在应该有道理了,控件不需要视图模型,因为它不依赖于任何

DataContext

换句话说,如果您避免反模式,您就可以避免很多问题,例如事件之间的竞争条件或类型成员的初始化。依赖这些细节总是会导致代码脆弱。

从设计模式的角度来看,MVVM 是一种描述应用程序结构的架构设计模式。该模式本身不关心数据绑定和单独的控件。数据绑定是有助于实现所需依赖图的技术。

以下示例强调了正确的控制设计。

如您所见,它完全消除了原始版本的竞争条件问题。自定义控件与

DataContext
无关,此外,它还具有高度可重用性。例如,本机 WPF 控件是可重用的,因为它们不依赖于它们的
DataContext
:

SimpleTextBoxExt.xaml.cs

public partial class SimpleTextBoxExt : UserControl
{
  // A property to retrieve external data.
  // This data can be internally processed and/or displayed by child elements.
  // No data context necessary.
  public string SomeText
  {
    get => (string)GetValue(SomeTextProperty);
    set => SetValue(SomeTextProperty, value);
  }

  public static readonly DependencyProperty SomeTextProperty = DependencyProperty.Register(
    "SomeText",
    typeof(string),
    typeof(SimpleTextBoxExt),
    new PropertyMetadata(default));

  public SimpleTextBoxExt()
  {
    InitializeComponent();
  }
}

SimpleTextBoxExt.xaml

<UserControl x:Name="Root">

  <!-- 
       Get external data from the parent's dependency properties that are set 
       by the client of this control, e.g. via data binding. 
  -->
  <TextBlock Text="{Binding ElementName=Root, Path=SomeText}" />
</UserControl>

MainWindow.xaml

<Window>
  <Window.DataContext>
    <MainViewModel />
  </Window.DataContext>

  <!-- 
       Feed external data to the custom control or configure the 
       custom control by using dedicated properties. 
  -->
  <SimpleTextBoxExt SomeText="{Binding ViewModelTextValue}" />
</Window>
© www.soinside.com 2019 - 2024. All rights reserved.