当DataGrid发生更改时,为什么我的ViewModel中的属性没有更新?

时间:2013-07-28 13:22:26

标签: c# wpf binding dictionary ivalueconverter

我正在尝试创建一个UserControl,让我可以在网格中编辑Dictionary<string,string>类型的词典(目前只编辑条目,而不是添加或删除)。

每当我将DataGrid绑定到Dictionary时,它将网格显示为只读,因此我决定创建一个值转换器,将其转换为ObservableCollection<DictionaryEntry>,其中DictionaryEntry只是一个类两个属性KeyValue

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

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

下面是我能做的最小的演示,展示了我正在做的事情。同样问题是,当我更改网格中的值时,MyDictionary上的MainViewModel未更新。为什么呢?

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 个答案:

答案 0 :(得分: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
}

在你的ViewModel(或后面的代码)中,你会像这样实例化它......

    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界面与DataGrids相比,它是一个众所周知的“陷阱”,并且在这里有对它的描述...... http://blogs.msdn.com/b/vinsibal/archive/2009/04/07/5-random-gotchas-with-the-wpf-datagrid.aspx

说...

  

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