2017-10-23 17 views
6

내 ViewModel에 변수의 컬렉션을 가지고 :WPF 멀티 바인드가 예상되는 경우 소스를 업데이트하지 않습니다. '모두 선택'에 체크 박스가

public ObservableCollection<ObservableVariable> Variables { get; }= new ObservableCollection<ObservableVariable>(); 

ObservableVariable 클래스는 두 가지 속성이 있습니다 문자열 이름을 선택하고 선택 BOOL; 이 클래스는 WPF 뷰의 체크리스트에 바인딩되어 있고 MultiBinding을 사용하여 구현 된 해당 목록에 바인딩 된 '모두 선택'체크 상자가 있어야합니다. 다음 그림은 원하는보기를 보여줍니다.

WPF checklist with 'Select All'

아래 XAML 지키

<CheckBox Content="Select All" Name="SelectAllCheckbox"></CheckBox> 
... 
<ListBox ItemsSource="{Binding Variables}"> 
    <ListBox.ItemTemplate> 
     <DataTemplate> 
      <CheckBox Content="{Binding Name}"> 
       <CheckBox.IsChecked> 
        <MultiBinding Converter="{StaticResource LogicalOrConverter}" Mode="TwoWay"> 
         <Binding Path="Selected"></Binding> 
         <Binding ElementName="SelectAllCheckbox" Path="IsChecked"></Binding> 
        </MultiBinding> 
       </CheckBox.IsChecked> 
      </CheckBox> 
     </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

LogicalOrConverter bools는 임의의 수를 얻어; 있는 경우 true를 반환합니다.

위에서 볼 수 있듯이 각 확인란은 viewmodel의 변수와 '모두 선택'확인란의 상태에 바인딩됩니다. 현재는 다음을 제외하고 모두 원하는대로 작동합니다. '모두 선택'을 클릭하면보기에서 확인란이 업데이트되지만 변경 사항이 다시보기 모델로 전파되지 않습니다.

내 구현의 대부분의 기능이 올바르게 작동합니다. 예를 들어 개별 체크 박스를 클릭하면 뷰 모델이 올바르게 업데이트됩니다.

더 자세히 문제 :

내가 OnPropertyChanged를 이벤트가 그 상자 만 변경된 변수에 발사되는 개인의 체크 박스를 클릭하면, 변환기의 ConvertBack 함수가 시작됩니다. viewmodel이 업데이트되고 모두 잘됩니다.

그러나 "모두 선택"확인란을 클릭하면 개별 확인란이 뷰에서 업데이트되지만 OnPropertyChanged는 모든 변수에서 호출되지 않으며 변환기의 ConvertBack 함수가 호출되지 않습니다.

또한 "모두 선택"의 선택을 취소하면 개별 수표는 이전의 수표로 되돌아갑니다.

보기 모드를 업데이트하는 유일한 방법은 개별 확인란을 클릭하는 것입니다. 그러나 멀티 바인딩은보기 목적으로 작동합니다.

내 질문은 :

왜 뷰 모델에서 소스 컬렉션에 전파 확인란을 변경하지

컨버터 :

public class LogicalOrConverter : IMultiValueConverter 
{ 
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 

     foreach (object arg in values) 
     { 
      if ((arg is bool) && (bool)arg == true) 
      { 
       return true; 
      } 
     } 

     return false; 
    } 

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 
    { 
     object[] values = new object[2] {false, false}; 

     if (value is bool && (bool) value == true) 
      values[0] = true; 

     return values; 
    } 
} 

ObservableVariable 정의 :

public class ObservableVariable : INotifyPropertyChanged 
{ 
    private string _name; 
    public string Name 
    { 
     get { return _name; } 
     set 
     { 
      _name = value; 
      OnPropertyChanged(nameof(Name)); 
     } 
    } 

    private bool _selected; 
    public bool Selected 
    { 
     get { return _selected; } 
     set 
     { 
      _selected = value; 
      OnPropertyChanged(nameof(Selected)); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void OnPropertyChanged(string propertyName = null) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 
+0

질문 - "모두 확인"을 통해 선택한 다음 "모두 선택"을 선택 취소 한 경우 확인란이 있어야합니다. MValConverter를 사용하면 값이 VM에 전달되는 경우 선택 취소되지 않습니다. – Rekshino

+0

@Rekshino, 늦은 답변을 드려 죄송합니다. 선택 취소 작업이 어떻게 작동할까요? 내가하고 싶은 것에 따라 논리를 추가해야 할 수도 있습니다. 나는 선택 취소 물류를 별개의 문제로 생각하기 때문에 의도적으로 문제의 일부를 생략했다. –

+0

필자가 반드시 뷰 모델을 업데이트하기 위해 멀티 바인드를 사용하고 싶다면 해결 방법을 통해 얻을 수 있지만, 필자는이 뷰가 실용적으로 유용 할 것입니다. 나중에 필요한 경우 답변을 게시하겠습니다. – Rekshino

답변

5

다중 바인딩 문제는 두 데이터 변경에서 "트리거"되지만 첫 번째 바인딩 (Path="Selected")은 VM의 데이터를 업데이트 할 데이터 바인딩입니다 (즉, 데이터가 바인딩 된 것임). 두 번째 바인딩은 SelectAll 확인란을 트리거하고 IsChecked 속성을 변경합니다. MultiBinding이 있다고해서 다른 Bindings가 변경 사항을 서로 보급한다는 의미는 아닙니다.

이 때문에 SelectAll에 대한 클릭 동작이 표시되고 체크 박스는 변경되지만 데이터는 변경되지 않습니다. ViewModel에 데이터 변경을 알리기 위해 SelectAll 확인란의 메커니즘을 명시 적으로 설정하지 않아도됩니다.

약간의 시행 착오를 통해 나는 멀티 바인딩만으로이 작업을 수행 할 수있는 명확하고 쉬운 방법이 없다고 결론을 내 렸습니다. (누군가가 그것을 할 수있는 방법이 있다면 배우고 싶어합니다.) 나는 또한 지저분 해지고있는 DataTriggers를 시도했다. 내가 찾은 가장 좋은 방법은 ViewModel에 SelectAll 논리를 오프로드하고 SelectAll 확인란에 Command을 사용하는 것입니다. 이를 통해 로직을 적절히 제어 할 수 있으며 더 강력한 디버깅이 가능합니다.

새로운 XAML : 당신이 선택 제어의 선택을 취소 할 수 있습니다

<CheckBox Content="Select All" x:Name="SelectAllCheckbox" 
      Command="{Binding SelectAllCommand}" 
      CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}"/> 


    <ListBox ItemsSource="{Binding Variables}"> 
     <ListBox.ItemTemplate> 
      <DataTemplate> 
       <CheckBox Content="{Binding Name}" 
          IsChecked="{Binding Selected}"> 
       </CheckBox> 
      </DataTemplate> 
     </ListBox.ItemTemplate> 
    </ListBox> 

나는 매개 변수로의 IsChecked 포함되어 있습니다.

내 뷰 모델 :

public class ViewModel 
{ 
    public ObservableCollection<ObservableVariable> Variables { get; set; } 
    public ViewModel() 
    { 
     Variables = new ObservableCollection<ObservableVariable>(); 
     SelectAllCommand = new RelayCommand(SelectAll,()=>true); 
    } 

    public RelayCommand SelectAllCommand { get; set; } 

    public void SelectAll(object param) 
    { 
     foreach (var observableVariable in Variables) 
     { 
      observableVariable.Selected = (bool)param; 
     } 
    } 
} 

은 분명히 당신은 매개 변수에 더 나은 유효성 검사 논리를 원한다. 이것은 주로 짧은 대답입니다.

그리고 완전 사용을 위해 표준 RelayCommand Code i가 사용되었습니다.

public class RelayCommand : ICommand 
{ 
    public event EventHandler CanExecuteChanged 
    { 
     add { CommandManager.RequerySuggested += value; } 
     remove { CommandManager.RequerySuggested -= value; } 
    } 
    private Action<object> methodToExecute; 
    private Func<bool> canExecuteEvaluator; 
    public RelayCommand(Action<object> methodToExecute, Func<bool> canExecuteEvaluator) 
    { 
     this.methodToExecute = methodToExecute; 
     this.canExecuteEvaluator = canExecuteEvaluator; 
    } 
    public RelayCommand(Action<object> methodToExecute) 
     : this(methodToExecute, null) 
    { 
    } 
    public bool CanExecute(object parameter) 
    { 
     if (this.canExecuteEvaluator == null) 
     { 
      return true; 
     } 
     else 
     { 
      bool result = this.canExecuteEvaluator.Invoke(); 
      return result; 
     } 
    } 
    public void Execute(object parameter) 
    { 
     this.methodToExecute.Invoke(parameter); 
    } 
} 
+0

흥미 롭습니다. 뷰 모델에서 '모두 선택'논리를 사용하지 않으려했습니다. MVVM 패러다임을 위반하는 것 같지만, 아아. WPF에는 MVVM을 적절하게 구현하는 도구가 포함되어 있지 않습니다. –

+0

@ShaneSims 정말 MVVM을 위반하지 않습니다. 뷰 모델은 데이터와 뷰 사이의 추가 로직을 처리합니다. 그것이 당신이하는 일입니다. 좀 더 복잡한 방식으로 데이터 (Selected Property)를 변경하려고합니다. 데이터가 해당 상태를 유지할 때 순전히 UI 기반이 아닙니다 (XAML 또는 코드 뒤에 있음). –