2013-07-28 106 views
0

我试图创建一个UserControl,它可以让我在网格中编辑一个类型为Dictionary<string,string>的字典(只是编辑目前为止的条目,而不是添加或删除)。DataGrid更改时,为什么ViewModel中的属性未更新?

每当我的DataGrid绑定到一个词典将其显示为只读网格,所以我decieded创建一个值转换器,将其转换为ObservableCollection<DictionaryEntry>其中DictionaryEntry只是一个具有两个属性KeyValue类。

这适用于在网格中显示字典,但现在当我对网格进行更改时,我的字典未被更新。我不确定为什么。

我认为这可能是我设置绑定的方式或我的值转换器的问题。如果有人能够说出一些光明,那将是太棒了。

下面是我能做的最小的演示,显示了我在做什么。再次问题是,当我改变网格中的值时,我的MainViewModel上的MyDictionary未更新。为什么?

MainViewModel.cs

public class MainViewModel : INotifyPropertyChanged 
{ 
    public MainViewModel() 
    { 
     _myDictionary = new Dictionary<string, string>() 
      { 
       {"Key1", "Value1"}, 
       {"Key2", "Value2"}, 
       {"Key3", "Value3"} 
      }; 
    } 
    private Dictionary<string, string> _myDictionary; 
    public Dictionary<string, string> MyDictionary 
    { 
     get 
     { 
      return _myDictionary; 
     } 
     set 
     { 
      if (_myDictionary == value) 
       return; 
      _myDictionary = value; 
      OnPropertyChanged("MyDictionary"); 
     } 
    } 
... 
} 

MainWindow.xaml

<Window ...> 
    <Window.Resources> 
     <local:MainViewModel x:Key="MainViewModel"></local:MainViewModel> 
    </Window.Resources> 
    <StackPanel Name="MainStackPanel" DataContext="{Binding Source={StaticResource MainViewModel}}"> 
     <local:DictionaryGrid /> 
     <Button Content="Print Dictionary" Click="PrintDictionary"></Button>   
    </StackPanel> 
</Window> 

DictionaryGrid.xaml

<UserControl ...> 
     <UserControl.Resources> 
     <testingGrid:DictionaryToOcConverter x:Key="Converter" /> 
    </UserControl.Resources> 
    <Grid> 
     <DataGrid ItemsSource="{Binding MyDictionary, 
            Converter={StaticResource Converter}}" 
     /> 
    </Grid> 
</UserControl> 

DictionaryToOcConverter.cs

public class DictionaryToOcConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     var collection = new ObservableCollection<DictionaryEntry>(); 
     var dictionary = value as Dictionary<string, string>; 
     if (dictionary != null) 
     { 
      foreach (var kvp in dictionary) 
       collection.Add(new DictionaryEntry { Key = kvp.Key, Value = kvp.Value }); 
     } 
     return collection; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     var dictionary = new Dictionary<string, string>(); 

     var entries = value as ObservableCollection<DictionaryEntry>; 
     if (entries != null) 
     { 
      foreach (var entry in entries) 
       dictionary.Add(entry.Key, entry.Value); 
     } 

     return dictionary; 
    } 
    public class DictionaryEntry 
    { 
     public string Key { get; set; } 
     public string Value { get; set; } 
    } 
} 

回答

1

其实这里有两个问题:因为你想在数据网格编辑内容会DictionaryEntry类应执行INotifyPropertyChanged与绑定引擎正常工作,其次它应该实现IEditableObject和避免“随机结果”。所以,你的类应该是这个样子......

public class DictionaryEntry : INotifyPropertyChanged, IEditableObject 
{ 
    private string _k; 
    [Description("The key")] 
    public string K 
    { 
     [DebuggerStepThrough] 
     get { return _k; } 
     [DebuggerStepThrough] 
     set 
     { 
      if (value != _k) 
      { 
       _k = value; 
       OnPropertyChanged("K"); 
      } 
     } 
    } 
    private string _v; 
    [Description("The value")] 
    public string V 
    { 
     [DebuggerStepThrough] 
     get { return _v; } 
     [DebuggerStepThrough] 
     set 
     { 
      if (value != _v) 
      { 
       _v = value; 
       OnPropertyChanged("V"); 
      } 
     } 
    } 
    #region INotifyPropertyChanged Implementation 
    public event PropertyChangedEventHandler PropertyChanged; 
    protected virtual void OnPropertyChanged(string name) 
    { 
     var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null); 
     if (handler != null) 
     { 
      handler(this, new PropertyChangedEventArgs(name)); 
     } 
    } 
    #endregion 
    #region IEditableObject 
    public void BeginEdit() 
    { 
     // implementation goes here 
    } 
    public void CancelEdit() 
    { 
     // implementation goes here 
    } 
    public void EndEdit() 
    { 
     // implementation goes here 
    } 
    #endregion 
} 

在您的视图模型(或代码隐藏),你会实例化它像这样...

public ObservableCollection<DictionaryEntry> MyItems { get; set; } 
    public ViewModel() 
    { 
     MyItems = new ObservableCollection<DictionaryEntry>(); 
     MyItems.Add(new DictionaryEntry{K="string1", V="value1"}); 
     MyItems.Add(new DictionaryEntry { K = "color", V = "red" }); 
    } 

...这是非常接近你有什么。而Xaml看起来是这样的......

<DataGrid ItemsSource="{Binding MyItems}" AutoGenerateColumns="True"> 
    </DataGrid> 

这些事情会带来你以后的行为。即,编辑将是粘性的。

IEditableObject接口面对面的人DataGrid中,这是一个众所周知的“疑难杂症”,并有它的位置的描述...... http://blogs.msdn.com/b/vinsibal/archive/2009/04/07/5-random-gotchas-with-the-wpf-datagrid.aspx

它说...

如果你是不熟悉IEditableObject,请参阅此MSDN文章 ,它有很好的解释和代码示例。DataGrid通过IEditableObject 接口烘焙了 的事务编辑功能。当您开始编辑单元格时,DataGrid将进入单元格 编辑模式以及行编辑模式。这意味着你可以取消/提交单元格以及取消/提交行。例如,I 编辑单元格0并按Tab键到下一个单元格。当按下标签 时,单元0被提交。我开始在单元格1中输入并意识到我要取消 的操作。我按'Esc'可恢复单元格1.我现在意识到我想要取消整个操作,因此我再次按'Esc',现在单元格 0恢复为其原始值。

+0

谢谢您的回复。我做了这些改变。内部字典会改变字符串,但它们不会回到我的主视图模型中,我是否错过了一些必须通过IValueConverter返回的内容? ConvertBack方法永远不会被调用,并且不确定调用它的正确方式,当可观察集合发生更改时是否应该调用它,还是必须连接其他的东西? –

+0

我不想将它包含在我的答案中,但价值转换器几乎是一个红鲱鱼。我会把它。如果这些更改正在运行(他们应该这么做!),并且您希望通知位于更深层次的抽象层中,那么VM应该预订每个DictionaryEntry的属性,并实现一个事件处理程序来处理它们。或者只是使用'内部词典'作为虚拟机的物品来源。 –

相关问题