动态用户控件更改 - WPF [英] Dynamic user control change - WPF

查看:17
本文介绍了动态用户控件更改 - WPF的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 WPF 中开发一个应用程序,我需要在运行时更改 ContentControl 的内容,具体取决于用户在 ComboBox 上选择的内容.

I'm developing an app in WPF and I need to change in runtime a content of a ContentControl depending than the user selected on ComboBox.

我有两个 UserControl,在我的组合中存在两个 itens,每个对应一个.

I have two UserControls and at my combo exists two itens, corresponding each one each.

第一个用户控件:

<UserControl x:Class="Validator.RespView"
         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="167" d:DesignWidth="366" Name="Resp">
<Grid>
    <CheckBox Content="CheckBox" Height="16" HorizontalAlignment="Left" Margin="12,12,0,0" Name="checkBox1" VerticalAlignment="Top" />
    <ListBox Height="112" HorizontalAlignment="Left" Margin="12,43,0,0" Name="listBox1" VerticalAlignment="Top" Width="168" />
    <Calendar Height="170" HorizontalAlignment="Left" Margin="186,0,0,0" Name="calendar1" VerticalAlignment="Top" Width="180" />
</Grid>

第二个用户控件:

<UserControl x:Class="Validator.DownloadView"
         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="76" d:DesignWidth="354" Name="Download">     
<Grid>
    <Label Content="States" Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" />
    <ComboBox Height="23" HorizontalAlignment="Left" Margin="12,35,0,0" Name="comboBox1" VerticalAlignment="Top" Width="120" />
    <RadioButton Content="Last 48 hs" Height="16" HorizontalAlignment="Left" Margin="230,42,0,0" Name="rdbLast48" VerticalAlignment="Top" />
    <Label Content="Kind:" Height="28" HorizontalAlignment="Left" Margin="164,12,0,0" Name="label2" VerticalAlignment="Top" />
    <RadioButton Content="General" Height="16" HorizontalAlignment="Left" Margin="165,42,0,0" Name="rdbGeral" VerticalAlignment="Top" />
</Grid>

在 MainWindowView.xaml

At MainWindowView.xaml

    <Window x:Class="Validator.MainWindowView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        xmlns:du="clr-namespace:Validator.Download"
        xmlns:resp="clr-namespace:Validator.Resp"                
        Title="Validator" Height="452" Width="668" 
        WindowStartupLocation="CenterScreen" ResizeMode="NoResize">
      <Window.Resources>
        <DataTemplate DataType="{x:Type du:DownloadViewModel}">
            <du:DownloadView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type resp:RespViewModel}">
            <resp:RespView/>
        </DataTemplate>
      </Window.Resources>
    <Grid>   

        <ComboBox  ItemsSource="{Binding Path=PagesName}" 
                   SelectedValue="{Binding Path=CurrentPageName}"
                   HorizontalAlignment="Left" Margin="251,93,0,0" 
                   Name="cmbType"                    
                   Width="187" VerticalAlignment="Top" Height="22"
                  SelectionChanged="cmbType_SelectionChanged_1" />
         <ContentControl Content="{Binding CurrentPageViewModel}" Height="171" HorizontalAlignment="Left" Margin="251,121,0,0" Name="contentControl1" VerticalAlignment="Top" Width="383" />
</Grid>
</Window>

我分配给了MainView的DataContext,viewmodel如下:

I assigned to the DataContext of the MainView, the viewmodel below:

public class MainWindowViewModel : ObservableObject
{
     #region Fields

    private ICommand _changePageCommand;

    private ViewModelBase _currentPageViewModel;
    private ObservableCollection<ViewModelBase> _pagesViewModel = new ObservableCollection<ViewModelBase>();        
    private readonly ObservableCollection<string> _pagesName = new ObservableCollection<string>();
    private string _currentPageName = "";

    #endregion

    public MainWindowViewModel()
    {
        this.LoadUserControls();         

        _pagesName.Add("Download");
        _pagesName.Add("Resp");
    }

    private void LoadUserControls()
    {
        Type type = this.GetType();
        Assembly assembly = type.Assembly;

        UserControl reso = (UserControl)assembly.CreateInstance("Validator.RespView");
        UserControl download = (UserControl)assembly.CreateInstance("Validator.DownloadView");

        _pagesViewModel.Add(new DownloadViewModel());
        _pagesViewModel.Add(new RespViewModel());
    }

    #region Properties / Commands

    public ICommand ChangePageCommand
    {
        get
        {
            if (_changePageCommand == null)
            {
                _changePageCommand = new RelayCommand(
                    p => ChangeViewModel((IPageViewModel)p),
                    p => p is IPageViewModel);
            }

            return _changePageCommand;
        }
    }

    public ObservableCollection<string> PagesName
    {
        get { return _pagesName; }            
    }

    public string CurrentPageName
    {
        get
        {
            return _currentPageName;
        }
        set
        {                
            if (_currentPageName != value)
            {
                _currentPageName = value;
                OnPropertyChanged("CurrentPageName");
            }
        }
    }

    public ViewModelBase CurrentPageViewModel
    {
        get
        {
            return _currentPageViewModel;
        }
        set
        {
            if (_currentPageViewModel != value)
            {
                _currentPageViewModel = value;
                OnPropertyChanged("CurrentPageViewModel");
            }
        }
    }

    #endregion

    #region Methods

    private void ChangeViewModel(IPageViewModel viewModel)
    {
        int indexCurrentView = _pagesViewModel.IndexOf(CurrentPageViewModel);

        indexCurrentView = (indexCurrentView == (_pagesViewModel.Count - 1)) ? 0 : indexCurrentView + 1;

        CurrentPageViewModel = _pagesViewModel[indexCurrentView];               
    }

    #endregion
}

在 MainWindowView.xaml.cs 上,我写了这个事件来做有效的改变:

On MainWindowView.xaml.cs, I wrote this event to do the effective change:

private void cmbType_SelectionChanged_1(object sender, SelectionChangedEventArgs e)
{
    MainWindowViewModel element = this.DataContext as MainWindowViewModel;
    if (element != null)
    {
        ICommand command = element.ChangePageCommand;
        command.Execute(null);
    }
}

应用程序运行正常,我使用 WPFInspector 检查了应用程序,发现在内部更改组合框时视图会发生变化,但 ContentControl 在视觉上仍然是空的..

The app run ok and I inspected the application with WPFInspector and saw that the view changes when the combobox is changed internally, but the ContentControl still empty visually..

对于我发布的代码量和我的知识缺失感到抱歉,但我在这方面工作了很长时间并且无法解决这个问题.谢谢

Sorry about the amount of code that I posted and my miss of knowledge but I'm working with this a long time and can't solve this problem. Thanks

推荐答案

问题:

  • 首先不要在 ViewModel (UserControl) 中创建与视图相关的内容.当您这样做时,这不再是 MVVM.
  • ViewModelBase 而非 ObservableObject 派生 ViewModel,除非您有充分的理由在使用 MVVMLight 时不使用 ViewModelBase.保持模型的 ObservableObject 继承.作为 VM 和 M 之间的良好分离
  • 接下来,您不需要像您的 _pagesViewModel 那样将所有内容都设为 ObservableCollection.你没有绑定到你的视图中的任何东西,所以这只是一种浪费.只需将其保留为私有列表或数组即可.检查一种类型与其他类似类型的实际区别.
  • 不确定这个,也许您将此代码片段作为演示提取,但不要使用边距来分隔网格中的项目.您的布局本质上只是 1 个 Grid 单元格,并且边距中的项目不重叠.如果您不知道该问题,请查看 WPF 布局文章.
  • 在编写 UI 应用程序时,请不要忘记 OOP、封装和排序的原则.当拥有像 CurrentPageViewModel 这样的属性时,您不打算让视图切换,使属性设置器 private 强制执行.
  • 不要过早在视图中使用代码隐藏.在这样做之前,首先检查它是否与视图相关的问题.我正在谈论您的 ComboBox SelectionChanged 事件处理程序.在本演示中,您的目的是切换保存在 VM 中的绑定视图模型.因此,这不是 View 全权负责的事情.因此,寻找一种涉及 VM 的方法.
  • Firstly don't ever create View related stuff in the ViewModel (UserControl). This is no longer MVVM when you do that.
  • Derive ViewModels from ViewModelBase and not ObservableObject unless you have a compelling reason to not use ViewModelBase when using MVVMLight. Keep ObservableObject inheritence for Models. Serves as a nice separation between VM's and M's
  • Next you do not need to make everything an ObservableCollection<T> like your _pagesViewModel. You do not have that bound to anything in your View's so it's just a waste. Just keep that as a private List or array. Check what a type actually does in difference to a similar other one.
  • Not sure about this one, maybe you pulled this code snippet as a demo, but do not use margins to separate items in a Grid. Your Layout is essentially just 1 Grid cell and the margins have the items not overlap. If you're not aware of that issue, Check into WPF Layout Articles.
  • Please don't forget principles of OOP, Encapsulation and sorts when writing a UI app. When having Properties like CurrentPageViewModel which you don't intend the View to switch make the property setter private to enforce that.
  • Don't resort to code-behind in the View too soon. Firstly check if it's only a View related concern before doing so. Am talking about your ComboBox SelectionChanged event handler. Your purpose of that in this demo is to switch the Bound ViewModel which is held in the VM. Hence it's not something that the View is solely responsible for. Thus look for a VM involved approach.

解决方案:

您可以从 此处 自己尝试一下.

You can get a working example of your code with the fixes for above from Here and try it out yourself.

点 1 -> 5 只是基本的直接更改.

Points 1 -> 5 are just basic straightforward changes.

对于 6,我在 MainViewModel 中创建了一个 SelectedVMIndex 属性,该属性绑定到 ComboBoxSelectedIndex.因此,当所选索引翻转时,更新自身后的属性Setter更新 CurrentPageViewModel ,如

For 6, I've created a SelectedVMIndex property in the MainViewModel which is bound to the SelectedIndex of the ComboBox. Thus when the selected index flips, the property setter after updating itself updates the CurrentPageViewModel as well such as

public int SelectedVMIndex {
  get {
    return _selectedVMIndex;
  }

  set {
    if (_selectedVMIndex == value) {
      return;
    }

    _selectedVMIndex = value;
    RaisePropertyChanged(() => SelectedVMIndex);

    CurrentPageViewModel = _pagesViewModel[_selectedVMIndex];
  }
}

这篇关于动态用户控件更改 - WPF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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