ContentControl Content Property not changing with hosted content

白昼怎懂夜的黑 提交于 2019-12-06 03:45:58

问题


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:

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>

and here is the ViewModel:

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

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

<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..

<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

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. What am I missing?


回答1:


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>

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.

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}"/>

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

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.



来源:https://stackoverflow.com/questions/53383868/contentcontrol-content-property-not-changing-with-hosted-content

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!