如何从继承的用户控件访问viewModel依赖项属性?

我在WPF演示项目中感到困惑。我必须认识到我有很多使代码复杂化的约束。

核心元素是从UserControl继承的控件。我想让它的代码隐藏尽可能的轻。另外,我想在ControlTemplate中使用其XAML。它的C#代码应位于专用的ViewModel中(此示例适用于大型项目,具有专用的viewModel可以通过将所有viewmodel分组来提供帮助。但是无论如何,这是强制性的)。 最后但并非最不重要的一点,我想将此控件的2个属性绑定到外部属性。

这是我的MainWindow.xaml文件:

<Window x:Class="ViewModel_defined_in_ControlTemplate.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ViewModel_defined_in_ControlTemplate"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="MyDictionary.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <local:MyUserControl Template="{StaticResource TextBoxTemplate}"
                                 NomPersonne="sg"/>
            <Button Content="Click me!" Command="{Binding ElementName=MyViewModel,Path=ChangeTextBoxContent}" Width="100" HorizontalAlignment="Left"/>
        </StackPanel>
    </Grid>
</Window>

该按钮仅更改NomPersonne依赖项属性的值(请参见下文)。 MyDictionary.xaml包含ControlTemplate:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:ViewModel_defined_in_ControlTemplate">

    <ControlTemplate x:Key="TextBoxTemplate" TargetType="{x:Type local:MyUserControl}">
        <Grid>
            <Grid.DataContext>
                <local:MyViewModel/>
            </Grid.DataContext>
            <TextBox Width="50" HorizontalAlignment="Left" Text="{TemplateBinding NomPersonne}"/>
        </Grid>


    </ControlTemplate>    
</ResourceDictionary>

我不知道将依赖项属性放在何处,以及如何访问它。 我试图将其放在MyUserControl中:

namespace ViewModel_defined_in_ControlTemplate
{
    public partial class MyUserControl : UserControl
    {



        public string NomPersonne
        {
            get { return (string)GetValue(NomPersonneProperty); }
            set { SetValue(NomPersonneProperty, value); }
        }

        public static readonly DependencyProperty NomPersonneProperty =
            DependencyProperty.Register("NomPersonne", typeof(string), typeof(MyUserControl), new PropertyMetadata(""));

    }
}

现在可以从MyUserCOntrol的XAML访问它,但是后来我不知道如何访问它才能使按钮的命令更改属性:

namespace ViewModel_defined_in_ControlTemplate
{
    public class MyViewModel : ViewModelBase
    {

        public RelayCommand ChangeTextBoxContent = new RelayCommand(() => 
        { 
            //...
        }, () => true);

    }

}

我宁愿在viewmodel中具有依赖项属性,但是在这种情况下,如何在MainWindow的MyUserControl的XAML中访问它?

谢谢。

评论
  • ut_sed
    ut_sed 回复

    You should add a source property to the view model, bind the target property of the UserControl to this one and update the source property in the view model:

    <local:MyUserControl Template="{StaticResource TextBoxTemplate}" 
                         NomPersonne="{Binding Name}"/>
    

    查看模型:

    public class MyViewModel : ViewModelBase
    {
        public RelayCommand ChangeTextBoxContent = new RelayCommand(() =>
        {
            Name = "...":
        }, () => true);
    
        private string _name;
    
        public string Name
        {
            get { return _name; }
            set { _name = value; OnPropertyChanged(); }
        }
    
        ...
    }
    

    You should also set the DataContext in the window rather than in the ResourceDictionary:

    <Window x:Class="ViewModel_defined_in_ControlTemplate.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ViewModel_defined_in_ControlTemplate"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
        <Window.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="MyDictionary.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </Window.Resources>
        <Window.DataContext>
            <local:MyViewModel/>
        </Window.DataContext>
        <Grid>
            <StackPanel>
                <local:MyUserControl Template="{StaticResource TextBoxTemplate}"
                                     NomPersonne="{Binding Name}"/>
                <Button Content="Click me!" Command="{Binding ChangeTextBoxContent}" Width="100" HorizontalAlignment="Left"/>
            </StackPanel>
        </Grid>
    </Window>
    

    The ResourceDictionary should only define the template:

        <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:local="clr-namespace:ViewModel_defined_in_ControlTemplate">
    
            <ControlTemplate x:Key="TextBoxTemplate" TargetType="{x:Type local:MyUserControl}">
                <Grid>
                    <TextBox Width="50" HorizontalAlignment="Left" 
                             Text="{TemplateBinding NomPersonne}"/>
                </Grid>
            </ControlTemplate>
        </ResourceDictionary>
    
  • 呤!!
    呤!! 回复

    给UserControl一个ViewModel是一个非常糟糕的主意。您刚刚偶然发现了这种方法的许多问题之一。无法在ViewModel中定义DependecyProperty,只有代码隐藏。

    为了在后台代码和ViewModel之间同步数据,您将必须在后台代码中订阅ViewModel的PropertyChanged,并且每次ViewModel中的值发生更改时,都要在后台代码中更新各自的DependencyProperties。这也必须以其他方式解决。当DependencyProperty更改时,您必须在ViewModel中对其进行更新。做到这一点并非不可能,但这确实很丑陋(相信我,我已经做到了;以后再也不会)。

    另一个问题是将UserControl的DataContext(以代码隐藏或XAML形式)设置为ViewModel。如果直接在UserControl上设置它,绑定将不起作用。解决方法是设置UserControl的第一个子级的DataContext(同样,不要这样做)。

    带有ViewModel的UserControl是一个非常糟糕的主意。但这并不是说您的后台代码应包含所有代码。您总是可以将执行一些高级逻辑的方法提取到各自的类中。静态方法可以在任何地方调用,甚至可以在代码后面调用。