我正在开发我的第一个WPF / MVVM项目,而且我很难将我的大脑包围在一些事情上。这个项目将是一个已经在VB中制作的独立应用程序,因此所有逻辑和业务规则都已存在。
我遇到的问题是尝试实现绑定。我的应用程序上有几个按钮,它们的所有属性(IsEnabled,Text,image)都依赖于枚举。枚举基于状态并存在于模型中。
在VB应用程序中,我有一个巨大的switch语句来刷新按钮的属性,并且在状态可能改变的每个地方都调用它。所以对于这个版本,我根据状态为我的ViewModel中的每个按钮设置了一个bool和字符串,但是每次状态改变时都没有强制它更新(这很常见)。
我已经阅读了一些关于INotifyPropertyChanged的内容,但需要启动我的ViewModel中的更改的属性在我的模型中。我是以错误的方式解决这个问题吗?
答案 0 :(得分:3)
我会在你的Button风格中使用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
样式执行相同操作 - 根据状态枚举的当前值,在Image.Source
中设置DataTrigger
属性。
请记住,MyEnumProperty
需要在更改时提出PropertyChange通知,以便UI知道值已更改并更新依赖于该值的任何绑定。
答案 1 :(得分:1)
我试图在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
}
在viewmodel中,您可以执行以下操作:
public bool IsSearchButtonEnabled
{
get
{
return ((Model.actualState) == States.StateTwo);
}
}
要自动更新,ViewModel必须实现INotifyPropertyChanged。我使用一般实现,我的ViewModel总是子类化以简化事情。看起来像这样,每次调用InvokePropertyChanged(string propertyName)时都应该处理更新视图。 ViewModel需要知道它应该更新视图,它知道何时调用此方法。您可以在模型中使用相同的技术,并在模型中放置一个事件处理程序,通知订阅者VM状态枚举的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));
}
}
答案 2 :(得分:1)
在MVVM中,您实际上不应该直接从ViewModel引用View中的任何内容。
最佳解决方案是使用命令,例如MVVM-Light RelayCommand,它具有基于枚举属性值的CanExecute操作。然后,将按钮的命令属性bind绑定到ViewModel中的相应命令属性。这将自动设置按钮的启用状态。
然后在enum属性的set方法中,为每个命令调用CanExecuteChanged事件。
答案 3 :(得分:1)
您需要将每个IsEnabled
的{{1}},Text
和Image
属性绑定到viewmodel的Status属性。然后,您需要提供Button
的3个实现,它们实际上将IValueConverter
枚举值转换为您要查找的相应bool,字符串或图像。对于每个Status
控件,请提供转换器可用于与viewmodel的Status属性进行比较的其他参数。
这样的事情:
Button
我不打算包含实现<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}"/>
的细节,因为它非常简单,但是你可以在这里获得更多信息(完整的例子):
http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx
还有一个注意事项:IValueConverter
将触发所有按钮的绑定更新,因此进行单个INotifyPropertyChanged
调用就可以了。