2011-07-06 10 views
6

MVVM, VS 2008 및 .NET 3.5 SP1을 사용하고 있습니다. IsSelected 속성을 노출하는 항목 목록이 있습니다. CheckBox를 추가하여 목록의 모든 항목 (각 항목의 IsSelected 속성 업데이트)의 선택/선택 취소를 관리합니다. CheckBox의 바운드 컨트롤에 대해 PropertyChanged 이벤트가 발생하면 IsChecked 속성이 뷰에서 업데이트되지 않는 것을 제외하고는 모두 작동합니다.WPF CheckBox 바인딩이 작동하지 않는 이유는 무엇입니까?

<CheckBox 
    Command="{Binding SelectAllCommand}" 
    IsChecked="{Binding Path=AreAllSelected, Mode=OneWay}" 
    Content="Select/deselect all identified duplicates" 
    IsThreeState="True" /> 

내 VM :

public class MainViewModel : BaseViewModel 
{ 
    public MainViewModel(ListViewModel listVM) 
    { 
    ListVM = listVM; 
    ListVM.PropertyChanged += OnListVmChanged; 
    } 

    public ListViewModel ListVM { get; private set; } 
    public ICommand SelectAllCommand { get { return ListVM.SelectAllCommand; } } 

    public bool? AreAllSelected 
    { 
    get 
    { 
     if (ListVM == null) 
     return false; 

     return ListVM.AreAllSelected; 
    } 
    } 

    private void OnListVmChanged(object sender, PropertyChangedEventArgs e) 
    { 
    if (e.PropertyName == "AreAllSelected") 
     OnPropertyChanged("AreAllSelected"); 
    } 
} 

는 여기 SelectAllCommand 또는 개별 항목 선택의 구현을 보여주는 아니지만, 관련 될 것 같지 않습니다. 사용자가 목록에서 단일 항목을 선택하거나 문제가있는 CheckBox를 클릭하여 모든 항목을 선택/선택 취소하면 코드의 OnPropertyChanged ("AreAllSelected") 행이 실행되고 디버거에서 추적이 표시되는지 확인할 수 있습니다. PropertyChanged 이벤트가 구독되어 예상대로 발생합니다. 그러나 AreAllSelected 속성의 get은 한 번만 실행됩니다. 뷰가 실제로 렌더링 될 때입니다. Visual Studio의 출력 윈도우는 데이터 바인딩 오류를보고하지 않으므로 내가 말할 수있는 것부터 CheckBox의 IsSelected 속성이 올바르게 바인딩되었습니다.

가 나는 버튼과 CheckBox에 교체하는 경우 :

<Button Content="{Binding SelectAllText}" Command="{Binding SelectAllCommand}"/> 

을하고 VM을 업데이트 :

... 

public string SelectAllText 
{ 
    get 
    { 
    var msg = "Select All"; 
    if (ListVM != null && ListVM.AreAllSelected != null && ListVM.AreAllSelected.Value) 
     msg = "Deselect All"; 

    return msg; 
    } 
} 

... 

private void OnListVmChanged(object sender, PropertyChangedEventArgs e) 
{ 
    if (e.PropertyName == "AreAllSelected") 
    OnPropertyChanged("SelectAllText"); 
} 

모든 것이 예상대로 작동 - 모든 항목이 선택 될 때 버튼의 텍스트가 desected/업데이트됩니다. CheckBox의 IsSelected 속성에서 바인딩에 대해 놓친 부분이 있습니까?

도움 주셔서 감사합니다.

답변

5

문제점을 발견했습니다. IsChecked에 OneWay 바인딩이있는 WPF 3.0에 버그가있어 바인딩이 제거되었습니다. 도움을 주시면 this post에게 도움을 주셨습니다. 버그가 WPF 4.0에서 수정 된 것 같습니다.

재현하려면 새 WPF 프로젝트를 만드십시오.

using System; 
using System.ComponentModel; 
using System.Windows.Input; 

namespace Foo 
{ 
    public class FooViewModel : INotifyPropertyChanged 
    { 
    private bool? _isCheckedState = true; 

    public FooViewModel() 
    { 
     ChangeStateCommand = new MyCmd(ChangeState); 
    } 

    public bool? IsCheckedState 
    { 
     get { return _isCheckedState; } 
    } 

    public ICommand ChangeStateCommand { get; private set; } 

    private void ChangeState() 
    { 
     switch (_isCheckedState) 
     { 
     case null: 
      _isCheckedState = true; 
      break; 
     default: 
      _isCheckedState = null; 
      break; 
     } 

     OnPropertyChanged("IsCheckedState"); 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    protected void OnPropertyChanged(string propertyName) 
    { 
     var changed = PropertyChanged; 
     if (changed != null) 
     changed(this, new PropertyChangedEventArgs(propertyName)); 
    } 
    } 

    public class MyCmd : ICommand 
    { 
    private readonly Action _execute; 
    public event EventHandler CanExecuteChanged; 

    public MyCmd(Action execute) 
    { 
     _execute = execute; 
    } 

    public void Execute(object parameter) 
    { 
     _execute(); 
    } 

    public bool CanExecute(object parameter) 
    { 
     return true; 
    } 
    } 
} 

수정 Window1.xaml.cs :

using System.Windows; 
using System.Windows.Controls.Primitives; 

namespace Foo 
{ 
    public partial class Window1 
    { 
    public Window1() 
    { 
     InitializeComponent(); 
    } 

    private void OnClick(object sender, RoutedEventArgs e) 
    { 
     var bindingExpression = MyCheckBox.GetBindingExpression(ToggleButton.IsCheckedProperty); 
     if (bindingExpression == null) 
     MessageBox.Show("IsChecked property is not bound!"); 
    } 
    } 
} 

수정 Window1.xaml : 버튼에

<Window 
    x:Class="Foo.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:vm="clr-namespace:Foo" 
    Title="Window1" 
    Height="200" 
    Width="200" 
    > 

    <Window.DataContext> 
    <vm:FooViewModel /> 
    </Window.DataContext> 

    <StackPanel> 
    <CheckBox 
     x:Name="MyCheckBox" 
     Command="{Binding ChangeStateCommand}" 
     IsChecked="{Binding Path=IsCheckedState, Mode=OneWay}" 
     Content="Foo" 
     IsThreeState="True" 
     Click="OnClick"/> 
    <Button Command="{Binding ChangeStateCommand}" Click="OnClick" Content="Change State"/> 
    </StackPanel> 
</Window> 

클릭 몇 번

FooViewModel.cs 추가 true 및 null (false가 아님) 사이에서 CheckBox의 상태 전환을 확인하십시오. 그러나 CheckBox를 클릭하면 IsChecked 속성에서 바인딩이 제거 된 것을 볼 수 있습니다.

해결 방법 :

업데이트의 IsChecked 양방향으로 바인딩을 명시하기 위해 UpdateSourceTrigger 설정 : 더 이상 읽기 전용 그래서

IsChecked="{Binding Path=IsCheckedState, Mode=TwoWay, UpdateSourceTrigger=Explicit}" 

및 바운드 속성을 업데이트하지 :

public bool? IsCheckedState 
{ 
    get { return _isCheckedState; } 
    set { } 
} 
관련 문제