2011-07-06 22 views
6

Estoy usando MVVM, VS 2008 y .NET 3.5 SP1. Tengo una lista de elementos, cada uno exponiendo una propiedad IsSelected. He agregado un CheckBox para administrar la selección/des-selección de todos los artículos en la lista (actualizando la propiedad IsSelected de cada artículo). Todo funciona, excepto que la propiedad IsChecked no se actualiza en la vista cuando el evento PropertyChanged se activa para el control vinculado de CheckBox.¿Por qué mi WPF CheckBox Binding no funciona?

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

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

no me muestra la implementación de SelectAllCommand o selección de elementos individuales aquí, pero no parece ser relevante. Cuando el usuario selecciona un solo elemento en la lista (o hace clic en el problema CheckBox para seleccionar/deseleccionar todos los elementos), he verificado que la línea de código OnPropertyChanged ("AreAllSelected") se ejecuta y el seguimiento en el depurador puede ver el evento PropertyChanged está suscrito y dispara como se esperaba. Pero el get de la propiedad AreAllSelected solo se ejecuta una vez, cuando la vista se procesa realmente. La ventana Salida de Visual Studio no informa ningún error de enlace de datos, por lo que puedo decir, la propiedad IsSelected del CheckBox está correctamente enlazada.

Si se sustituye la casilla de verificación con un botón:

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

y actualizar la máquina virtual:

... 

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

todo funciona como se espera - el texto del botón se actualiza a medida que todos los artículos son seleccionados/desected. ¿Hay algo que me falta sobre el enlace en la propiedad IsSelected del CheckBox?

¡Gracias por cualquier ayuda!

Respuesta

5

Encontré el problema. Parece que existió un error en WPF 3.0 con enlaces OneWay en IsChecked, lo que provocó que se elimine el enlace. Gracias a this post por la asistencia, parece que el error se solucionó en WPF 4.0

Para reproducir, cree un nuevo proyecto WPF.

Añadir 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; 
    } 
    } 
} 

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

Modificar 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> 

Haga clic en el botón un par de veces y ver el estado del CheckBox alternar entre verdadero y nulo (no falso). Pero haga clic en la casilla de verificación y verá que la vinculación se elimina de la propiedad IsChecked.

La solución:

actualización de la IsChecked unión a ser TwoWay y establecer su UpdateSourceTrigger ser explícitos:

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

y actualizar la propiedad obligados por lo que ya no es de sólo lectura:

public bool? IsCheckedState 
{ 
    get { return _isCheckedState; } 
    set { } 
} 
Cuestiones relacionadas