2013-07-28 5 views
0

Я пытаюсь создать UserControl, который позволит мне редактировать словарь типа Dictionary<string,string> в сетке (только редактируя записи до сих пор, не добавляя или не удаляя).Почему в моем ViewModel не изменяется свойство, когда изменяется DataGrid?

Всякий раз, когда я привязать DataGrid к словарю он показывает сетку только для чтения, так что я decieded создать преобразователь значений, что бы преобразовать его в ObservableCollection<DictionaryEntry> где DictionaryEntry просто класс с двумя свойствами Key и Value.

Это работает для отображения словаря в сетке, но теперь, когда я вношу изменения в сетку, мой словарь не обновляется. Я не знаю, почему.

Я думаю, что это либо проблема с тем, как у меня установлены привязки, либо мой конвертер значений. Если бы кто-нибудь мог пролить свет, это было бы фантастически.

Ниже представлена ​​самая маленькая демонстрация, которую я мог бы сделать, чтобы показать, что я делаю. Опять проблема заключается в том, что когда я изменяю значения в сетке, 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

Там на самом деле две проблемы: Ваш 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> 

Эти вещи приведут к поведению вас после. I.e., правки будут липкими.

На DataGrids IEditableObject интерфейс визави, это известная «Гоча» и есть описание этого здесь ... 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 и нажмите вкладку в следующую ячейку. Ячейка 0 фиксируется при нажатии клавиши . Я начинаю печатать в ячейке 1 и понимаю, что хочу отменить операцию . Я нажимаю «Esc», который возвращает ячейку 1. Теперь я понимаю, что я хочу отменить всю операцию, чтобы снова нажать «Esc», и теперь ячейка 0 возвращается к исходному значению.

+0

Благодарим за отзыв. Я сделал эти изменения. Внутренний словарь меняет палку, но они не возвращаются к моей основной модели представления, я пропущу что-то, что мне нужно сделать, чтобы заставить его вернуться через IValueConverter? Метод ConvertBack никогда не вызывается и не уверен в правильном способе его вызова, должен ли он вызываться, когда меняется наблюдаемая коллекция, или мне нужно подключить что-то еще? –

+0

Я не хотел включать его в свой ответ, но конвертер значений в значительной степени является красной селедкой. Я бы это уложил. Если изменения работают (что им нужно!), И вы хотите получать уведомления в еще одном слое абстракции, тогда виртуальная машина должна подписаться на каждое свойство DictionaryEntry и реализовать один обработчик событий для их обработки. Или просто используйте «внутренний словарь» в качестве источника элементов виртуальной машины. –