简体   繁体   English

ContentControl内容属性不随托管内容而变化

[英]ContentControl Content Property not changing with hosted content

I am trying to learn MVVM and have come across a weird snag. 我正在尝试学习MVVM,并遇到了一个奇怪的障碍。 I have a main menu with a drawer control that comes out and shows a menu: 我有一个主菜单,抽屉控件出来并显示一个菜单: 在此输入图像描述

In the main window where this drawer is, I have a ContentControl where I set its content with a Binding. 在这个抽屉所在的主窗口中,我有一个ContentControl ,我在其中使用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>

and here is the ViewModel: 这是ViewModel:

MainWindowViewModel.cs 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 在此输入图像描述

In " RecoveryOperationsView.xaml " (which is a UserControl ) I also reference the view model from above like so.. 在“ RecoveryOperationsView.xaml ”(这是一个UserControl )中,我也像这样引用了上面的视图模型。

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

and have a button to call the command to change the Content property of the ContentControl from the main window.. 并有一个按钮来调用命令从主窗口更改ContentControl的Content属性..

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

In my class to process the commands, I change the content based off of the passed parameter using a switch statement like so 在我的类中处理命令,我使用switch语句更改基于传递参数的内容

ChangeWindowContentCommand.cs 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;
        }
    }
}

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. 但是,这就是我迷路的地方......如果我点击这个新窗口中的内容,说出“恢复数据库”并用断点检查它,我可以看到属性被更改但实际的ContentControl内容属性没有改变为新的UserControl我做了......我可以用抽屉里的任何内容更改内容,但是如果我尝试单击ContentControl的托管内容中的按钮,则没有任何变化。 What am I missing? 我错过了什么?

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 . 很难100%肯定,而无需将项目与测试,但我非常相信,这些问题至少有一个是你的UserControl和你的MainWindow使用的不同实例MainWindowViewModel You do not need to instantiate the VM for the user control, as it will inherit the DataContext from the MainWindow . 您不需要为用户控件实例化VM,因为它将从MainWindow继承DataContext The way it works in WPF is that if any given UIElement does not have the DataContext assigned explicitly, it will inherit it from the first element up the logical tree that does has one assigned. 它工作在WPF的方式是,如果任何给定UIElement 具备DataContext明确分配,它会从第一个元素了逻辑树,做一个分配继承它。

So, just delete this code, and it should solve at least that issue. 所以,只需删除此代码,它至少应解决该问题。

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

And since you're learning WPF, I feel obligated to provide a couple other tips. 既然你正在学习WPF,我觉得有义务提供其他一些技巧。 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. 即使您正在使用ViewModel,您仍然通过创建ICommand的非常具体的实现并通过ViewModel分配UI元素来混合UI和逻辑。 This breaks the MVVM pattern. 这打破了MVVM模式。 I know MVVM takes a little time to understand, but once you do, it is very easy to use and maintain. 我知道MVVM需要一点时间来理解,但是一旦你这样做,它就很容易使用和维护。

To solve your problem, I would suggest creating View Models for each of your user controls. 为解决您的问题,我建议为每个用户控件创建View Models。 Please see this answer , where I go into quite a bit of detail on the implementation. 请参阅这个答案 ,我将详细介绍该实现。

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 . 您可以使用TabControl ,或者如果要使用命令,则可以将单个ContentControl绑定到MainWindowViewModelViewModelBase类型的属性。 Let's call it CurrentViewModel . 我们称之为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. 基本思想是为每个用户控制VM类型创建一个模板,该模板只包含一个Views实例。 When you assign the user control VM to the CurrentViewModel property, the binding will find those data templates and render the user control. 将用户控件VM分配给CurrentViewModel属性时,绑定将查找这些数据模板并呈现用户控件。 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}"/>

See how this approach keeps UI and logic at an arm's length? 了解这种方法如何将UI和逻辑保持在一定距离?

And lastly, consider creating a very generic implementation of ICommand to use in all your ViewModels rather than many specific implementations. 最后,考虑创建一个非常通用的ICommand实现,以便在所有ViewModel中使用,而不是在许多特定实现中使用。 I think most WPF programmers have more or less this exact RelayCommand implementation in their arsenal. 我认为大多数WPF程序员在他们的工具库中或多或少地具有这种确切的RelayCommand实现

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM