【问题标题】:Dynamic user control change - WPF动态用户控件更改 - WPF
【发布时间】:2013-04-26 19:56:12
【问题描述】:

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

我有两个用户控件,在我的组合中存在两个项,每个项对应一个。

第一个用户控件:

<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

    <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如下:

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 上,我写了这个事件来做有效的改变:

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 在视觉上仍然为空..

对于我发布的代码量和知识缺失感到抱歉,但我已经处理了很长时间,但无法解决这个问题。 谢谢

【问题讨论】:

    标签: c# wpf mvvm user-controls


    【解决方案1】:

    问题:

    • 首先不要在 ViewModel (UserControl) 中创建与视图相关的内容。当你这样做时,这不再是 MVVM。
    • ViewModelBase 而不是 ObservableObject 派生 ViewModel,除非您在使用 MVVMLight 时有充分的理由不使用 ViewModelBase。保留模型的ObservableObject 继承。很好地分隔了 VM 和 M
    • 接下来,您不需要像 _pagesViewModel 那样将所有内容都设置为 ObservableCollection&lt;T&gt;。你没有绑定到你的视图中的任何东西,所以这只是一种浪费。只需将其保留为私有列表或数组即可。检查一种类型与其他类似类型的实际不同之处。
    • 不确定这个,也许你把这段代码 sn-p 作为演示,但不要使用边距来分隔网格中的项目。您的布局本质上只是 1 个网格单元格,并且边距的项目不重叠。如果您不知道该问题,请查看 WPF 布局文章。
    • 在编写 UI 应用时,请不要忘记 OOP、封装和排序的原则。当拥有像 CurrentPageViewModel 这样的属性时,您不打算让视图切换,让属性设置器 private 强制执行。
    • 不要过早在视图中使用代码隐藏。在这样做之前,首先检查它是否与视图相关的问题。我正在谈论您的 ComboBox SelectionChanged 事件处理程序。您在此演示中的目的是切换 VM 中保存的 Bound ViewModel。因此,这不是 View 单独负责的事情。因此,请寻找涉及 VM 的方法。

    解决方案

    您可以从 Here 获取带有上述修复程序的代码的工作示例,然后自己尝试一下。

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

    对于 6,我在 MainViewModel 中创建了一个 SelectedVMIndex 属性,该属性绑定到 ComboBoxSelectedIndex。因此,当所选索引翻转时,更新自身后的属性设置器会更新 CurrentPageViewModel以及

    public int SelectedVMIndex {
      get {
        return _selectedVMIndex;
      }
    
      set {
        if (_selectedVMIndex == value) {
          return;
        }
    
        _selectedVMIndex = value;
        RaisePropertyChanged(() => SelectedVMIndex);
    
        CurrentPageViewModel = _pagesViewModel[_selectedVMIndex];
      }
    }
    

    【讨论】:

    • 嗨 Viv,它完美运行!谢谢你。还有一个问题,“首先不要在 ViewModel (UserControl) 中创建与 View 相关的东西。当你这样做时,这不再是 MVVM”是什么意思。 ?
    • 我的意思是“UserControl”是一个视图控件。在 ViewModel 中创建它并不是真正遵循 MVVM。 MVVM 断开了 View 和 ViewModel 之间的关系和依赖关系。所以你尽量不要在你的 ViewModel 中创建诸如 UserControl、Button、ComboBox 之类的东西。甚至在 VM 中也避免了 System.Windows.Visibility 之类的事情,而是在 VM 中使用 bool 属性并使用转换器将其转换为 View 中的 Visibility
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多