WPF + MVVM - 如何将属性绑定到父视图的数据上下文

发布于 2024-09-07 22:04:21 字数 3207 浏览 5 评论 0原文

使用 MVVM 模式,我有一对表示两层数据层次结构的视图模型类,每个类都有一个对应的 UserControl 表示其视图。两个视图模型类都实现了 INotifyPropertyChanged,并且根级别视图模型公开了与其自身视图和子视图相关的属性。

根级别视图获取根级别视图模型作为其数据上下文,并显式地将数据上下文分配给其包含的视图。但是,它还需要将子视图的属性之一绑定到上述共享属性。以下是我尝试实现此目的的方法,但它不起作用:

<UserControl x:Name="rootView">
    <StackPanel>

        <!-- other controls here -->

        <my:ChildView
            DataContext="{Binding Path=SelectedChild}"
            EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode />
    </StackPanel>
</UserControl>

尽管没有运行时绑定错误并且子视图正确绑定到适当的子视图模型实例,但其 EditingMode 属性从未设置。我已经运行测试来验证相应的视图模型属性是否正在被修改,以及它是否通过 INotifyPropertyChanged 通知此更改,但绑定无法检测到它。

有没有更好的方法来声明此绑定,或者我犯了一个更基本的架构错误?

非常感谢您的建议,

蒂姆

更新:根据要求,我发布了一些代码来显示我的视图和视图模型的非常简化的版本,以及我进行的实验结果提供一些额外的线索。

// The relevant parts of the ParentViewModel class
public class ParentViewModel : INotifyPropertyChanged
{
    // Although not shown, the following properties
    // correctly participate in INotifyPropertyChanged

    public ChildViewModel SelectedChild { get; private set; }

    public ContentEditingMode EditingMode { get; private set; }
}

// The relevant parts of the ChildViewModel class
public class ChildViewModel : INotifyPropertyChanged
{
    // No properties of ChildViewModel affect this issue.
}

// The relevant parts of the ParentView class
public partial class ParentView : UserControl
{
    // No properties of ParentView affect this issue.
}

// The relevant members of the ChildView class
public partial class ChildView : UserControl
{
    public static readonly DependencyProperty EditingModeProperty =
        DependencyProperty.Register(
            "EditingMode",
            typeof(ContentEditingMode),
            typeof(PostView)
        );

    public ContentEditingMode EditingMode
    {
        get { return (ContentEditingMode)GetValue(EditingModeProperty); }
        set { SetValue(EditingModeProperty, value); }
    }
}

// The enumeration used for the EditingMode property
public enum ContentEditingMode
{
    Html,
    WYSYWIG
}

我的意图是,父视图实例的 DataContext 将被分配一个 ParentViewModel 的实例,并且依次分配其 SelectedChild 的值code> 属性到嵌套 ChildViewDataContext。所有这些似乎都工作正常,但问题出现了,因为 ParentViewModel.EditingModeChildView.EditingMode 之间的绑定不起作用。

为了测试我的绑定表达式是否存在问题,我在 ChildView 附近引入了一个 TextBlock,并将其与 ParentViewModel.EditingMode< 类似地绑定。 /code> 属性:

<UserControl x:Name="rootView">
    <StackPanel>

        <!-- other controls here -->

        <TextBlock Text="{Binding ElementName=rootView, Path=DataContext.EditingMode}" />

        <my:ChildView
            DataContext="{Binding Path=SelectedChild}"
            EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode />
    </StackPanel>
</UserControl>

在此测试中,每次源属性更改时,TextBlock 都会正确更新。但是,如果我在 ChildView.EditingMode 的 setter 上设置断点,它永远不会被命中。

我很困惑!

Working with the MVVM pattern, I have a pair of view model classes that represent a two-tier data hierarchy, each with a corresponding UserControl that represents its view. Both view model classes implement INotifyPropertyChanged and the root level view model exposes a property that is relevant to both its own view and the child view.

The root level view acquires the root level view model as its data context and explicitly assigns a data context to its contained view. However, it also needs to bind one of the properties of the child view to the above-mentioned shared property. Here is how I have attempted to achieve this, but it's not working:

<UserControl x:Name="rootView">
    <StackPanel>

        <!-- other controls here -->

        <my:ChildView
            DataContext="{Binding Path=SelectedChild}"
            EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode />
    </StackPanel>
</UserControl>

Although there are no runtime binding errors and the child view correctly binds to the appropriate child view model instance, its EditingMode property is never set. I have run tests to verify that the corresponding view model property is being modified and that it is notifying this change via INotifyPropertyChanged, but the binding fails to detect it.

Is there a better way to declare this binding or have I made a more basic architectural error?

Many thanks for your advice,

Tim

Update: As requested, I am posting some code to show a very simplified version of my views and view models, together with the results of an experiment that I have conducted that may provide some additional clues.

// The relevant parts of the ParentViewModel class
public class ParentViewModel : INotifyPropertyChanged
{
    // Although not shown, the following properties
    // correctly participate in INotifyPropertyChanged

    public ChildViewModel SelectedChild { get; private set; }

    public ContentEditingMode EditingMode { get; private set; }
}

// The relevant parts of the ChildViewModel class
public class ChildViewModel : INotifyPropertyChanged
{
    // No properties of ChildViewModel affect this issue.
}

// The relevant parts of the ParentView class
public partial class ParentView : UserControl
{
    // No properties of ParentView affect this issue.
}

// The relevant members of the ChildView class
public partial class ChildView : UserControl
{
    public static readonly DependencyProperty EditingModeProperty =
        DependencyProperty.Register(
            "EditingMode",
            typeof(ContentEditingMode),
            typeof(PostView)
        );

    public ContentEditingMode EditingMode
    {
        get { return (ContentEditingMode)GetValue(EditingModeProperty); }
        set { SetValue(EditingModeProperty, value); }
    }
}

// The enumeration used for the EditingMode property
public enum ContentEditingMode
{
    Html,
    WYSYWIG
}

My intention is that the DataContext of the parent view instance will be assigned an instance of ParentViewModel and it will, in turn, assign the value of its SelectedChild property to the DataContext of the nested ChildView. All of this seems to work correctly, but the problem arises because the binding between ParentViewModel.EditingMode and ChildView.EditingMode does not work.

In an attempt to test whether there is a problem with my binding expression, I introduced a TextBlock adjacent to the ChildView and bound it similarly to the ParentViewModel.EditingMode property:

<UserControl x:Name="rootView">
    <StackPanel>

        <!-- other controls here -->

        <TextBlock Text="{Binding ElementName=rootView, Path=DataContext.EditingMode}" />

        <my:ChildView
            DataContext="{Binding Path=SelectedChild}"
            EditingMode="{Binding ElementName=rootView, Path=DataContext.EditingMode />
    </StackPanel>
</UserControl>

In this test, the TextBlock is correctly updated every time the source property changes. However, if I set a breakpoint on the setter of ChildView.EditingMode, it never gets hit.

I'm baffled !

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

灯角 2024-09-14 22:04:21

解决此问题的最简单方法是在视图模型中。在子视图模型中实现 EditingMode 属性并绑定到它。这样,您就不必对建立绑定的正确方法进行任何猜测;此外,您还可以在 UI 之外进行测试。

编辑

实际上,正确的解决方案并不那么简单,但值得了解如何去做。

您想要的是子控件中的 EditingMode 能够有效地从父控件继承其值。这听起来像 WPF 中其他任何东西所做的事情吗?就像几乎每个实现依赖属性的框架元素一样?

EditingMode 实现为父级和子级 UserControl 中的依赖属性,并使用属性值继承,如 此处。这将继承行为完全从视图模型中取出,并将其放在它所属的位置。

The simplest way to fix this is in your view model. Implement an EditingMode property in the child view model and bind to it. That way, you don't have to make any kind of guesses about what the right way to establish the binding might be; also, it's something that you can test outside of the UI.

Edit

Actually the right solution is not quite as simple, but it's worth knowing how to do.

What you want is for EditingMode in the child control to efficiently inherit its value from the parent control. Does that sound like something that anything else in WPF does? Like just about every framework element that implements dependency properties?

Implement EditingMode as a dependency property in both the parent and child UserControls and use property value inheritance, as described here. That takes the inheritance behavior out of the view model entirely and puts it where it belongs.

青春有你 2024-09-14 22:04:21

看看是否可以使用完整路径来获取所选子项的编辑模式:

<my:childView
        DataContext="{Binding SelectedChild}"
        EditingMode="{Binding SelectedChild.EditingMode />

See if you can just use a full path to get the editing mode of the selected child:

<my:childView
        DataContext="{Binding SelectedChild}"
        EditingMode="{Binding SelectedChild.EditingMode />
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文