2011-07-06 57 views
6

Tôi đang sử dụng MVVM, VS 2008 và .NET 3.5 SP1. Tôi có một danh sách các mục, mỗi mục phơi bày một thuộc tính IsSelected. Tôi đã thêm một Hộp kiểm để quản lý việc chọn/bỏ chọn tất cả các mục trong danh sách (cập nhật thuộc tính IsSelected của mỗi mục). Mọi thứ đang hoạt động ngoại trừ thuộc tính IsChecked không được cập nhật trong khung nhìn khi sự kiện PropertyChanged kích hoạt để kiểm soát ràng buộc của CheckBox.Tại sao WPF CheckBox Binding của tôi không hoạt động?

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

Tôi không thấy việc thực hiện SelectAllCommand hoặc lựa chọn mục cá nhân ở đây, nhưng nó dường như không có liên quan. Khi người dùng chọn một mục duy nhất trong danh sách (hoặc nhấp vào vấn đề CheckBox để chọn/bỏ chọn tất cả các mục), tôi đã xác minh rằng dòng mã OnPropertyChanged ("AreAllSelected") thực hiện và truy tìm trong trình gỡ lỗi, có thể thấy sự kiện PropertyChanged được đăng ký và không kích hoạt như mong đợi. Nhưng giá trị của thuộc tính AreAllSelected chỉ được thực hiện một lần - khi chế độ xem thực sự được hiển thị. Cửa sổ Output của Visual Studio không báo cáo bất kỳ lỗi ràng buộc dữ liệu nào, vì vậy từ những gì tôi có thể nói, thuộc tính IsSelected của CheckBox bị ràng buộc đúng cách.

Nếu tôi thay thế các CheckBox với một Button:

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

và cập nhật 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"); 
} 

mọi thứ hoạt động như mong đợi - văn bản của nút được cập nhật như tất cả các mục được lựa chọn/desected. Có cái gì tôi đang thiếu về Binding trên tài sản IsSelected của CheckBox?

Cảm ơn bạn đã trợ giúp!

Trả lời

5

Tôi đã tìm thấy sự cố. Có vẻ như một lỗi tồn tại trong WPF 3.0 với các ràng buộc OneWay trên IsChecked khiến cho ràng buộc bị loại bỏ. Nhờ có this post để được hỗ trợ, có vẻ như lỗi đã được khắc phục trong WPF 4.0

Để tái tạo, hãy tạo một dự án WPF mới.

Thêm một 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; 
    } 
    } 
} 

Sửa 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!"); 
    } 
    } 
} 

Sửa 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> 

Bấm vào nút một vài lần và xem trạng thái chuyển đổi trạng thái của CheckBox giữa true và null (không phải false). Nhưng click vào CheckBox và bạn sẽ thấy rằng Binding được loại bỏ khỏi thuộc tính IsChecked.

Cách giải quyết:

Cập nhật các IsChecked ràng buộc để được TwoWay và thiết lập UpdateSourceTrigger của nó sẽ được rõ ràng:

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

và cập nhật thuộc tính ràng buộc vì vậy nó không còn read-only:

public bool? IsCheckedState 
{ 
    get { return _isCheckedState; } 
    set { } 
} 
Các vấn đề liên quan