在WPF(MVVM)中动态更改窗口的用户控件

4
I'm ready to assist you in translating IT-related content into Chinese. Please provide the text that needs to be translated and specify any requirements for formatting.
<Window x:Class="OfficeMenu.Views.Menu.ControlPanel"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Control Panel" Height="800" Width="800">
<Grid>

    <ContentControl Content="{Binding contentWindow}"/>

</Grid>
</Window>

这是一个 UserControls 控件之一,当我运行项目时,它会向主窗口提供一个按钮菜单:
<UserControl x:Class="OfficeMenu.Views.ButtonsMenu"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
    <!-- One style for each *type* of control on the window -->
    <Style TargetType="Button">
        <Setter Property="Margin" Value="5"/>
    </Style>

</UserControl.Resources>
 <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="1*" />
        <RowDefinition Height="1*" />
    </Grid.RowDefinitions>
    <Button Command="{Binding OpenUsersCommand}">BUTTON 1</Button>
    <Button Grid.Column="1">BUTTON 2</Button>
    <Button Grid.Column="1" Grid.Row="2" >BUTTON 3</Button>
    <Button Grid.Row="1">BUTTON 4</Button>
</Grid>
</UserControl>

这是我用于此的ViewModel:

class MenuViewModel : ViewModelBase
{
    RandomModel _model;  <!-- It does nothing important -->
    private UserControl _content;

    public ICommand OpenUsersCommand { get; private set; }

    public UserControl Content
    {
        get { return _content; }
        set {
            _content = value;
            NotifyPropertyChanged("contentWindow");
        }
    }

    public MenuViewModel(RandomModel model )
    {
        this._model= model;
        OpenUsersCommand = new RelayCommand(OpenUsers,null);
    }

    private void OpenUsers()
    {
        Content = new UsersPanel();  //This is the UserControl we need to load dinamically
    }
}

在 App.xaml.cs 中,我们加载主窗口:
public partial class App : Application
{

    private void OnStartup(object sender, StartupEventArgs e)
    {
        ControlPanel view = new ControlPanel();
        view.ShowDialog();
    }
}

现在,控制面板(controlPanel.xaml.cs)的内容如下:
public ControlPanel()
    {
        InitializeComponent();

        ModelRandom model = new ModelRandom(); <!-- It does nothing yet -->
        MenuViewModel viewmodel = new MenuViewModel(model);
        Content = new BottonsMenu();  <!-- We load a default UserControl when we run the program -->
        this.DataContext = viewmodel;
    }
}

非常感谢。

在您的视图模型中,没有名为“contentWindow”的属性,而您在绑定中使用了它。另外,OpenUsers函数如何绑定到OpenUsersCommand? - Firoz
抱歉,我在发布代码之前尝试翻译它,但忘记更改这个。但我仍然遇到同样的问题。 - Ferran
我使用Prism,如果我理解你想做什么的话,你希望你的主窗口有两个“区域”:一个按钮区域和一个“主内容”区域。然后,根据按钮区域中的按钮触发的命令,在“主内容”区域中加载特定的VM。这非常简单,并且在Prism中处理得很好。要了解更多信息,请搜索MVVM导航主题。 - Mashton
1个回答

3

我认为问题在于你试图更改ContentControl的Content属性,而不是ViewModel的属性。你已将ViewModel连接到主机ControlPanel,但还需要单独的视图模型来控制将要托管在那里的用户控件。我已添加了用户控制器视图模型的类,并为清晰起见修改了宿主的视图模型属性名称。请将代码更正如下。

//host view model
class MainModel : ViewModelBase
{
    private UserControl _content;

    public MainModel() { }

    internal void SetNewContent(UserControl _content)
    {
        ContentWindow = _content;
    }

    public UserControl ContentWindow
    {
        get { return _content; }
        set
        {
            _content = value;
            OnPropertyChanged("ContentWindow");
        }
    }
}

//user contol's view model
class MenuViewModel : ViewModelBase
{
    MainModel _mainModel;
    RandomModel _model; // <!-- It does nothing important -->

    public ICommand OpenUsersCommand { get; private set; }


    public MenuViewModel(MainModel mainModel, RandomModel model )
    {
        this._mainModel = mainModel;
        this._model = model;
        OpenUsersCommand = new RelayCommand(OpenUsers, CanOpenUsers);
    }

    private void OpenUsers(object _param)
    {
        UsersPanelViewModel upmodel = new UsersPanelViewModel(_mainModel, _model);
        UsersPanel up = new UsersPanel();
        up.DataContext = upmodel;
        _mainModel.SetNewContent(up);
    }

    private bool CanOpenUsers(object _param)
    {
        return true;
    }
}

    //main window function
    public ControlPanel()
    {
        InitializeComponent();

        //create main view model for host
        MainModel mainModel = new MainModel();

        RandomModel model = new RandomModel(); //<!-- It does nothing yet -->

        //create view model for user controls
        MenuViewModel viewmodel = new MenuViewModel(mainModel, model);
        ButtonsMenu bm = new ButtonsMenu(); // <!-- We load a default UserControl when we run the program -->
        bm.DataContext = viewmodel;

        //set host's property in our user control
        mainModel.ContentWindow = bm;
        this.DataContext = mainModel;
    }

主窗口XAML

<Window x:Class="WpfApplication1.ControlPanel"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ContentControl Content="{Binding ContentWindow}"/>
    </Grid>
</Window>

希望这很容易理解。


3
在MVVM中,不允许在ViewModel中使用UserControl。 - Sumsuddin Shojib
@Ultraviolet 永远不要这样想。你只是因为别人告诉你才这么说的。每次都要批判性地思考,并思考什么对你最好,尽管有那些“规则”。不要把别人的意见当成自己的。这让我想到 - 甚至有人会告诉你,在代码后端不应该只有一行代码。那是胡说八道。在代码后端中,您编写与视图相关的代码。 - Ladislav Ondris
1
存在 MVVM 这个词的原因是有其道理的。不过,我在这里不想再继续这个话题了。谢谢。 - Sumsuddin Shojib
@SumsuddinShojib:说在M-V-VM中“不允许”*是没有意义的。MVVM是一种架构,人们可以在各种合规级别上遵循或不遵循它。人们可以争论将“View/UserControl”放置在ViewModel中的价值,或者这种做法是好还是坏;但除了编译器或语言规范外,不能推断出什么是/不是允许的。 - IAbstract

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接