MVVM:实现 ViewModel 的类不更新其 Model 实例
因此,我一直在尝试在一个简单的 WPF
应用程序中实现 MVVM
模式,该应用程序具有以下结构:
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.";
}
}
VIEW MODEL ( BASE)
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);
};
}
命令
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>
这种方法的问题是,尽管 ViewModel 与
View
绑定正常,Model
没有反映此类更改(意味着 Model
不通知返回< /em> 在 ViewModel
处更改其实例)
我非常感谢有关这篇文章的任何建议,提前非常感谢你们。
编辑
- 使用缺失的代码更新了片段(感谢 Pavlo 和 Ben)
- 向公共 svn 存储库提交了解决方案http://nanotaboada.svn.beanstalkapp.com/dotnet/trunk/Dotnet.Samples.Rijndael/ 适合任何有兴趣查看整个项目的人。
- 修改了
Model
和ViewModel
方法,添加了ICommand
实现。如需完整的工作示例,请查看修订版 16。
So I've been trying to implement the MVVM
pattern within a simple WPF
application that has the following structure:
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.";
}
}
VIEW MODEL (BASE)
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));
}
}
}
VIEW MODEL (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();
}
}
VIEW
<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>
The problem with this approach is, despite that the ViewModel
is binding okay with the View
, the Model
is not reflecting such changes (meaning the Model
is not notifying-back changes to its instance at the ViewModel
)
I would really appreciate any bit of advice regarding this post, thanks much you guys in advance.
EDIT
- Updated snippets with the missing code (thanks Pavlo and Ben)
- Committed solution to a public svn repo http://nanotaboada.svn.beanstalkapp.com/dotnet/trunk/Dotnet.Samples.Rijndael/ for anyone interested in checking out the whole project.
- Modified
Model
andViewModel
methods, addedICommand
implementation. For a full working sample please checkout revision 16.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
除了一个小但重要的细节之外,一切看起来都不错。看起来您忘记将视图的 DataContext 设置为视图模型的实例。
如果没有它,您的绑定将会失败(在调试器下查看 Visual Studio 的输出窗口,您将看到绑定错误)。
另请注意,当
TextBox
失去焦点时,视图模型和模型中的值将会更新。要在您在绑定上键入 setUpdateSourceTrigger
toPropertyChanged
时使其更新:Everything looks OK except one small, but important detail. It looks like you forgot to set
DataContext
of your view to the instance of the view model.Without it your bindings will fail (look in the output window of Visual Studio when under debugger and you'll see binding errors).
Also note that the values will be updated in your view model and model when the
TextBox
looses focus. To make it update while you type setUpdateSourceTrigger
toPropertyChanged
on your bindings:在您的 FooBarViewModel 中,您没有实例化您的模型,它保留为 null,因为您将其标记为只读,所以您需要在默认构造函数中new它。
In your FooBarViewModel you are not instantiating your Model, it is left as null, since you marked it readonly, you will need to new it in a default constructor.