2011-07-07 1 views
3

Я пытаюсь реализовать сценарий master/detail с шаблоном MVVM в проекте Windows Forms (я злюсь, я знаю). Рассмотрят следующие модели вида:Windows Forms привязка данных и путь к свойствам: как обрабатывать значение nullability?

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

Я пытаюсь связать Подствойство DetailViewModel к текстовому полю, используя следующий код (и путь свойства, поддерживаемый Windows Forms привязки данных):

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

It работает отлично (например, WPF !!!) ... до тех пор, пока SelectedDetail не будет нулевым (для моей логики значение null SelectedDetail означает, что в главном представлении ничего не выбрано). Если SelectedDetail имеет значение null, я получил ArgumentNullException (имя параметра: компонент).

Есть ли способ обработать нулеустойчивость свойства «parent» (внутри пути навигации)?

Вот исключение StackTrace:

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) 
+0

Вы не сумасшедший. Я делаю это, и со всей серьезностью это блаженство. IMO - единственный способ управлять проектом winforms. – jnm2

ответ

2

Вы должны 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

+1

Спасибо, он работает. Действительно ли необходимо избавиться от BindingSource, созданного во время выполнения, вместо использования конструктора? – Notoriousxl

0

Вы можете попробовать, как это вместо:

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

Таким образом, текстовое поле не обновляется, если свойство «SelectedDetail» изменяется ... – Notoriousxl

+0

@Notoriousxl: А, я вижу. Вы можете обработать событие SubPropertyChanged в SelectedDetail и поднять SelectedDetailChanged. Это может сработать. –

+0

Я искал что-то более автоматическое и универсальное, сохраняя преимущества пути свойств (пунктирная строка SelectedDetail.SubProperty). В качестве (грязного) обходного пути я уже обрабатываю событие Format и Parse привязки (после добавления метода IsNull() в DetailViewModel): если элемент управления возвращает нулевой DetailViewModel, я преобразовываю его в DetailViewModel, который IsNull () возвращает true; если модель представления SelectedNetail IsNull() возвращает true, я преобразую ее в нулевое значение. – Notoriousxl

Смежные вопросы