问题不在于如何让这些东西运转起来,它已经做到了;这是关于我正在经历的一些奇怪的行为,我需要理解它。我有ResourceDictionary
包含一些样式,其中一个得到TargetType="{x:Type UserControl}"
和x:Key="UCStyle"
;那个应用于项目中的多个UserControl
。
其中一些UserControl
在其ViewModel中具有string State
属性,用于应用Visual States(通过外部类和附加属性,绑定到XAML中的ViewModel)。到目前为止,一切都很完美,然后,我尝试将DependencyProperty State
添加到UserControl
,并将其绑定到ViewModel中的state属性,我的尝试是:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--ResourceDictionary Source="..."/-->
</ResourceDictionary.MergedDictionaries>
<Style x:Key="MyStyle" TargetType="{x:Type local:MyUserControl}" BasedOn="{StaticResource UCStyle}">
<Setter Property="State" Value="{Binding State, Mode=TwoWay}"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.Style>
<DynamicResourceExtension ResourceKey="MyStyle" />
</UserControl.Style>
并显示错误说:
'MyUserControl'TargetType与元素'UserControl'的类型不匹配。
在Visual Studio中的XAML Viewer中既不应用UCStyle
也不应用MyStyle
,甚至不能正确绘制子UserControl
。我没想到解决方案能够正常运行,但确实如此!
现在我的问题是:
我正在使用Visual Studio 2012。
答案 0 :(得分:2)
wpf设计师在设计时显示虚假错误是邪恶的。你不能做太多但忽略它们。
视觉状态是UI的一个问题,因此应该包含在UI中。 MVVM 不 意味着没有代码隐藏。将您的代码隐藏用于UI任务,并将您的业务逻辑放在视图模型中。
您的问题表明您正在创建自定义视图模型以保存用户控件的视图逻辑。说真的,不要这样做。这会让你在路上遇到麻烦。它干扰了数据绑定的设计方式。
没有&#34;最佳实践&#34;用于将用户控件元素绑定到其表面上定义的属性。这取决于。然而,使用样式来做这件事似乎很奇怪。您可以简单地将UserControl的根目录设为x:Name="root"
,然后在绑定中使用ElementName=root
。
在UserControl中绑定到UserControl上定义的属性的一个示例(取自旧原型)...
这是一个用于添加或删除内容列表的UserControl。
我不保证这有效,但它会说明它是如何完成的:
public partial class ItemsEditor : UserControl
{
#region Items
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"Items",
typeof(IEnumerable<Item>),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public IEnumerable<Item> Items
{
get { return (IEnumerable<Item>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
#endregion
#region AddItem
public static readonly DependencyProperty AddItemProperty =
DependencyProperty.Register(
"AddItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand AddItem
{
get { return (ICommand)GetValue(AddItemProperty); }
set { SetValue(AddItemProperty, value); }
}
#endregion
#region RemoveItem
public static readonly DependencyProperty RemoveItemProperty =
DependencyProperty.Register(
"RemoveItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand RemoveItem
{
get { return (ICommand)GetValue(RemoveItemProperty); }
set { SetValue(RemoveItemProperty, value); }
}
#endregion
public ItemsEditor()
{
InitializeComponent();
}
}
它只是列出了一堆东西,你可以添加一个新东西或从列表中删除一个东西。这是xaml中的绑定
<UserControl x:Class="LolPrototype.ItemsEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:t="clr-namespace:UCsAndICommands"
x:Name="root">
<UserControl.Resources>
<DataTemplate DataType="{x:Type t:Item}">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding RemoveItem, ElementName=root}"
CommandParameter="{Binding}">Remove</Button>
<TextBox Text="{Binding Name}" Width="100"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Button Command="{Binding AddItem, ElementName=root}">Add</Button>
<ItemsControl ItemsSource="{Binding Items, ElementName=root}" />
</StackPanel>
</UserControl>
显然,您可以在祖先的资源中定义列表外的DataTemplates。关键是要说明如何使用ElementName绑定来绑定UserControl中定义的属性。