【问题标题】:Windows Forms data binding and property path: how to handle nullability?Windows 窗体数据绑定和属性路径:如何处理可空性?
【发布时间】:2011-09-30 09:21:55
【问题描述】:

我正在尝试在 Windows 窗体项目中使用MVVM 模式实现master/detail 场景(我知道我很生气)。考虑以下视图模型:

public class MasterViewModel
{
    public BindingList<DetailViewModel> Details { get; set; }

    public DetailViewModel SelectedDetail
    {
        get
        {
            //
        }
        set
        {
            // raises SelectedDetailChanged
        }
    }
}

public class DetailViewModel
{
    public string SubProperty
    {
        get
        {
            // ...
        }
        set
        {
            // ... raises SubPropertyChanged
        }
    }
}

我正在尝试使用以下代码(以及 Windows 窗体数据绑定支持的属性路径)将 DetailViewModel 的 SubProperty 绑定到 TextBox:

            MasterViewModel masterViewModel;
            TextBox textBox;
            // ...

            Binding binding = new Binding("Text", masterViewModel, "SelectedDetail.SubProperty");
            binding.FormattingEnabled = true;
            binding.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
            binding.ControlUpdateMode = ControlUpdateMode.OnPropertyChanged;

            textBox.DataBindings.Add(binding);

它完美地工作(就像 WPF 一样!!!)...直到 SelectedDetail 为空(对于我的逻辑,SelectedDetail null 意味着在主视图中没有选择任何内容)。如果 SelectedDetail 为 null,我得到一个 ArgumentNullException(参数名称:组件)。

有没有办法处理“父”属性的可空性(在导航路径内)?

这里是异常堆栈跟踪:

   in System.ComponentModel.ReflectPropertyDescriptor.AddValueChanged(Object component, EventHandler handler)
   in System.Windows.Forms.BindToObject.CheckBinding()
   in System.Windows.Forms.BindToObject.SetBindingManagerBase(BindingManagerBase lManager)
   in System.Windows.Forms.Binding.SetListManager(BindingManagerBase bindingManagerBase)
   in System.Windows.Forms.ListManagerBindingsCollection.AddCore(Binding dataBinding)
   in System.Windows.Forms.BindingsCollection.Add(Binding binding)
   in System.Windows.Forms.BindingContext.UpdateBinding(BindingContext newBindingContext, Binding binding)
   in System.Windows.Forms.Control.UpdateBindings()
   in System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.set_BindingContextInternal(BindingContext value)
   in System.Windows.Forms.ContainerControl.set_BindingContext(BindingContext value)
   in System.Windows.Forms.ContainerControl.get_BindingContext()
   in System.Windows.Forms.Control.get_BindingContextInternal()
   in System.Windows.Forms.ContainerControl.get_BindingContext()
   in System.Windows.Forms.Control.get_BindingContextInternal()
   in System.Windows.Forms.ContainerControl.get_BindingContext()
   in System.Windows.Forms.Control.get_BindingContextInternal()
   in System.Windows.Forms.ContainerControl.get_BindingContext()
   in System.Windows.Forms.Control.get_BindingContextInternal()
   in System.Windows.Forms.Control.get_BindingContext()
   in System.Windows.Forms.Control.get_BindingContextInternal()
   in System.Windows.Forms.Control.get_BindingContext()
   in System.Windows.Forms.Control.UpdateBindings()
   in System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   in System.Windows.Forms.DataGridView.OnBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnParentBindingContextChanged(EventArgs e)
   in System.Windows.Forms.Control.OnBindingContextChanged(EventArgs e)
   in System.Windows.Forms.ContainerControl.OnCreateControl()
   in System.Windows.Forms.UserControl.OnCreateControl()
   in System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   in System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   in System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   in System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   in System.Windows.Forms.Control.CreateControl()
   in System.Windows.Forms.Control.WmShowWindow(Message& m)
   in System.Windows.Forms.Control.WndProc(Message& m)
   in System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   in System.Windows.Forms.ContainerControl.WndProc(Message& m)
   in System.Windows.Forms.Form.WmShowWindow(Message& m)
   in System.Windows.Forms.Form.WndProc(Message& m)
...
   in System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   in System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   in System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

【问题讨论】:

  • 你没疯。我一直在这样做,说真的,这是幸福。 IMO 是管理 winforms 项目的唯一方法。

标签: .net winforms data-binding mvvm navigation-properties


【解决方案1】:

您需要BindingSource 来提供一个间接层。

bindingSource1 = new BindingSource(components);
bindingSource1.DataMember = "Details";
bindingSource1.DataSource = typeof(MasterViewModel);

Binding binding = new Binding("Text", bindingSource1, "SubProperty");
binding.FormattingEnabled = true;
binding.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
binding.ControlUpdateMode = ControlUpdateMode.OnPropertyChanged;

textBox.DataBindings.Add(binding);

更详细的例子:BindingSource and BindingNavigator in C# 2.0

【讨论】:

  • 谢谢,它有效。真的有必要处置在运行时创建的 BindingSource 而不是使用设计器吗?
【解决方案2】:

您可能想这样尝试:

 Binding binding = 
    new Binding("Text", masterViewModel.SelectedDetail, "SubProperty");

【讨论】:

  • @Notoriousxl:啊,我明白了。您可以在 SelectedDetail 中处理 SubPropertyChanged 事件并从那里引发 SelectedDetailChanged。这可能行得通。
  • 我一直在寻找更自动和通用的东西,保持属性路径的好处(点“SelectedDetail.SubProperty”字符串)。作为(肮脏的)解决方法,我已经在处理绑定的 Format 和 Parse 事件(在向 DetailViewModel 添加 IsNull() 方法之后):如果控件返回 null DetailViewModel,我将其转换为 IsNull( ) 方法返回 true;如果视图模型 SelectedDetail 的 IsNull() 返回 true,我将其转换为 null 值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-14
  • 2011-03-23
  • 2019-06-29
  • 2012-02-12
相关资源
最近更新 更多