如何将UserControl的DependencyProperty绑定到MVVM中的ViewModel中的属性?

时间:2016-02-08 18:35:17

标签: c# wpf xaml mvvm user-controls

问题不在于如何让这些东西运转起来,它已经做到了;这是关于我正在经历的一些奇怪的行为,我需要理解它。我有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 doesn't match type of element 'UserControl'.

并显示错误说:

  

'MyUserControl'TargetType与元素'UserControl'的类型不匹配。

在Visual Studio中的XAML Viewer中既不应用UCStyle也不应用MyStyle,甚至不能正确绘制子UserControl。我没想到解决方案能够正常运行,但确实如此!

现在我的问题是:

        
  • 为什么在正常运行时会在设计时显示这些错误?
  •     
  • 如何在设计时消除这些错误? (我清理并重新构建了解决方案,并重新启动了Visual Studio,但这些都没有工作)
  •     
  • MVVM 中处理“UserControl”视觉状态的最佳做法是什么?
  •     
  • 将UserControl的DependencyProperty绑定到 MVVM 中的ViewModel中的属性的最佳做法是什么?

我正在使用Visual Studio 2012。

1 个答案:

答案 0 :(得分:2)

wpf设计师在设计时显示虚假错误是邪恶的。你不能做太多但忽略它们。

视觉状态是UI的一个问题,因此应该包含在UI中。 MVVM 意味着没有代码隐藏。将您的代码隐藏用于UI任务,并将您的业务逻辑放在视图模型中。

您的问题表明您正在创建自定义视图模型以保存用户控件的视图逻辑。说真的,不要这样做。这会让你在路上遇到麻烦。它干扰了数据绑定的设计方式。

没有&#34;最佳实践&#34;用于将用户控件元素绑定到其表面上定义的属性。这取决于。然而,使用样式来做这件事似乎很奇怪。您可以简单地将UserControl的根目录设为x:Name="root",然后在绑定中使用ElementName=root

在UserControl中绑定到UserControl上定义的属性的一个示例(取自旧原型)...

这是一个用于添加或删除内容列表的UserControl。

  • UserControl上定义的DependencyProperties
  • 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中定义的属性。