Data binding 根据ViewModel的属性在视图中的UserControls之间切换

Data binding 根据ViewModel的属性在视图中的UserControls之间切换,data-binding,mvvm,windows-runtime,windows-store-apps,Data Binding,Mvvm,Windows Runtime,Windows Store Apps,在Windows 8应用商店应用程序中,如何根据ViewModel的属性在视图中的用户控件之间进行更改 假设我的ViewModel具有如下属性: class MyViewModel { public string CurrentStatus { get { return (string)GetValue(CurrentStatusProperty); } set { SetValue(CurrentS

在Windows 8应用商店应用程序中,如何根据ViewModel的属性在视图中的用户控件之间进行更改

假设我的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));


        ...
     }
/// <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
}
如何使视图根据ViewModel中CurrentStatus的值更改UserControl

strightforward的解决方案是在ViewModel中的CurrentStatus和视图中的另一个字符串之间创建一个绑定,但显然,数据绑定只能用于字符串不是的DependencyObject

编辑: xaml文件只包含一个StackPanel,我想根据当前状态在其中放置一个UserControl。因此,如果CurrentStatus是一个,我希望StackPanel包含UserControlOne等等

对这个问题有什么好的解决办法吗


非常感谢

我不确定我是否完全理解您的意图。你能发布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
}
以上假设您在资源文件中定义了模板

如果您只想做一些简单的事情,如状态1的红色文本或状态2的绿色文本,您可以使用一个转换器,将状态转换为颜色,并将FontColor绑定到状态,然后使用转换器


但正如我所说,如果没有更多的代码发布,我就不能100%清楚地了解您想要实现的目标。你能发布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
}
以上假设您在资源文件中定义了模板

如果您只想做一些简单的事情,如状态1的红色文本或状态2的绿色文本,您可以使用一个转换器,将状态转换为颜色,并将FontColor绑定到状态,然后使用转换器

但正如我所说,如果没有更多的代码发布,我并不100%清楚您想要实现什么

我通常使用ContentControl并基于DataTrigger设置它的ContentTemplate

我在我的博客文章中有一个例子,但下面是一个使用您的场景的示例:

<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,它已被替换为。我还没有机会使用它,但据我所知,他们对WinRT使用了与Silverlight相同的方法,Silverlight在v5之前也不支持DataTriggers,我对Silverlight的解决方案是使用

我希望这能为您指明正确的方向:

我通常使用ContentControl,并基于DataTrigger设置它的ContentTemplate

我在我的博客文章中有一个例子,但下面是一个使用您的场景的示例:

<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,它已被替换为。我还没有机会使用它,但据我所知,他们对WinRT使用了与Silverlight相同的方法,Silverlight在v5之前也不支持DataTriggers,我对Silverlight的解决方案是使用

我希望这能为您指明正确的方向:

我以前在这种情况下使用过DataTemplateSelector和IValueConverter,现在我最喜欢的方法是使用VisualStateManager。我已经创建了一个最基本的附加属性,该属性在WinRT XAML Toolkit中实现了附加的行为模式,如下所示:

    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));


        ...
     }
/// <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
}
一旦你有了这些,你可以在你的视图模型中有一个属性,比如

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来定义该状态的外观-例如,更改内容或ContentTemplate,或者仅更改布局中各种元素的可见性。我认为这种方法对设计器工具的支持最好,因为您只需单击一个按钮就可以在视图之间切换,并以混合方式对这些视图进行更新。

虽然我以前在这种情况下使用过DataTemplateSelector和IValueConverter,但现在我最喜欢的方法是使用VisualStateManager。我已经创建了一个最基本的附加属性,该属性在WinRT XAML Toolkit中实现了附加的行为模式,如下所示:

    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));


        ...
     }
/// <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
}
一旦你有了这些,你可以在你的视图模型中有一个属性,比如

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来定义该状态的外观-例如,更改内容或ContentTemplate,或者仅更改布局中各种元素的可见性。我认为这种方法最好地支持设计器工具,因为您只需点击一个按钮,就可以在视图之间切换
在Blend中更新这些视图。

很抱歉,您的代码在Windows 8应用程序项目中不起作用。Visual Studio抱怨Windows应用程序项目不支持该类型。对于x:键入,如果我修复了它的问题。@alex_和_-ra我每天都会学到一些新东西:根据,DataTriggers被WinRTI中从未使用过的DataTriggers或VisualStateManager中的替换。。我在这方面有点生疏:我要试一试。谢谢@alex_和__-ra对不起,我帮不上什么忙了。我以前从未使用过Windows 8或WinRT。我想删除我的答案,但我认为最好还是留下来说明为什么这个常见的WPF解决方案不能与WinRT一起工作。从阅读中我可以看出,触发器通常在WinRT中使用VisualStateManager、代码隐藏或类似转换器完成。也许再看看他的答案,看看是否行得通很抱歉,您的代码在Windows 8应用程序项目中不起作用。Visual Studio抱怨Windows应用程序项目不支持该类型。对于x:键入,如果我修复了它的问题。@alex_和_-ra我每天都会学到一些新东西:根据,DataTriggers被WinRTI中从未使用过的DataTriggers或VisualStateManager中的替换。。我在这方面有点生疏:我要试一试。谢谢@alex_和__-ra对不起,我帮不上什么忙了。我以前从未使用过Windows 8或WinRT。我想删除我的答案,但我认为最好还是留下来说明为什么这个常见的WPF解决方案不能与WinRT一起工作。从阅读中我可以看出,触发器通常在WinRT中使用VisualStateManager、代码隐藏或类似转换器完成。也许再看看他的答案,看看是否行得通对于此行扩展:VisualStateExtensions.State={Binding State}>它表示在类型“VisualStateExtensions”中找不到可附加属性“State”。另外,谁应该是绑定中的“State”?我的答案中的VisualStateExtensions类具有State属性。只需确保将xmlns:extensions=using:WinRTXamlToolkit.Controls.extensions添加到XAML中的根XML元素中。抱歉,我错过了绑定中的State属性,该属性实际上应该称为VisualState,它来自我在第三个代码块中引用的视图模型属性。让我更新它。对于此行扩展:VisualStateExtensions.State={Binding State}>它说在类型“VisualStateExtensions”中找不到可附加属性“State”。另外,谁应该是绑定中的“State”?我的答案中的VisualStateExtensions类具有State属性。只需确保将xmlns:extensions=using:WinRTXamlToolkit.Controls.extensions添加到XAML中的根XML元素中。抱歉,我错过了绑定中的State属性,该属性实际上应该称为VisualState,它来自我在第三个代码块中引用的视图模型属性。让我更新一下。