【问题标题】:How can I supply an object to a UserControl's constructor? [duplicate]如何向 UserControl 的构造函数提供对象? [复制]
【发布时间】:2020-03-29 08:13:11
【问题描述】:

我有一个 UserControl 将属性绑定到控件,这似乎有效:

<UserControl x:Class="MyUserControl" Name="control">
<TextBox Text="{Binding SomeData, ElementName=control}" />

但是,获取此特定属性的信息需要设置另一个属性,该属性设置在主窗口中的InitializeComponent 之后。我的UserControl 的构造函数在InitializeComponent 中被调用,导致NullReferenceException

public MainWindow() {
   InitializeComponent();           // <-- here my UserControl is instatiated and needs Settings

   Settings = new Settings();    
   MyUserControl.Settings = Settings;    // <-- only here do we set the Settings object (which is needed in UserControl's constructor!
}


public MyUserControl {
    public string SomeData { 
         get {
               return Settings.Get("someSetting");
         }
    }
}

我该如何解决这个问题?还是我的架构错了(刚开始使用 WPF 和数据绑定)。

【问题讨论】:

  • XAML 中的 &lt;UserControl .../&gt; 调用无参数构造函数。重新设计您的控件,使其能够处理稍后设置的 Settings 属性。
  • 您应该将MyUserControl 绑定到MainWindow.Settings 属性,该属性应该是DependencyProperty。然后将初始化从MyUserControl 的构造函数移动到MyUserControl.Loaded 事件处理程序。执行此处理程序时,绑定的Settings 值将可用。
  • 您没有提供足够的上下文来了解您已经走了多远的错误路径,但请参阅标记的重复项以获取有关处理该场景的各种方法的信息。简短版本:您的用户控件需要公开一个属性,设置数据可以绑定到该属性,优雅地处理该属性的值为 null/un-set 以及具有有效值,并在 XAML 中绑定到该属性。跨度>

标签: c# wpf


【解决方案1】:

您应该将MyUserControl 绑定到MainWindow.Settings 属性,该属性应该是DependencyProperty。然后将初始化从MyUserControl 的构造函数移动到MyUserControl.Loaded 事件处理程序。执行此处理程序时,绑定的Settings 值将可用。

另外定义一个PropertyChangedCallback

MainWindow.xaml.cs

partial class MainWindow : Window
{
  public static readonly DependencyProperty SettingsProperty = DependencyProperty.Register(
    "Settings",
    typeof(Settings),
    typeof(MainWindow),
    new PropertyMetadata(default(Settings)));

  public Settings Settings
  {
    get => (Settings) GetValue(MainWindow.SettingsProperty);
    set => SetValue(MainWindow.SettingsProperty, value);
  }

  public MainWindow() 
  {
    InitializeComponent();
    this.Settings = new Settings();   
  }
}

MyUserControl.xaml.cs

partial class MyUserControl : UserControl
{
  public static readonly DependencyProperty SettingsProperty = DependencyProperty.Register(
    "Settings",
    typeof(Settings),
    typeof(MyUserControl),
    new PropertyMetadata(default(Settings), OnCurrentReadingChanged));

  public Settings Settings
  {
    get => (Settings) GetValue(MyUserControl.SettingsProperty);
    set => SetValue(MyUserControl.SettingsProperty, value);
  }

  public MyUserControl() 
  {
    InitializeComponent();
    this.Loaded += Initialize;   
  }

  private static void OnCurrentReadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
    Settings settings = (d as MyUserControl).Settings;
    // TODO: Initialize using this.Settings
  }

  public Initialize(object sender, EventArgs e) 
  {
    // TODO: Initialize using this.Settings
  }
}

MyUserControl.xaml

<UserControl x:Class="MyUserControl" 
             Name="control" 
             Settings="{Binding RelativeSource={RelativeSource AncestorType=MainWindow}, Path=Settings}">
  <TextBox Text="{Binding SomeData, ElementName=control}" />
</UserControl>

【讨论】:

  • 这是不正确的。作为一般规则,不需要为此类数据处理Loaded 事件。数据应该在 XAML 中绑定到用户控件公开的属性,并且用户控件应该能够处理默认值以及稍后设置的新值。然后,框架将根据绑定在最方便的时候分配值,这一切都“正常工作”。无需在Loaded 事件中进行特殊处理。
  • 什么是“不正确”?那么您从 XAML 调用参数化构造函数的建议也是不正确的。如果你批评他的设计选择,你应该推荐使用视图模型。你只是像我一样向他展示了一个解决方法。同样如您所见,数据 “在 XAML 中绑定到用户控件公开的属性”。我不知道他为什么要在构造函数中处理这些数据。我只是建议将依赖于 Settings 实例的初始化代码从构造函数移动到 Loaded 事件 - 以修复空引用问题(请参阅问题)。
  • 您已关闭此问题并提供了对主题“从 XAML 调用参数化构造函数”的参考作为解决方案。我的意思是,拜托——你从来没有说过我说过你说过的话?认真的吗?
  • 是的,认真的。这个问题与另一个问题完全相同。我既不宣传也不批评在另一个问题中找到的答案。以其他方式思考和主张是您的错误。我只是将这个问题确定为完全重复的问题。我对 this 问题的 OP 的建议包含在我关闭问题时包含的评论中。如果你觉得你有更好的答案,一定要把它贴在那里。但这些都不会改变我的看法,即您提出的解决方案并不比副本中找到的答案更好,在某些情况下更糟。
  • 我添加了第二个解决方案,它使用了PropertyChangedCallback
猜你喜欢
  • 1970-01-01
  • 2011-05-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多