为什么不是';我的ViewModel中的某个属性在DataGrid更改时更新

本文关键字:属性 DataGrid 更新 我的 ViewModel 为什么不 | 更新日期: 2023-09-27 18:25:10

我正在尝试创建一个UserControl,它将允许我在网格中编辑Dictionary<string,string>类型的Dictionary(到目前为止只是编辑条目,而不是添加或删除)。

每当我将DataGrid绑定到Dictionary时,它都会将网格显示为只读,因此我决定创建一个值转换器,将其转换为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");
        }
    }
...
}

主窗口.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; }
    }
}

为什么不是';我的ViewModel中的某个属性在DataGrid更改时更新

这里实际上有两个问题: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>

这些事情会带来你想要的行为。也就是说,编辑会有粘性。

在相对于DataGrids的IEditableObject接口上,它是一个已知的"gotcha",这里有它的描述。。。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键进入下一个单元格。当按tab键。我开始在单元格1中输入,意识到我想取消操作。我按"Esc"键,返回单元格1。我现在意识到想取消整个操作,所以我再次按"Esc",现在是单元格0将恢复为其原始值。