如何绑定仅具get访问器的属性

6

我有一些自定义可编辑的列表框在我的WPF窗口上。 我还有一个视图模型类,它具有类似于以下内容的属性更改:

public bool HasChanges
{
    get
    {
        return customers.Any(customer => customer.Changed);
    }
}

因此,我想将我的保存按钮绑定到这个属性:

<Button IsEnabled="{Binding HasChanges, Mode=OneWay}"...

我的问题是,如果列表框的一行被更改了,如何更新保存按钮?
5个回答

2
为了让WPF能够对属性的更改做出反应,类必须实现INotifyPropertyChanged接口。每当客户信息发生更改时,您需要发送通知,例如:
class CustomerList : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;
    private List<Customer> customers = ...
    public bool HasChanges {
        get {
            return customers.Any(customer => customer.Changed);
        }
    }
    // Callers who change customers inside your list must call this method
    public void ChangeCustomer(Customer c) {
        // Do whatever you need to do, ...
        ...
        // then send out the notification to WPF
        OnPropertyChanged("HasChanges");
    }
    protected void OnPropertyChanged(string name) {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

2
处理按钮的正确方式是实现ICommand接口。这里是我的解决方案中的一个示例:
public class RelayCommand : ICommand
{
    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    public RelayCommand(Action<object> execute) : this(execute, null)
    {
    }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;           
    }

    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

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

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    #endregion
}

您可以像这样将数据绑定到按钮:
<Button Command="{Binding MyCommand}" .../>

剩下的就是在你的视图模型上声明一个 ICommand 属性:
public ICommand MyCommand { get; private set; }

//in constructor:
MyCommand = new RelayCommand(_ => SomeActionOnButtonClick(), _ => HasChanges);

按钮的状态将在大多数更改时自动更新。如果由于某种原因它没有更新 - 您可以通过调用CommandManager.InvalidateRequerySuggested强制更新。

1
你的ViewModel应该实现INotifyPropertyChanged接口,并且在你更改customers中的客户时,应该为HasChanges引发PropertyChanged事件。
更新:
如果Customers实现了INotifyPropertyChanged并且customers本身是一个可观察的集合,那么你可以订阅(并根据操作取消订阅)customers集合的CollectionChangedEvent中的所有客户。

0
如果您的ViewModel实现了INotifyPropertyChanged接口,您只需要在HasChanges上调用OnPropertyChanged()方法即可。使用Prism时,等效的方法是RaisePropertyChanged。
然而,在MVVM中,您可能希望将该测试放在CanExecute方法中,该方法绑定到您的按钮Command属性。这将自动处理IsEnabled。

0
按钮必须以某种方式接收通知。在您的情况下,您可能会在视图模型中实现INotifyPropertyChanged接口。当您的“列表框行更改”时,应为“HasChanges”属性引发PropertyChanged事件。但是,在您的视图模型中应该注意到更改,并在那里引发事件。
作为另一种解决方案,由于您有一个视图模型,因此您可以在按钮上使用命令,CanExecute将具有返回true或false的逻辑,这也必须在更改发生时由您标记。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接