如何实现MVVM自定义控件?

问题描述:

我有费用屏幕,其中包含一个文本框,我可以把费用的价格。正如我为这个文本框建立了很多逻辑(验证等),我想将它提取到一个单独的控件并在其他屏幕上重新使用它。我试图用mvvm风格来构建它。下面是我对现在:如何实现MVVM自定义控件?

ExpenseView

<page> 
    <Label Text={Binding Date} /> 
    <wpfControls:CurrencyTextBoxView ViewModel="{Binding PriceViewModel}" /> 
</page> 

ExpenseViewModel

public class ExpenseViewModel : INotifyPropertyChanged 
{ 
    private ExpenseModel Model { get; } 

    public string Date 
    { 
    get { return Model.Date.ToString(); } 
    set 
    { 
     Model.Date = DateTime.Parse(value); 
     RaisePropertyChanged(); 
    } 
    } 

    private CurrencyTextBoxViewModel _priceViewModel; 
    public CurrencyTextBoxViewModel PriceViewModel 
    { 
    get { return _priceViewModel; } 
    set { _priceViewModel = value; } 
    } 
} 

ExpenseModel

public class ExpenseModel 
{ 
    public DateTime Date { get; set; } 
    public decimal Price { get; set; } // This is the bit I don't know how to implement correctly 
} 

CurrencyTextBoxView

<control> 
    <TextBox Content={Binding Price} /> 
</control> 

CurrencyTextBoxViewModel

public class CurrencyTextBoxViewModel : INotifyPropertyChanged 
{ 
    private CurrencyModel Model { get; } 

    public string Price 
    { 
    get { return Model.Price.ToString(); } 
    set 
    { 
     Model.Price = decimal.Parse(value); 
     RaisePropertyChanged(); 
    } 
    } 
} 

CurrencyModel

public class CurrencyModel 
{ 
    public decimal Price { get; set; } 
} 

而且现在的proble m是:我需要在我的ExpenseModel中具有Price属性(因为它保存在数据库中)。我不想在我的CurrencyModel中使用Date属性(因为并非总是有意义)。

我应该将CurrencyModel放在我的ExpenseModel中吗?当货币文本框中的文本会更改时,如何有效更新它? 此外,ExpenseModel位于与其余类不同的项目中,我不希望使用CurrencyModel将此项目依赖于项目。

如果CurrencyTextBoxViewModel.Price字符串发生更改,我应该从CurrencyTextBoxViewModel监听PropertyChanged事件并更新ExpenseModel.Price吗?我喜欢我的视图模型充当视图模型的解析器(Date属性示例)。有什么方法可以实现PriceViewModel,以便它的getter从ExpenseModel直接返回数据(因此它充当解析器)?

我知道有很多方法来实现它,但我正在寻找最mvvm一致的。另外,我不确定我是否正确实施了整个模式?

+0

你做错了。您的自定义控件应该像其他任何控件一样。告诉我 - 有没有TextBoxViewModel?那么GridViewModel呢? RichTextBox是否带有RichTextBoxViewModel? **否。**不要陷入你认为MVVM ==没有隐藏代码的陷阱。如果您有与自定义控件关联的UI逻辑,请将其置于代码隐藏中。您的自定义控件是否需要X,Y和Z来完成它的工作?在控件的表面添加X,Y和Z的属性。将这些绑定到您的ViewModels。这很简单,效果很好。 – Will

至于Date属性,如果它不需要序列化,只将它放在视图模型上。

我该怎么办: 如果你想重新使用你的CurrencyTextBox,我不会把控制与模型联系起来。没有CurrencyModel是必要的。在CurrencyTextBox上准备DependencyProperty(比如CurrencyText)。视图去为:

<xxx:CurrencyTextBox CurrencyText="{Binding Currency}" /> 

可以省略CurrencyTextBoxViewModel因为CurrencyText可以绑定到内部TextBox.Text(或者,只是更新视图模型相关的属性)。无论如何,通过双向绑定,当更新CurrencyText时,ExpenseViewModel.Currency也会更新,您可以在此更新模型。

我认为可以听取模型的事件,因为视图模型比模型短。但是,如果新值与旧值相同,则应阻止传播事件触发。

该模型充当数据的来源并执行业务逻辑。视图模型只是模型的阴影,因此通常很薄。考虑使用一些MVVM框架来了解这一点。

+0

我不知道为什么没有想到我的CurrencyTextBoxView具有除ViewModel以外的依赖属性。 –

+0

关于我的自定义控件的模型和视图模型 - 这是我的爱好项目。在工作中,我们并没有真正做好mvvm。经过一段时间后,它总会变得混乱。因此,即使我知道在这种情况下使用模型和视图模型只会让事情变得更加复杂,但我更愿意坚持下去。 不久,我应该有更多的时间来处理它,所以我会发表我的设法做的评论。 –