ContentControl 内容属性不随托管内容而变化 [英] ContentControl Content Property not changing with hosted content

查看:78
本文介绍了ContentControl 内容属性不随托管内容而变化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试学习 MVVM,但遇到了一个奇怪的问题.我有一个带有抽屉控件的主菜单,它出来并显示一个菜单:

I am trying to learn MVVM and have come across a weird snag. I have a main menu with a drawer control that comes out and shows a menu:

在此抽屉所在的主窗口中,我有一个 ContentControl,我在其中使用 Binding 设置其内容.

In the main window where this drawer is, I have a ContentControl where I set its content with a Binding.

<ContentControl x:Name="MainWindowContentControl" Content="{Binding Path=WindowContent}"/>

此窗口的绑定设置为视图模型.

This window's binding is set to a view model.

<Window.DataContext>
    <viewmodels:MainWindowViewModel/>
</Window.DataContext>

这是视图模型:

MainWindowViewModel.cs

public class MainWindowViewModel: ViewModelBase
{

    private object _content;

    public object WindowContent
    {
        get { return _content; }
        set
        {
            _content = value;
            RaisePropertyChanged(nameof(WindowContent));
        }
    }
    public ICommand SetWindowContent { get; set; }

    public MainWindowViewModel()
    {
        SetWindowContent = new ChangeWindowContentCommand(this);
    }


}

到目前为止,一切正常.例如,如果我点击恢复操作",我会得到:

So far up to this point, everything works fine. So for example, if I click "Recovery Operations", I get this:

RecoveryOperationsView.xaml

RecoveryOperationsView.xaml"(这是一个UserControl)中,我也像这样引用了上面的视图模型..

In "RecoveryOperationsView.xaml" (which is a UserControl) I also reference the view model from above like so..

<UserControl.DataContext>
    <viewmodels:MainWindowViewModel/>
</UserControl.DataContext>

并有一个按钮来调用命令以从主窗口更改 ContentControl 的 Content 属性..

and have a button to call the command to change the Content property of the ContentControl from the main window..

<Button Grid.Row="2" Content="Restore Database" Width="150" Style="{StaticResource MaterialDesignFlatButton}" Command="{Binding SetWindowContent}" CommandParameter="DatabaseRecovery" >

在我的类中处理命令,我使用像这样的 switch 语句根据传递的参数更改内容

In my class to process the commands, I change the content based off of the passed parameter using a switch statement like so

ChangeWindowContentCommand.cs

public class ChangeWindowContentCommand : ICommand
{
    private MainWindowViewModel viewModel;
    public ChangeWindowContentCommand(MainWindowViewModel vm)
    {
        this.viewModel = vm;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        switch (parameter)
        {
            case "Home":
                viewModel.WindowContent = new HomeView();
                break;
            case "RecoveryOps":
                viewModel.WindowContent = new RecoveryOperationsView();
                break;
            case "DatabaseRecovery":
                viewModel.WindowContent = new DatabaseRestoreView();
                break;
        }
    }
}

然而,这就是我迷路的地方...如果我在这个新窗口中单击某些内容,说恢复数据库"并使用断点检查它,我可以看到属性被更改,但实际 ContentControl Content 属性不会更改为我创建的新 UserControl... 我可以使用抽屉中的任何内容更改内容,但是如果我尝试单击 的托管内容中的按钮>ContentControl 没有任何变化.我错过了什么?

However, this is where I get lost... If I click something within this new window, say "Restore Database" and inspect it with a breakpoint, I can see the property being changed but the actual ContentControl Content property doesnt change to the new UserControl I made... I can change the content with anything in the drawer, but if I try to click a button in the hosted Content of the ContentControl nothing changes. What am I missing?

推荐答案

如果没有您的项目进行测试,很难 100% 确定,但我相当有信心,至少其中一个问题是您的 UserControl 和您的 MainWindow 使用 MainWindowViewModel 的不同实例.您不需要为用户控件实例化 VM,因为它将从 MainWindow 继承 DataContext.它在 WPF 中的工作方式是,如果任何给定的 UIElement 没有没有显式分配DataContext,它将从第一个元素向上继承它分配了一个的逻辑树.

It's hard to be 100% sure without having your project to test with, but I am fairly confident that at least one of the issues is that your UserControl and your MainWindow use different instances of the MainWindowViewModel. You do not need to instantiate the VM for the user control, as it will inherit the DataContext from the MainWindow. The way it works in WPF is that if any given UIElement does not have theDataContext assigned explicitly, it will inherit it from the first element up the logical tree that does has one assigned.

所以,删除这段代码,它应该至少可以解决这个问题.

So, just delete this code, and it should solve at least that issue.

<UserControl.DataContext>
    <viewmodels:MainWindowViewModel/>
</UserControl.DataContext>

既然您正在学习 WPF,我觉得有必要提供一些其他提示.即使您使用的是 ViewModel,您仍然通过创建 ICommand 的非常具体的实现并通过 ViewModel 分配 UI 元素来混合 UI 和逻辑.这打破了 MVVM 模式.我知道 MVVM 需要一点时间来理解,但是一旦你理解了,它就非常容易使用和维护.

And since you're learning WPF, I feel obligated to provide a couple other tips. Even though you're using a ViewModel, you are still mixing UI and logic by creating a very specific implementation of ICommand and assigning a UI element through your ViewModel. This breaks the MVVM pattern. I know MVVM takes a little time to understand, but once you do, it is very easy to use and maintain.

为了解决您的问题,我建议为您的每个用户控件创建视图模型.请参阅这个答案,我在其中详细介绍了实现.

To solve your problem, I would suggest creating View Models for each of your user controls. Please see this answer, where I go into quite a bit of detail on the implementation.

要切换不同的视图,您有几个选择.您可以使用 TabControl,或者如果您想使用命令,您可以将单个 ContentControl 绑定到 MainWindowViewModel 的属性属于 ViewModelBase 类型.我们称之为CurrentViewModel.然后,当命令触发时,您将所需用户控件的视图模型分配给该绑定属性.您还需要使用隐式数据模板.基本思想是为每个用户控制 VM 类型创建一个模板,其中只包含一个视图实例.当您将用户控件 VM 分配给 CurrentViewModel 属性时,绑定将找到这些数据模板并呈现用户控件.例如:

For switching the different views, you have a couple of options. You can either use a TabControl, or if you want to use a command, you can have a single ContentControl bound to a property of MainWindowViewModel that is of type ViewModelBase. Let's call it CurrentViewModel. Then when the command fires, you assign the view model of the desired user control to that bound property. You will also need to utilize implicit data templates. The basic idea is that you create a template for each of the user control VM types, which would just contains an instance of the Views. When you assign the user control VM to the CurrentViewModel property, the binding will find those data templates and render the user control. For example:

<Window.Resources>
  <DataTemplate DataType = "{x:Type viewmodels:RecoveryOperationsViewModel}">
    <views:RecoveryOperationsView/>
  </DataTemplate> 
  <!-- Now add a template for each of the views-->
</Window.Resources>

<ContentControl x:Name="MainWindowContentControl" Content="{Binding CurrentViewModel}"/>

看看这种方法如何让 UI 和逻辑保持一臂之力?

See how this approach keeps UI and logic at an arm's length?

最后,考虑创建一个非常通用的 ICommand 实现,以在您的所有 ViewModel 中使用,而不是许多特定的实现.我认为大多数 WPF 程序员或多或少都有这个确切的RelayCommand 实现.

And lastly, consider creating a very generic implementation of ICommand to use in all your ViewModels rather than many specific implementations. I think most WPF programmers have more or less this exact RelayCommand implementation in their arsenal.

这篇关于ContentControl 内容属性不随托管内容而变化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆