使用MVVM模式处理Silverlight中的视图状态

7
我很想知道在MVVM模式下,Silverlight应用程序如何处理视图状态。假设我有一个简单的搜索掩码,它异步调用Web服务。在搜索正在进行时,我想相应地更改GUI: - 禁用搜索按钮 - 启用取消按钮 - 等等。
在使用WPF时,我可以创建一个DataTrigger来绑定到ViewModel中的某个属性,然后对GUI进行更改。现在由于Silverlight中没有DataTrigger,最合适的方法是类似于DataTrigger(代码整洁,尽可能在一个地方)实现这一点是什么?(我发布了一个类似的问题,但表述不清
4个回答

7
我的标准做法是在视图模型中公开一个“ViewState”属性(通常是枚举类型)。然后视图绑定到该属性,并使用VisualStateManager根据枚举值切换到适当的视觉状态。
来自Expression Samples的DataStateSwitchBehavior是如何切换到视觉状态的好例子。 编辑:回应评论
首先,处理VisualStates时请使用Blend(没有人应该被迫手写那么多XAML)。我相信它甚至在所有(或大部分)MSDN订阅中都可以使用。
使用Visual States从Visual State Manager开始。
<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="GroupOne">
        <VisualState x:Name="Normal"/>
        <VisualState x:Name="Searching"/>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

你通常会将此添加到layoutroot中。
视觉状态管理器由StateGroups集合组成,这些集合又包含VisualStates集合。
这些组织互斥状态的组,因为你可以在任何时间有多个视觉状态处于活动状态,但每个组只能有一个状态。标准模式是拥有一个名为“Normal”或“Default”的空状态,用于关闭其他状态。基本状态基本上就是这样。
在你的情况下,你将拥有一个名为“Searching”的视觉状态,其中包含一个故事板,可以禁用各种按钮,激活繁忙动画等。

那么我可以在我的控件上(也可以是子窗口上)定义可视化状态,然后使用DataStateSwitchBehavior切换它们吗? 例如,一个状态可以是“正在搜索”。那么我如何使用VSM禁用按钮,使其在控件处于“SearchInProgress”状态时不可点击? - Manuel R.
谢谢Graeme。我在一个示例项目中让它工作了,看起来这是正确的方法。然而,我好像遇到了这里提到的同样问题:http://stackoverflow.com/questions/2118814/how-can-i-use-visualstates-in-a-childwindow。VSM似乎不能在子窗口中使用。现在我将把所有内容放在UserControl中,并在子窗口中引用它。我正在使用您提到的DataStateSwitcher使其驱动ViewModel。 - Manuel R.

0

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

所以这是我做的:

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

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
        }
    }
}

而XStateChangedMessage是一个简单的类:

public class XStateChangedMessage 
{
    public string CurrentState { get; private set; }

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

0

我认为最方便的方法是使用Silverlight Toolkit中的BusyIndicator。因为它可以遮盖您应用它的整个区域,所有按钮都会自动禁用。

如果需要取消按钮,您需要编辑BusyIndicator的模板,将其直接放置在加载动画旁边。

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


0

1) 你可以在视图模型中创建像IsEnabledSearch属性的东西,然后将其绑定到Button的IsEnabled或Visibility属性(你需要一个Bool to Visibility Converter)。仅为此创建新的可视状态不是很有效,因为你的按钮已经包含了各种支持这种行为的可视状态。

2) Jounce mvvm框架有一个非常好的实现来支持从ViewModel中获取VisualStates; Jounce Visual State Manager


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接