根据ViewModel的属性在View中切换UserControl

时间:2013-02-28 15:09:04

标签: data-binding mvvm windows-runtime windows-store-apps

如何在Windows 8商店应用中基于ViewModel的属性在View中的UserControl之间进行更改?

假设我的ViewModel有一个看起来像这样的属性:

    class MyViewModel
    {
        public string CurrentStatus
        {
            get { return (string)GetValue(CurrentStatusProperty); }
            set { SetValue(CurrentStatusProperty, value); }
        }

        public static readonly DependencyProperty CurrentStatusProperty =
            DependencyProperty.Register("CurrentStatus", typeof(string), typeof(MyViewModel), new PropertyMetadata("default", CurrentStatusChanged));


        ...
     }

如何根据ViewModel中CurrentStatus的值让我的View更改UserControl?

对我来说,直截了当的解决方案是在ViewModel中的CurrentStatus和View中的另一个字符串之间创建绑定,但显然数据绑定只能用于DependencyObject(其中一个字符串不是。)

修改: xaml文件只包含StackPanel,其中我想根据CurrentStatus放置UserControl。因此,如果CurrentStatus"one",我希望StackPanel包含UserControlOne,依此类推......

这个问题的任何想法或好的解决方案?

非常感谢!

3 个答案:

答案 0 :(得分:2)

我通常使用ContentControl并根据ContentTemplate

设置DataTrigger

我在我的博文Switching between Views/UserControls using MVVM中有一个例子,但这里有一个使用你的场景可能看起来的样本:

<DataTemplate x:Key="DefaultTemplate">
     <local:DefaultUserControl />
</DataTemplate> 

<DataTemplate x:Key="ClosedTemplate">
     <local:ClosedUserControl />
 </DataTemplate>

<Style x:Key="MyContentControlStyle" TargetType="{x:Type ContentControl}">
    <Setter Property="ContentTemplate" Value="{StaticResource DefaultTemplate}" />
    <Style.Triggers>
        <DataTrigger Binding="{Binding CurrentStatus}" Value="Closed">
            <Setter Property="ContentTemplate" Value="{StaticResource ClosedTemplate}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

...

<ContentControl Style="{StaticResource MyContentControlStyle}" />

修改

显然,WinRT不支持DataTriggers,并且已被VisualStateManager替换。我还没有机会使用它,但是从我正在阅读的内容来看,他们使用与WinRT相同的方法,就像Silverlight一样(在v5之前也不支持DataTriggers),以及我的解决方案Silverlight使用DataTemplateSelector

我希望能指出你正确的方向:)

答案 1 :(得分:1)

我不确定我完全明白你要做什么。你可以发布xaml吗?

如果您想要的是根据状态不同地显示控件,请使用转换器,您可以根据状态显示不同的模板:

public class StatusToTemplateConverter : IValueConverter
{
    #region IValueConverter Members


    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        switch ((string) value)
        {
            case "Status1":
                return Application.Current.Resources["Status1Template"];
            case "Status2":
                return Application.Current.Resources["Status2Template"];

            default:
                return Application.Current.Resources["Status3Template"];
        }
    }


    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }

    #endregion
}

以上假设您在资源文件中定义了模板

如果您想要做的事情更简单,例如Status1的红色文本或status2的绿色文本,您可以使用转换器将状态转换为颜色并将FontColor绑定到状态并使用转换器。 / p>

但就像我说的那样,如果没有更多的代码发布,我不会100%明白你想要实现的目标

答案 2 :(得分:1)

虽然我之前在这种情况下使用了DataTemplateSelectorIValueConverter,但现在我最喜欢的方法是使用VisualStateManager。我创建了一个最基本的附加属性,它在WinRT XAML Toolkit here中实现附加的行为模式 - 看起来像这样:

/// <summary>
/// Defines an attached property that controls the visual state of the element based on the value.
/// </summary>
public static class VisualStateExtensions
{
    #region State
    /// <summary>
    /// State Attached Dependency Property
    /// </summary>
    public static readonly DependencyProperty StateProperty =
        DependencyProperty.RegisterAttached(
            "State",
            typeof(string),
            typeof(VisualStateExtensions),
            new PropertyMetadata(null, OnStateChanged));

    /// <summary>
    /// Gets the State property. This dependency property 
    /// indicates the VisualState that the associated control should be set to.
    /// </summary>
    public static string GetState(DependencyObject d)
    {
        return (string)d.GetValue(StateProperty);
    }

    /// <summary>
    /// Sets the State property. This dependency property 
    /// indicates the VisualState that the associated control should be set to.
    /// </summary>
    public static void SetState(DependencyObject d, string value)
    {
        d.SetValue(StateProperty, value);
    }

    /// <summary>
    /// Handles changes to the State property.
    /// </summary>
    /// <param name="d">
    /// The <see cref="DependencyObject"/> on which
    /// the property has changed value.
    /// </param>
    /// <param name="e">
    /// Event data that is issued by any event that
    /// tracks changes to the effective value of this property.
    /// </param>
    private static void OnStateChanged(
        DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var stateName = (string)e.NewValue;
        var ctrl = (Control)d;
        VisualStateManager.GoToState(ctrl, stateName, true);
    }
    #endregion
}

您应该定义一个枚举样式的类,例如Silverlight Toolkit中的VisualStates类,它列出了所有的可视状态(因此您没有重复项),例如。

internal static class VisualStates
{
    #region GroupCommon
    /// <summary>
    /// Common state group.
    /// </summary>
    public const string GroupCommon = "CommonStates";

    /// <summary>
    /// Normal state of the Common state group.
    /// </summary>
    public const string StateNormal = "Normal";

    /// <summary>
    /// Normal state of the Common state group.
    /// </summary>
    public const string StateReadOnly = "ReadOnly";

    /// <summary>
    /// MouseOver state of the Common state group.
    /// </summary>
    public const string StateMouseOver = "MouseOver";

    /// <summary>
    /// Pressed state of the Common state group.
    /// </summary>
    public const string StatePressed = "Pressed";

    /// <summary>
    /// Disabled state of the Common state group.
    /// </summary>
    public const string StateDisabled = "Disabled";
    #endregion GroupCommon

    #region GroupFocus
    /// <summary>
    /// Focus state group.
    /// </summary>
    public const string GroupFocus = "FocusStates";

    /// <summary>
    /// Unfocused state of the Focus state group.
    /// </summary>
    public const string StateUnfocused = "Unfocused";

    /// <summary>
    /// Focused state of the Focus state group.
    /// </summary>
    public const string StateFocused = "Focused";
    #endregion GroupFocus
}

一旦有了这些 - 您可以在视图模型中拥有属性,如

public string VisualState { get; set; /* Raise PropertyChange event your preferred way here */ }

在你看来说

<Page
    ...
    xmlns:extensions="using:WinRTXamlToolkit.Controls.Extensions"
    extensions:VisualStateExtensions.State="{Binding VisualState}">...

然后您的视图模型可以轻松驱动视觉状态的更改,您可以使用Blend来定义该状态的样子 - 例如更改ContentContentTemplate或仅更改布局中各种元素的可见性。我认为这种方法对设计器工具有最好的支持,因为只需单击按钮就可以在视图之间切换,并在Blend中对这些视图进行更新。