MVVM:实现ViewModel而不更新其Model实例的类

时间:2011-03-22 20:18:22

标签: c# wpf mvvm inotifypropertychanged

所以我一直在尝试在具有以下结构的简单MVVM应用程序中实现WPF模式:

MODEL

public class Foobar
{
    public string Foo { get; set; }
    public string Bar { get; set; }

    public string DoSomethingWithFoo()
    {
        return "The quick brown fox";
    }

    public string DoSomethingWithBar()
    {
        return "jumps over the lazy dog.";
    }
}

查看模型(基础)

public abstract class ViewModel : INotifyPropertyChanged
{
    [Conditional("DEBUG")]
    [DebuggerStepThrough]
    public void VerifyPropertyName(string propertyName)
    {
        if (TypeDescriptor.GetProperties(this)[propertyName] == null)
        {
            Debug.Fail("Invalid property name: " + propertyName);
        }
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        this.VerifyPropertyName(propertyName);

        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

查看模型(IMPL)

public class FoobarViewModel : ViewModel
{
    private readonly Foobar foobar;

    public string Foo
    {
        get
        {
            return this.foobar.Foo;
        }
        set
        {
            this.foobar.Foo = value;
            OnPropertyChanged("Foo");
        }
    }

    public string Bar
    {
        get
        {
            return this.foobar.Bar;
        }
        set
        {
            this.foobar.Bar = value;
            OnPropertyChanged("Bar");
        }
    }

    private FoobarCommand fooCommand;
    public FoobarCommand FooCommand
    {
        get
        {
            return fooCommand;
        }
        set
        { 
            fooCommand = value;
            OnPropertyChanged("FooCommand");
        }
    }

    private FoobarCommand barCommand;
    public FoobarCommand BarCommand
    {
        get
        {
            return barCommand;
        }
        set
        { 
            barCommand = value;
            OnPropertyChanged("BarCommand");
        }
    }

    private void DoSomethingWithFoo()
    {
        if (!string.IsNullOrEmpty(this.foobar.Foo))
        {
            this.foobar.Foo = this.foobar.DoSomethingWithFoo();
            OnPropertyChanged("Foo");
        }
    }

    private void DoSomethingWithBar()
    {
        if (!string.IsNullOrEmpty(this.foobar.Bar))
        {
            this.foobar.Bar = this.foobar.DoSomethingWithBar();
            OnPropertyChanged("Bar");
        }
    }

    ///<remarks>
    /// must use the parameterless constructor to satisfy <Window.Resources>
    ///</remarks>
    public FoobarViewModel()
    {
        this.foobar = new Foobar()
        {
            Foo = "Lorem",
            Bar = "Ipsum"
        }

        this.fooCommand = new FoobarCommand(DoSomethingWithFoo);
        this.barCommand = new FoobarCommand(DoSomethingWithBar);
    };
}

COMMAND

public class FoobarCommand : ICommand
{
    Action action;

    public FoobarCommand(Action action)
    {
        this.action = action;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        this.action.Invoke();
    }
}

查看

<Window.Resources>
    <local:FoobarViewModel x:Key="FoobarViewModel" />
</Window.Resources>

<Grid DataContext="{StaticResource FoobarViewModel}">

    <TextBox Name="FooTextBox" Text="{Binding Foo, Mode=TwoWay, ValidatesOnDataErrors=True}" />
    <TextBox Name="BarTextBox" Text="{Binding Bar, Mode=TwoWay, ValidatesOnDataErrors=True}" />

</Grid>

这种方法的问题是,尽管ViewModelView绑定正常,但Model并未反映此类更改(意味着Model不是通知更改其ViewModel上的实例

我非常感谢有关这篇文章的任何建议,非常感谢你们。

修改

  1. 更新了代码片段(感谢Pavlo和Ben)
  2. 为有兴趣查看整个项目的人提供公共svn回购http://nanotaboada.svn.beanstalkapp.com/dotnet/trunk/Dotnet.Samples.Rijndael/的解决方案。
  3. 修改了ModelViewModel方法,添加了ICommand实施。如需完整的工作样本,请查看修订版16。

2 个答案:

答案 0 :(得分:5)

除了一个小而重要的细节之外,一切看起来还不错。您似乎忘记将视图的DataContext设置为视图模型的实例。

<Window ...
        DataContext="{StaticResource FoobarViewModel}">

没有它,您的绑定将失败(在调试器下查看Visual Studio的输出窗口,您将看到绑定错误)。

另请注意,当TextBox失去焦点时,值将在视图模型和模型中更新。要在绑定时将UpdateSourceTrigger设置为PropertyChanged,请对其进行更新:

<TextBox Name="FooTextBox" Text="{Binding Foo, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />

答案 1 :(得分:1)

在你的FooBarViewModel中,你没有实例化你的模型,它被保留为null,因为你只读了它,你需要在默认的构造函数中 new