使用MVVM在Silverlight中处理视图状态

使用MVVM在Silverlight中处理视图状态,silverlight,mvvm,Silverlight,Mvvm,我很想知道你们这些人是如何使用MVVM模式处理Silverlight应用程序中的视图状态的。 假设我有一个简单的搜索掩码,可以异步调用Web服务。 在搜索过程中,我想相应地更改gui: -禁用搜索按钮 -启用取消按钮 -等 使用wpf,我可以创建一个datatrigger,它绑定到viewmodel中的某些属性,然后对gui进行更改。 既然我在Silverlight中没有datatrigger,那么与datatrigger类似的最明智的实现方法是什么(如果可能的话,在一个地方使用简洁的代码) (

我很想知道你们这些人是如何使用MVVM模式处理Silverlight应用程序中的视图状态的。 假设我有一个简单的搜索掩码,可以异步调用Web服务。 在搜索过程中,我想相应地更改gui: -禁用搜索按钮 -启用取消按钮 -等

使用wpf,我可以创建一个datatrigger,它绑定到viewmodel中的某些属性,然后对gui进行更改。 既然我在Silverlight中没有datatrigger,那么与datatrigger类似的最明智的实现方法是什么(如果可能的话,在一个地方使用简洁的代码)


()

我的标准方法是从视图模型(通常是枚举)中公开“ViewState”属性。然后,视图绑定到属性,并使用visualstatemanager根据枚举切换到适当的可视状态

来自的DataStateSwitchBehavior是如何切换到可视状态的一个很好的示例

编辑以回应评论

首先,在处理VisualState时使用Blend(不应该强迫任何人手工编写那么多XAML)。我相信它甚至出现在所有(大多数?)MSDN订阅上

使用可视状态始于


您通常会将其添加到layoutroot

可视状态管理器由一组状态组组成,而状态组又由一组可视状态组成

这些组将相互排斥的状态组织起来,因为您可以在任何时候激活多个视觉状态,但每个组中只能有一个状态。标准模式是使用一个称为“正常”或“默认”的空状态来关闭其他状态。基本上是一个基态


在您的情况下,您将有一个“搜索”视觉状态,其中将包含一个故事板,该故事板将禁用各种按钮,激活繁忙的动画等。

最简便的方法是使用Silverlight工具箱中的
总线指示器。当它遮罩应用它的整个区域时,所有按钮都会自动禁用

对于取消按钮,您必须编辑
BusyIndicator
的模板,将其直接放置在加载动画旁边


然后,您只需将BusyIndicator的
IsBusy
属性绑定到ViewModel中相应的属性,该属性在加载之前设置,并在加载完成后重置。

我的解决方案与Graeme Bradbury的类似,但我不使用DataStateSwitchBehavior,因为如果我的X控件放在选项卡面板(或类似的东西)中当我在另一个选项卡上时,状态会发生变化,然后我会得到一个异常(“元素”未找到..)。引发异常是因为当我在另一个选项卡上时,我的X控件被卸载,并且找不到需要更新的元素

下面是我的工作:

在我的视图模型中,我有一个属性VisualState,它在状态更改时发送通知消息(我使用):

在我的视图代码中,我订阅了一个通知:

public partial class XControl : UserControl
{
    private string visualState = XVisualStates.InitialState;
    public XControl()
    {
        InitializeComponent();

        //go to state when view is loaded
        Loaded += (s, e) => ChangeState(); //every time a view is loaded go to current state

        //change visual state when a notification is received
        Messenger.Default.Register<XStateChangedMessage>(this,
            state =>
            {
                visualState = state.CurrentState; //save current state
                ChangeState();
            });
    }

    void ChangeState()
    {
        try
        {
            VisualStateManager.GoToState(this, visualState, true); //will throw an exception if current view is unloaded
        }
        catch
        {
            //NOTE: supress 'element' not found errors if user navigated to another view and state changes
        }
    }
}
1) 您可以在视图模型中创建类似IsEnabledSearch的属性,并将其绑定到按钮的IsEnabled或Visibility属性(您将需要一个布尔到可见性转换器)。仅仅为此创建新的视觉状态不是很有效,因为您的按钮内部已经有各种视觉状态来支持此行为


2) Jounce mvvm框架有一个非常好的实现来支持ViewModel中的可视化状态

那么,我可以在控件(也在childwindow?)上定义视觉状态,然后使用DataStateSwitchBehavior切换它们吗?例如,一个状态可以是“SearchInProgress”。然后如何使用VSM禁用按钮,使其在控件处于“SearchInProgress”时不可单击?谢谢Graeme。我在一个样本项目中得到了这个结果,看起来这是一条可行的道路。然而,我似乎遇到了这里提到的相同问题:。VSM在子窗口中似乎不起作用。现在,我将把所有内容都放在UserControl中,并在子窗口中引用它。我正在使用您提到的DataStateSwitcher使其成为ViewModel驱动的。
private string visualState = XVisualStates.InitialState;
    public string VisualState
    {
        get
        {
            return visualState;
        }

        set
        {
            visualState = value;
            Messenger.Default.Send(new XStateChangedMessage(value));
        }
    }
public partial class XControl : UserControl
{
    private string visualState = XVisualStates.InitialState;
    public XControl()
    {
        InitializeComponent();

        //go to state when view is loaded
        Loaded += (s, e) => ChangeState(); //every time a view is loaded go to current state

        //change visual state when a notification is received
        Messenger.Default.Register<XStateChangedMessage>(this,
            state =>
            {
                visualState = state.CurrentState; //save current state
                ChangeState();
            });
    }

    void ChangeState()
    {
        try
        {
            VisualStateManager.GoToState(this, visualState, true); //will throw an exception if current view is unloaded
        }
        catch
        {
            //NOTE: supress 'element' not found errors if user navigated to another view and state changes
        }
    }
}
public class XStateChangedMessage 
{
    public string CurrentState { get; private set; }

    public XStateChangedMessage (string currentState)
    {
        CurrentState = currentState;
    }
}