【问题标题】:How call a general method with data binding?如何调用具有数据绑定的通用方法?
【发布时间】:2012-07-17 15:56:22
【问题描述】:

我想了解在处理许多属性时如何正确使用 MVVM 和数据绑定。

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <TextBox Height="23" HorizontalAlignment="Left" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="463" Text="{Binding OriginalText, UpdateSourceTrigger=PropertyChanged}" />
    <Label Height="28" HorizontalAlignment="Left" Margin="12,242,0,0" Name="label1" VerticalAlignment="Top" Width="463" Content="{Binding ModifiedText}"/>
    <CheckBox Content="Upper" Height="16" HorizontalAlignment="Left" Margin="12,41,0,0" Name="checkBox1" VerticalAlignment="Top" />
    <CheckBox Content="Underline" Height="16" HorizontalAlignment="Left" Margin="12,63,0,0" Name="checkBox2" VerticalAlignment="Top" />
    <CheckBox Content="Bold" Height="16" HorizontalAlignment="Left" Margin="12,85,0,0" Name="checkBox3" VerticalAlignment="Top" />
    <CheckBox Content="Shadow" Height="16" HorizontalAlignment="Left" Margin="12,107,0,0" Name="checkBox4" VerticalAlignment="Top" />
    <CheckBox Content="Red" Height="16" HorizontalAlignment="Left" Margin="12,129,0,0" Name="checkBox5" VerticalAlignment="Top" />
    <CheckBox Content="Scary" Height="16" HorizontalAlignment="Left" Margin="12,151,0,0" Name="checkBox6" VerticalAlignment="Top" />
    <CheckBox Content="Remove first letter" Height="16" HorizontalAlignment="Left" Margin="12,173,0,0" Name="checkBox7" VerticalAlignment="Top" />
    <CheckBox Content="Remove last letter" Height="16" HorizontalAlignment="Left" Margin="12,195,0,0" Name="checkBox8" VerticalAlignment="Top" />
</Grid>

我有一个 OriginalText 文本框和一个 ModifiedText 标签。当我选中一个框时,我想直接应用修改而无需单击按钮。我该怎么做?

在我的 ViewModel 中,我创建了绑定到 XAML CheckBox 的所有属性。

    private string _originalText = string.Empty;
    public string OriginalText
    {
        get { return _originalText; }
        set
        {
            _originalText = value;
            NotifyPropertyChanged("OriginalText");
        }
    }

    private string _modifiedText;
    public string ModifiedText
    {
        get { return _originalText; }
        set
        {
            _originalText = value;
            NotifyPropertyChanged("ModifiedText");
        }
    }

    private bool upper;
    public bool Upper
    {
        get { return upper; }
        set
        {
            upper = value;
            NotifyPropertyChanged("Upper");
            // Should I notify something else here or call a refresh method?
        }
    }

    private bool removeFirstLetter;
    public bool RemoveFirstLetter
    {
        get { return removeFirstLetter; }
        set
        {
            removeFirstLetter = value;
            NotifyPropertyChanged("RemoveFirstLetter");
            // Should I notify something else here or call a refresh method?
        }
    }

    // ...

然后我此时在同一个 ViewModel 类中创建了一个 Work 方法。稍后我会将这种方法转移到业务中。

private void Work()
{ 
    string result = _originalText;
    if (Upper)
        result = result.ToUpper();
    if (removeFirstLetter)
        result = result.Substring(1, result.Length);
    // if ...
    ModifiedText = result; 
}

我的问题是我应该在什么时候、在哪里调用工作方法?我应该在每个 setter 或 getter 中调用它吗?我不喜欢这个主意。我做错了什么……

谢谢。

【问题讨论】:

    标签: c# .net data-binding mvvm


    【解决方案1】:

    在您的特定情况下,您应该使用 INotifyPropertyChanged 接口创建一个布尔属性。现在将此属性绑定到您的“IsChecked”复选框属性。通过在 setter 中调用 Work() 方法,每次“勾选”复选框时,setter 都会触发。

    【讨论】:

      【解决方案2】:

      您的问题的答案很简单:使用Commands

      命令是 MVVM 实现绑定到 ViewModel 中方法的方式。命令的实现遵循一个非常标准的模式。您会在 Internet 上找到大量信息,这里只是一个简短的草图:

      在 ViewModel 中实现的命令必须是 ICommand 类型,并且每个命令都必须与代码中的方法一起使用,一个负责执行实际方法,另一个负责检查当前是否可以执行。

      这些方法必须分别命名为CanExecuteExecute。通常情况下,通过一个名为 DelegateCommand 的小型帮助类来促进使用多个命令,该类为前面提到的方法提供委托。

      直接使用这个类,不做任何修改:

      public class DelegateCommand<T> : ICommand {
      
          private Predicate<T> canExecute;
          private Action<T> execute;
      
          public event EventHandler CanExecuteChanged;
      
          public DelegateCommand (Predicate<T> canExecute, Action<T> execute) {
      
              this.canExecute = canExecute;
              this.execute = execute;
          }
      
          public bool CanExecute (object param) {
      
              return canExecute((T)param);
          }
      
          public void Execute (object param) {
      
      
              execute((T)param);
          }
      
          public void CanExecuteChangedRaised () {
      
              CanExecuteChanged(this, new EventArgs());
          }
      }
      

      那么你的 Command 声明是 DelegateCommand 类型而不是 ICommand 类型。看下面的例子来说明,你就会明白了:

      假设您的 ViewModel 中有一个方法 foo(),您希望通过单击按钮来调用它:

      class ViewModel {
      
          // ...
      
          public DelegateCommand<object> FooCommand { get; set; }
      
          public ViewModel () {
      
              FooCommand = new DelegateCommand<object>(CanExecuteFooCommand, ExecuteFooCommand);
          }
      
          public bool CanExecuteFooCommand (object param) {
      
              return true;
          }
      
          public void ExecuteFooCommand (object param) {
      
              foo();
          }
      
          // ...
      }
      

      假设您已通过 DataContext 属性将 ViewModel 设置为控件 DataContext,那么剩下要做的就是将 FooCommand 绑定到您的按钮,如下所示:

      就是这样!

      附录(参考评论):

      为了在不实际点击按钮的情况下执行某些操作,您只需使用 ViewModel 跟踪 UI 中的任何更改并做出相应反应 - 这就是 MVVM 的意义所在:跟踪来自 UI 的数据修改或处理它们并将它们填充回 UI。

      要对 TextBox 文本更改做出反应,请在 ViewModel 中创建相应的字符串属性,并跟踪来自视图的新离子值是否与当前文本框文本不同:

      private string _text;
      
      public string Text {
         get { return _text; }
         set {          
             // the text in the TextBox is about to change.
             if (!_text.Equals(value)) 
             {
              doSomething();
             }
      
             _text = value;
             FirePropertyChanged("Text");
         }
      }
      

      为了对 CheckBox 做同样的事情,您可以如上所述应用 ICommand,因为 CheckBox 是从 Button 派生的,因此提供了 Command 属性。

      【讨论】:

      • 感谢您的帮助。实际上我知道我必须在我的按钮上创建一个 ICommand 。我的按钮执行 Work() 和 Save() 结果。我的问题不是关于单击按钮,而是如何调用我的 Work() 方法来预览我的结果,而无需在每次用户检查或取消选中选项或修改原始文本时实时保存命中。
      猜你喜欢
      • 2017-10-26
      • 1970-01-01
      • 2017-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多