1

我想了解当我们使用许多属性时如何正确使用 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 中调用它吗?我不喜欢这个主意。我做错了什么...

谢谢你。

4

2 回答 2

1

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

于 2012-07-18T13:54:56.113 回答
0

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

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

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

这些方法必须CanExecute分别命名Execute。通常情况下,通过一个名为的小帮助类来促进多个命令的使用,该类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 声明是 typeDelegateCommand而不是 type 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通过其DataContext属性将 ViewModel 设置为控件,那么剩下要做的就是将 绑定FooCommand到您的按钮,如下所示:

就是这样!

附录(参考评论):

为了在不实际点击按钮的情况下执行某些操作,您只需使用 ViewModel 跟踪 UI 中的任何更改并做出相应反应 - 这就是 MVVM 的意义所在:跟踪来自 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 属性。

于 2012-07-17T16:11:38.950 回答