如何在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,依此类推......
这个问题的任何想法或好的解决方案?
非常感谢!
答案 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)
虽然我之前在这种情况下使用了DataTemplateSelector
和IValueConverter
,但现在我最喜欢的方法是使用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来定义该状态的样子 - 例如更改Content
或ContentTemplate
或仅更改布局中各种元素的可见性。我认为这种方法对设计器工具有最好的支持,因为只需单击按钮就可以在视图之间切换,并在Blend中对这些视图进行更新。