如何使用数据绑定调用通用方法?

时间:2012-07-17 15:56:22

标签: c# .net data-binding mvvm

我想了解在处理许多属性时如何正确使用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 TextBox和一个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; 
}

我的问题是何时,我应该在哪里调用工作方法?我应该在每个安装者或吸气器中给它打电话吗?我不喜欢这个主意。我做错了什么......

谢谢。

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

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

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

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

这些方法必须分别命名为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();
    }

    // ...
}

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

就是这样!

附录(参见评论):

为了在没有实际按下Button的情况下执行某些操作,您只需要使用ViewModel跟踪UI中的任何更改并做出相应的反应 - 这就是MVVM的用途:跟踪UI中的数据修改或处理它们并将它们填充回用户界面。

要对TextBox文本更改做出反应,请在ViewModel中创建相应的字符串属性,并跟踪View中新的ioncoming值是否与当前textBox文本不同:

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属性。