ContentControl 内容属性不随托管内容而变化 [英] ContentControl Content Property not changing with hosted content
问题描述
我正在尝试学习 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屋!