wpf 无论用户控件有多少层,在用户控件内设置用户控件的数据上下文

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16148442/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 08:39:09  来源:igfitidea点击:

Set the datacontext of a usercontrol within a usercontrol no matter how many layers of usercontrol

c#wpfmvvmuser-controlsmvvm-light

提问by Bien Baldonado

I'm currently creating an application that would test the flexibility of WPF. I have some usercontrols that were intended to be quite transparent when it comes to its ViewModel. What I mean by transparent is that the usercontrol can use any type of ViewModel provided that the ViewModel has all the required properties that would be binded to the controls within that usercontrol. I do this by assigning the ViewModel as datacontext for the certain usercontrol.

我目前正在创建一个应用程序来测试 WPF 的灵活性。我有一些用户控件,当涉及到它的 ViewModel 时,它们是非常透明的。我所说的透明是指用户控件可以使用任何类型的 ViewModel,前提是 ViewModel 具有将绑定到该用户控件中的控件的所有必需属性。为此,我将 ViewModel 分配为特定用户控件的数据上下文。

This works when there are only two usercontrols (one has access to ViewModelLocator, one requires a datacontext declaration from the former). I don't know what to do when it reaches 3 layers of usercontrols or more. Is there a way to set a datacontext of a usercontrol within a usercontrol which resides in a usercontrol that has access to the ViewModelLocator?

这适用于只有两个用户控件(一个可以访问 ViewModelLocator,一个需要来自前者的数据上下文声明)。当它达到 3 层用户控件或更多时,我不知道该怎么办。有没有办法在驻留在有权访问 ViewModelLocator 的用户控件中的用户控件中设置用户控件的数据上下文?

Below are some code that would clarify my question.

下面是一些可以澄清我的问题的代码。

This code is the parent usercontrol. It was intended to be used on an application that utilizes MAF. I used a non-static ViewModelLocator to make sure that each plugin instance uses a different instance of ViewModelLocator and because the plugin would not have its own app.xaml (thus no global resource). As you can see, I placed a usercontrol from a separate assembly in the grid, then declared its datacontext so that the said usercontrol would interact with the parent usercontrol's ViewModelLocator.

此代码是父用户控件。它旨在用于利用 MAF 的应用程序。我使用了一个非静态的 ViewModelLocator 来确保每个插件实例使用不同的 ViewModelLocator 实例,因为插件没有自己的 app.xaml(因此没有全局资源)。如您所见,我在网格中放置了一个来自单独程序集的用户控件,然后声明了它的数据上下文,以便所述用户控件与父用户控件的 ViewModelLocator 进行交互。

<UserControl x:Class="TestApp.Inventory.Common.Views.MaterialsNewView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:vm="clr-namespace:TestApp.Inventory.Common.ViewModel"
             xmlns:views="clr-namespace:TestApp.Inventory.Common.Views"
             xmlns:viewsSupp="clr-namespace:TestApp.Supplier.Common.Views;assembly=TestApp.Supplier.Common"
             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" 
             mc:Ignorable="d" Height="607" Width="616" Loaded="UserControl_Loaded">

    <UserControl.Resources>
        <vm:ViewModelLocator x:Key="Locator" />
    </UserControl.Resources>

    <UserControl.DataContext>
        <Binding Path="MaterialsNewView" Source="{StaticResource Locator}" />
    </UserControl.DataContext>

    <Grid>
        <views:SupplierView x:Name="supplierView" Margin="145,306,0,0" HorizontalAlignment="Left" Width="328" Height="258" VerticalAlignment="Top" DataContext="{Binding Source={StaticResource Locator}, Path=SupplierView}" />
    </Grid>
</UserControl>

Then I have the code for the child usercontrol. Like what I said earlier, the child usercontrol was intended to be transparent in terms of the ViewModel. That's why the datacontext should be declared in the parent usercontrol every time.

然后我有子用户控件的代码。就像我之前所说的,子用户控件在 ViewModel 方面是透明的。这就是为什么每次都应该在父用户控件中声明数据上下文。

<UserControl x:Class="TestApp.Supplier.Common.Views.SupplierView"
        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:ignore="http://www.ignore.com"
        mc:Ignorable="d ignore" Height="289" Width="352" 
        xmlns:my="clr-namespace:TestApp.Lookup.Common.Views;assembly=TestApp.Lookup.Common">

    <Grid>
        <my:MaterialTypeListView Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Grid, AncestorLevel=1}, Path=ActualHeight}" HorizontalAlignment="Left" Name="materialTypeListView1" VerticalAlignment="Top" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Grid, AncestorLevel=1}, Path=ActualWidth}" />
    </Grid>
</UserControl>

My problem is when the child usercontrol has its own child usercontrol. I don't know how to declare its datacontext from the parent usercontrol. The objective is no matter how many layers of usercontrol are there, they should interact with the ViewModelLocator of the parent usercontrol.

我的问题是当子用户控件有自己的子用户控件时。我不知道如何从父用户控件声明其数据上下文。目标是无论有多少层用户控件,它们都应该与父用户控件的 ViewModelLocator 进行交互。

采纳答案by Ralf de Kleine

Add a DependencyPropertyto the UserControl SupplierView named MaterialTypeList.

DependencyProperty向名为 MaterialTypeList 的 UserControl SupplierView添加一个。

public partial class SupplierView
{
    public List<string> MaterialTypeList
    {
        get { return (List<string>)GetValue(MaterialTypeListProperty); }
        set { SetValue(MaterialTypeListProperty, value);}
    }

    public static readonly DependencyProperty MaterialTypeListProperty =
            DependencyProperty.Register("MaterialTypeList", typeof(string), typeof(SupplierView),
            new PropertyMetadata(null));
}

Add binding on UserControl MaterialNewView on SupplierView to MaterialTypeList

将 SupplierView 上的 UserControl MaterialNewView 绑定添加到 MaterialTypeList

 <UserControl x:Class="TestApp.Supplier.Common.Views.SupplierView"
            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:ignore="http://www.ignore.com"
            mc:Ignorable="d ignore" Height="289" Width="352" 
            xmlns:my="clr-namespace:TestApp.Lookup.Common.Views;assembly=TestApp.Lookup.Common">

        <Grid>
            <my:MaterialTypeListView 
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl, AncestorLevel=1}, Path=MaterialTypeList}"
Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Grid, AncestorLevel=1}, Path=ActualHeight}" HorizontalAlignment="Left" Name="materialTypeListView1" VerticalAlignment="Top" Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Grid, AncestorLevel=1}, Path=ActualWidth}" />
        </Grid>
    </UserControl>

Add binding on UserControl SupplierView on MaterialTypeListView to MaterialTypeList.

将 MaterialTypeListView 上的 UserControl SupplierView 绑定添加到 MaterialTypeList。

    <UserControl x:Class="TestApp.Inventory.Common.Views.MaterialsNewView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:vm="clr-namespace:TestApp.Inventory.Common.ViewModel"
                 xmlns:views="clr-namespace:TestApp.Inventory.Common.Views"
                 xmlns:viewsSupp="clr-namespace:TestApp.Supplier.Common.Views;assembly=TestApp.Supplier.Common"
                 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" 
                 mc:Ignorable="d" Height="607" Width="616" Loaded="UserControl_Loaded">

        <UserControl.Resources>
            <vm:ViewModelLocator x:Key="Locator" />
        </UserControl.Resources>

        <UserControl.DataContext>
            <Binding Path="MaterialsNewView" Source="{StaticResource Locator}" />
        </UserControl.DataContext>

        <Grid>
            <views:SupplierView x:Name="supplierView" Margin="145,306,0,0" 
MaterialTypeList="{Binding [HERE YOUR LIST OF MATERIALTYPE]}"
HorizontalAlignment="Left" Width="328" Height="258" VerticalAlignment="Top" DataContext="{Binding Source={StaticResource Locator}, Path=SupplierView}" />
        </Grid>
    </UserControl>