2013-07-28 3 views
0

UserControl을 만들려고합니다. 그러면 입력란 Dictionary<string,string>의 사전을 편집 할 수 있습니다 (항목을 지금까지 편집 중이거나 추가 또는 삭제하지 않음). 나는 사전에 데이터 그리드를 결합 할 때마다DataGrid가 변경 될 때 내 ViewModel의 속성이 업데이트되지 않는 이유는 무엇입니까?

그것은 읽기 전용으로 그리드를 보여줍니다, 그래서 나는 DictionaryEntry이 두 가지 속성 KeyValue와 단지 클래스 곳이 ObservableCollection<DictionaryEntry>로 변환 것, 값 변환기를 만들 수 decieded.

이것은 표를 사전에 표시하기 위해 작동하지만, 이제 표를 변경할 때 사전이 업데이트되지 않습니다. 나는 왜 그런지 확신 할 수 없다.

내 바인딩 설정 방식이나 가치 변환기에 문제가 있다고 생각합니다. 누군가가 약간의 빛을 비출 수 있다면 그것은 환상적 일 것입니다.

다음은 제가 할 수있는 가장 작은 데모입니다. 다시 문제는 내가 그리드의 값을 변경할 때 에있는 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

두 가지 문제 여기에 실제로있다 : 당신은 데이터 그리드의 항목을 편집 할 때문에 IEditableObject 당신의 DictionaryEntry 클래스는 바인딩 엔진이 제대로 작동하려면에서 INotifyPropertyChanged를 구현해야하고, 이차적으로는 구현해야하고 '무작위 결과'를 피하십시오. 그래서 클래스는 ... 꽤 가까운 당신의 ViewModel에서

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 인터페이스 마주 DataGrids를에

, 그것은 알려진 '잡았다'이고 말한다 여기에 대한 설명 ... 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 메서드는 절대로 호출되지 않으며 observable 컬렉션이 변경 될 때 호출되어야하는지 또는 다른 것을 연결해야합니까? –

+0

나는 그것을 내 대답에 포함하고 싶지 않았지만 값 변환기는 꽤 많은 청어이다. 나는 그것을 비울 것이다. 변경 사항이 작동하면 (그리고해야 함), 추가 추상화 계층에서 알림을 원하면 VM은 각 DictionaryEntry의 속성을 변경하고 해당 이벤트를 처리하기 위해 단일 이벤트 핸들러를 구현해야합니다. 또는 VM의 항목 소스로 '내부 사전'을 사용하십시오. –