wpf mvvm 嵌套用户控件数据上下文

wpf mvvm nested user controls datacontext

我对嵌套 user-controlsdatacontext 有疑问。问题是 ViewModel 中的模型绑定在第一个 user-controlDependencyProperty 上。第一个 user-control 是第二个 user-control。第二个 user-control 还有一个 DependencyProperty。第一个 user-control 绑定到第二个 user-control 的那个 DependencyProperty

主视图:

<UserControl x:Class="XXX.XXX.Hmi.Views.L1SetupViewerView"
             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:prism="http://prismlibrary.com/"
             xmlns:uc="clr-namespace:XXX.XXX.Hmi.UserControls"
             d:DesignHeight="450"
             d:DesignWidth="800"
             prism:ViewModelLocator.AutoWireViewModel="True"
             Background="{DynamicResource BaseBrush}"
             Foreground="{DynamicResource TextBrush}"
             mc:Ignorable="d">
.
.
.
    <GroupBox Grid.Row="1" Header="Entry Section">
        <uc:L1SetupViewerEntrySectionView Margin="8" Setup="{Binding L1Setup}" />
    </GroupBox>
.
.
.
</UserControl>

MainViewModel:

public class L1SetupViewerViewModel : ViewModelBase
{
    private L1Setup _l1Setup;
    private Pdi _pdi;

    public L1SetupViewerViewModel()
    {
        Title = LanguageProvider.Instance.GetValue("L1SetupViewer_Window_Title");
        LanguageProvider.Instance.UiCultureChanged += Instance_UiCultureChanged;
    }

    #region Properties

    public L1Setup L1Setup
    {
        get { return _l1Setup; }
        set { SetProperty(ref _l1Setup, value); }
    }
    public Pdi Pdi
    {
        get { return _pdi; }
        set { SetProperty(ref _pdi, value); }
    }

    #endregion

    #region Navigation

    public override void OnNavigatedTo(NavigationContext navigationContext)
    {
        if (navigationContext.Parameters.ContainsKey("L1Setup"))
        {
            L1Setup = navigationContext.Parameters.GetValue<L1Setup>("L1Setup");
        }

        if (navigationContext.Parameters.ContainsKey("Pdi"))
        {
            Pdi = navigationContext.Parameters.GetValue<Pdi>("Pdi");
        }
    }

    #endregion

    private void Instance_UiCultureChanged(object sender, EventArgs e)
    {
        Title = LanguageProvider.Instance.GetValue("L1SetupViewer_Window_Title");
    }
}

第一个用户控件:

<UserControl x:Class="XXX.XXX.Hmi.UserControls.L1SetupViewerEntrySectionView"
             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:uc="clr-namespace:XXX.XXX.Hmi.UserControls"
             d:DesignHeight="450"
             d:DesignWidth="800"
             mc:Ignorable="d">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" SharedSizeGroup="c" />
            <ColumnDefinition Width="*" SharedSizeGroup="c" />
            <ColumnDefinition Width="*" SharedSizeGroup="c" />
            <ColumnDefinition Width="*" SharedSizeGroup="c" />
            <ColumnDefinition Width="*" SharedSizeGroup="c" />
            <ColumnDefinition Width="*" SharedSizeGroup="c" />
            <ColumnDefinition Width="*" SharedSizeGroup="c" />
        </Grid.ColumnDefinitions>
        <uc:L1SetupViewerCellControl Title="Over Speed"
                                     Grid.Row="0"
                                     Grid.Column="0"
                                     Margin="0,0,5,0"
                                     Value="{Binding Setup.EnOveSpd}" />
         .
         .
         .
    </Grid>
</UserControl>

第一个用户控件隐藏代码:

public partial class L1SetupViewerEntrySectionView : UserControl
{
    public L1Setup Setup
    {
        get { return (L1Setup)GetValue(SetupProperty); }
        set { SetValue(SetupProperty, value); }
    }
    public static readonly DependencyProperty SetupProperty =
        DependencyProperty.Register("Setup", typeof(L1Setup), typeof(L1SetupViewerEntrySectionView), new PropertyMetadata(null));

    public L1SetupViewerEntrySectionView()
    {
        InitializeComponent();
        this.DataContext = this;
    }
}

第二个用户控件:

<UserControl x:Class="XXX.XXX.Hmi.UserControls.L1SetupViewerCellControl"
             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"
             d:DesignHeight="450"
             d:DesignWidth="800"
             mc:Ignorable="d">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0"
                   Margin="0,0,3,0"
                   Text="{Binding Title}" />
        <TextBox Grid.Column="1" Text="{Binding Value}" />
    </Grid>
</UserControl>

第二个用户控件代码隐藏:

public partial class L1SetupViewerCellControl : UserControl
{
    public string Title
    {
        get { return (string)GetValue(TitleProperty); }
        set { SetValue(TitleProperty, value); }
    }
    public static readonly DependencyProperty TitleProperty =
        DependencyProperty.Register("Title", typeof(string), typeof(L1SetupViewerCellControl), new PropertyMetadata(string.Empty));

    public string Value
    {
        get { return (string)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(string), typeof(L1SetupViewerCellControl), new PropertyMetadata(string.Empty));

    public L1SetupViewerCellControl()
    {
        InitializeComponent();
        this.DataContext = this;
    }
}

如果我 运行 应用程序我得到以下绑定错误:

System.Windows.Data Error: 40 : BindingExpression path error: 'L1Setup' property not found on 'object' ''L1SetupViewerCellControl' (Name='')'. BindingExpression:Path=L1Setup.EnPor1OutDia; DataItem='L1SetupViewerCellControl' (Name=''); target element is 'L1SetupViewerCellControl' (Name=''); target property is 'Value' (type 'String')

我在 Whosebug 上尝试了几个答案,但对我没有任何帮助,我也不明白这是怎么回事。

L1SetupViewerEntrySectionView 上设置 this.DataContext = this 会破坏数据上下文从 L1SetupViewerView 的继承,这就是绑定到 L1Setup 失败的原因。

与其在代码隐藏中显式设置 DataContext,不如在 UserControl 中设置根元素的 DataContext,如下所示:

<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}>

...或每个绑定的来源:

<uc:L1SetupViewerCellControl ... 
    Value="{Binding Setup.EnOveSpd, RelativeSource={RelativeSource AncestorType=UserControl}}" />

同样的事情也适用于 L1SetupViewerCellControl,即从构造函数中删除 this.DataContext = this 并使用 RelativeSource 绑定到 Value 属性:

<TextBox Grid.Column="1" Text="{Binding Value, RelativeSource={RelativeSource AncestorType=UserControl}}" />