2011-07-06 23 views
6

Używam MVVM, VS 2008 i .NET 3.5 SP1. Mam listę przedmiotów, z których każda eksponuje właściwość IsSelected. Dodałem CheckBox, aby zarządzać wyborem/anulowaniem wyboru wszystkich pozycji na liście (aktualizując właściwość IsSelected każdej pozycji). Wszystko działa, z tym wyjątkiem, że właściwość IsChecked nie jest aktualizowana w widoku, gdy zdarzenie PropertyChanged jest uruchamiane dla kontrolki kontrolnej CheckBox.Dlaczego moje powiązanie CheckBox WPF nie działa?

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

My 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"); 
    } 
} 

nie jestem pokazujący realizację SelectAllCommand lub indywidualnego wyboru pozycji, ale to nie wydaje się być istotne. Gdy użytkownik wybierze pojedynczą pozycję na liście (lub kliknie przycisk CheckBox, aby wybrać/odznaczyć wszystkie elementy), sprawdziłem, czy linia kodu OnPropertyChanged ("AreAllSelected") jest wykonywana, a śledzenie w debugerze Zdarzenie PropertyChanged zostało zasubskrybowane i jest uruchamiane zgodnie z oczekiwaniami. Jednak pobieranie właściwości AreAllSelected jest wykonywane tylko raz - kiedy widok jest faktycznie renderowany. Okno wyjściowe programu Visual Studio nie zgłasza żadnych błędów powiązania danych, więc z tego co wiem, właściwość IsSelected CheckBox jest poprawnie powiązana.

Gdybym wymienić CheckBox z przyciskiem:

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

i zaktualizować 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"); 
} 

wszystko działa zgodnie z oczekiwaniami - tekst przycisku jest aktualizowany jak wybierane są wszystkie elementy/desected. Czy jest coś, czego mi brakuje w powiązaniu na właściwości IsSelected CheckBox?

Dzięki za pomoc!

Odpowiedz

5

Znalazłem problem. Wygląda na to, że w pakiecie WPF 3.0 występował błąd z powiązaniami OneWay w IsChecked, co powoduje usunięcie powiązania. Dzięki pomocy this post wygląda na to, że błąd został naprawiony w WPF 4.0

Aby odtworzyć, utwórz nowy projekt WPF.

Dodaj FooViewModel.cs:

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; 
    } 
    } 
} 

Zmienić 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!"); 
    } 
    } 
} 

Zmienić 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> 

kliknąć na przycisk kilka razy i zobacz stan CheckBox przełączania między true i null (nie false). Ale kliknij na CheckBox, a zobaczysz, że Powiązanie zostanie usunięte z właściwości IsChecked.

Obejście:

Aktualizacja IsChecked wiążące się TwoWay i ustawić jej UpdateSourceTrigger być jawne:

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

i aktualizuje związany właściwość więc to już nie jest tylko do odczytu:

public bool? IsCheckedState 
{ 
    get { return _isCheckedState; } 
    set { } 
} 
Powiązane problemy