我正在进行我的第一个WPF/MVVM项目,我很难把我的大脑集中在一些事情上。这个项目将是VB中已经制作的东西的独立应用程序,因此所有的逻辑和业务规则都已经存在。
我遇到的问题是试图实现绑定。我的应用程序上有几个按钮,它们的所有属性(IsEnabled、Text、image)都依赖于枚举。枚举基于状态,并且存在于模型中。
在VB应用程序中,我有一个巨大的switch语句来刷新按钮的属性,它在状态可能发生变化的每个地方都被调用。因此,对于这个版本,我的ViewModel中的每个按钮都有一个基于状态的bool和字符串,但无法强制它在每次状态更改时进行更新(这是非常频繁的)。
我读过一些关于INotifyPropertyChanged的内容,但需要在ViewModel中启动更改的属性在我的Model中。我是不是搞错了?
我会在您的按钮样式中使用DataTrigger
。无论何时更新绑定值,都会重新评估DataTrigger
,并在必要时设置新值
<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding MyEnumProperty}"
Value="{x:Static local:MyEnum.Value1}">
<Setter Property="IsEnabled" Value="False" />
<Setter Property="Content" Value="Value 1" />
</DataTrigger>
<DataTrigger Binding="{Binding MyEnumProperty}"
Value="{x:Static local:MyEnum.Value2}">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
您可以对Image
样式执行相同的操作-根据Status枚举的当前值在DataTrigger
中设置Image.Source
属性。
请记住,MyEnumProperty
在更改时需要引发PropertyChange通知,以便UI知道该值已更改,并更新依赖该值的任何绑定。
您需要将每个Button
的IsEnabled
、Text
和Image
属性绑定到视图模型的Status属性。然后,您将需要提供IValueConverter
的3个实现,它基本上将Status
枚举值转换为您要查找的适当布尔、字符串或图像。对于每个Button
控件,提供一个附加参数,转换器可以使用该参数与视图模型的Status属性进行比较。
类似这样的东西:
<myView.Resources>
<local:statToBoolConverter x:Key="statToBoolConv" />
<local:statToTextConverter x:Key="statToTextConv" />
<local:statToImgConverter x:Key="statToImgConv" />
</myView.Resources>
// ..... further down in code ....
<Button x:Key="aButton"
IsEnabled="{Binding Path=Status, Converter={StaticResource statToBoolConv},
ConverterParamter=caseA}"
Text="{Binding Path=Status, Converter={StaticResource statToTextConv},
ConverterParamter=caseA}"
Image="{Binding Path=Status, Converter={StaticResource statToImgConv},
ConverterParamter=caseA}"/>
<Button x:Key="aButton"
IsEnabled="{Binding Path=Status, Converter={StaticResource statToBoolConv},
ConverterParamter=caseB}"
Text="{Binding Path=Status, Converter={StaticResource statToTextConv},
ConverterParamter=caseB}"
Image="{Binding Path=Status, Converter={StaticResource statToImgConv},
ConverterParamter=caseB}"/>
我不打算包括实现IValueConverter
的细节,因为它非常简单,但您可以在这里获得更多信息(包括示例):
http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx
还有一点需要注意:INotifyPropertyChanged
将触发所有按钮的绑定更新,所以只调用一个NotifyPropertyChanged("MyStatus")
就可以了。
我曾试图在WPF中处理到enum的绑定,但我并不喜欢它。
您可以通过将属性映射到模型中的相应属性来解决此问题,在get方法中,您可以实现对枚举状态的依赖关系。
例如:
<Button Height="41" HorizontalAlignment="Center" Style="{StaticResource ButtonStyle}"
Margin="407,77,289,0" Name="buttonSearch" VerticalAlignment="Top" Width="137" Click="search_click"
IsEnabled="{Binding IsSearchButtonEnabled}" ...
假设你有这个枚举:
public enum States
{
StateOne,
StateTwo,
StateThree
}
在视图模型中,您可以这样做:
public bool IsSearchButtonEnabled
{
get
{
return ((Model.actualState) == States.StateTwo);
}
}
若要自动更新,ViewModel必须实现INotifyPropertyChanged。我使用ViewModels总是子类化的通用实现来简化事情。它看起来像这样,并且应该在每次调用InvokePropertyChanged(字符串propertyName)时处理视图的更新。ViewModel需要知道它应该更新视图,并知道何时调用此方法。您可以在Model中使用相同的技术,并在Model中放置一个事件处理程序,通知订阅虚拟机状态枚举的setter中的更改。
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public static event PropertyChangedEventHandler PropertyChangedStatic;
public void InvokePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public static void InvokePropertyChangedStatic(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChangedStatic;
if (handler != null) handler(null, new PropertyChangedEventArgs(propertyName));
}
}
在MVVM中,您不应该直接从ViewModel引用视图中的任何内容。
最好的解决方案是使用命令,如MVVM Light RelayCommand,它具有基于枚举属性值的CanExecute操作。然后将按钮的命令属性bind绑定到ViewModel中相应的命令属性。这将自动设置按钮的启用状态。
然后在枚举属性的set方法中,为每个命令调用CanExecuteChanged事件。