【问题标题】:How to bind to property with only get accessor如何仅使用 get 访问器绑定到属性
【发布时间】:2013-08-21 02:40:49
【问题描述】:

我的 wpf 窗口上有一些自定义的可编辑列表框。 我还有一个带有 Property Changed 的​​ viewmodel 类,看起来像这样:

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

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

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

我的问题是,如果其中一个列表框行发生更改,如何更新“保存”按钮?

【问题讨论】:

    标签: c# wpf mvvm viewmodel


    【解决方案1】:

    处理按钮的正确方法是实现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 来强制更新

    【讨论】:

      【解决方案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));
              }
          }
      }
      

      【讨论】:

        【解决方案3】:

        当您更改 customers 中的客户时,您的 ViewModel 应该实现 INotifyPropertyChanged 并且应该为 HasChanges 引发 PropertyChanged 事件

        更新:

        如果客户实现 INotifyPropertyChanged 并且客户它本身就是一个可观察的集合。您可以订阅 customers 集合的 CollectionChangedEvent 中的所有客户,具体取决于取消订阅操作。

        【讨论】:

          【解决方案4】:

          如果您的 ViewModel 实现了 INotifyPropertyChanged,您只需在 HasChanges 上调用 OnPropertyChanged() 方法。对于 Prism,等效方法是 RaisePropertyChanged。

          但是,对于 MVVM,您可能希望将该测试放在绑定到按钮 Command 属性的命令的 CanExecute 方法中。这将自动处理 IsEnabled。

          【讨论】:

            【解决方案5】:

            按钮必须以某种方式接收通知。在您的情况下,您可能在视图模型中实现 INotifyPropertyChanged 接口。当您的“列表框行更改”时,您应该为“HasChanges”属性引发 PropertyChanged 事件。但是在您的视图模型中应该注意更改,并且应该引发事件。 作为一个不同的解决方案,因为你有一个视图模型,你可以在你的按钮上使用一个命令,并且 CanExecute 的逻辑会返回真或假,当发生变化时,你也必须标记它。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-11-10
              • 2012-04-24
              • 2015-01-22
              • 2010-12-11
              • 2016-05-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多