【问题标题】:WPF ContentControl binding of dynamic content does not works properly动态内容的 WPF ContentControl 绑定无法正常工作
【发布时间】:2021-04-20 21:29:59
【问题描述】:

再次,对于任何错误,我提前道歉,英语不是我的母语。 我正在制作一个 MVVM 应用程序,并且我想在 MainWindow 中使用 ContentControl 动态更改视图,这是代码中必须理解的部分:

首先,观点: MainWindow.xaml

<Window x:Class="Vernam.MainWindow"
        x:Name="MainWindowID"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Vernam"
        xmlns:viewModel="clr-namespace:Vernam.ViewModels"
        mc:Ignorable="d"
        Height="600"
        Width="900"
        ...>

    <Window.Resources>
        <viewModel:MainViewModel x:Key="vm"></viewModel:MainViewModel>
    </Window.Resources>
                  ...
          <Grid>
            <Grid Grid.Row="0">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="150"></ColumnDefinition>
                    <ColumnDefinition Width="40"></ColumnDefinition>
                    <ColumnDefinition Width="454"></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>

                ...
                <StackPanel Grid.Column="2" Orientation="Horizontal"
                            DataContext="{Binding Source={StaticResource vm}}">
                    <RadioButton x:Name="CorrRadioButton"
                        Content="Correspondences"
                        Width="176"
                        Foreground="White"
                        FontSize="18"
                        Style="{StaticResource HeadButtonTheme}"
                        GroupName="Head"
                        IsChecked="True"
                        Command="{Binding Path=CorrCommand}"/>
                    <RadioButton x:Name="ProfileRadioButton"
                        Content="Profile"
                        Width="89"
                        Foreground="White"
                        FontSize="18"
                        Style="{StaticResource HeadButtonTheme}"
                        GroupName="Head"
                        Command="{Binding Path=ProfileCommand}"/>
                </StackPanel>

               ...

            </Grid>
            <ContentControl Grid.Row="1"
                            Content="{Binding CurrentView}"/>
        </Grid>
    </Border>
    </Window>

和 MainWindow.xaml.cs:

public partial class MainWindow : Window
    {
        bool isMinimized = false;
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new MainViewModel();
        }
    }

两个视图,我想在 MainWindow 中显示:

CorrespondensesView.xaml

<UserControl x:Class="Vernam.Views.CorrespondensesView"
             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" 
             xmlns:local="clr-namespace:Vernam.Views"
             mc:Ignorable="d"
             Height="540"
             Width="900">
    <Grid Background="#035251">
        ...
    </Grid>
</UserControl>

CorrespondensesView.xaml.cs

public partial class CorrespondensesView : UserControl
    {
        public CorrespondensesView()
        {
            InitializeComponent();
        }
    }

ProfileView.xaml:

<UserControl x:Class="Vernam.Views.ProfileView"
             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" 
             xmlns:local="clr-namespace:Vernam.Views"
             mc:Ignorable="d" 
             Height="540"
             Width="900">
    <Grid Background="#035251">
        ...
    </Grid>
</UserControl>

ProfileView.xaml.cs:

public partial class ProfileView : UserControl
    {
        public ProfileView()
        {
            InitializeComponent();
        }
    }

这里是 MainWindow 视图模型

namespace Vernam.ViewModels
{
    public class MainViewModel : ObservableObject
    {
        private RelayCommand corrCommand;
        private RelayCommand profileCommand;
        private object currentView;

        public CorrespondensesViewModel CorrVM { get; set; }
        public ProfileViewModel ProfileVM { get; set; }

        public object CurrentView
        {
            get { return currentView; }
            set
            {
                currentView = value;
                this.OnPropertyChanged("CurrentView");
            }
        }
        public RelayCommand CorrCommand
        {
            get
            {
                return corrCommand ??
                    (corrCommand = new RelayCommand(obj =>
                    {
                        CurrentView = CorrVM;
                    }));
            }
        }

        public RelayCommand ProfileCommand
        {
            get
            {
                return profileCommand ??
                    (profileCommand = new RelayCommand(obj =>
                    {
                        CurrentView = ProfileVM;
                    }));
            }
        }
        public MainViewModel()
        {
            CorrVM = new CorrespondensesViewModel();
            ProfileVM = new ProfileViewModel();
            
            CurrentView = CorrVM;
        }
    }
}

CorrespondencesViewModel 和 ProfileViewModel 为空。

最后是 App.xaml

<Application x:Class="Vernam.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Vernam"
             xmlns:viewModel="clr-namespace:Vernam.ViewModels"
             xmlns:view="clr-namespace:Vernam.Views"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
             ...
            <DataTemplate DataType="{x:Type viewModel:CorrespondensesViewModel}">
                <view:CorrespondensesView/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type viewModel:ProfileViewModel}">
                <view:ProfileView/>
            </DataTemplate>
        </ResourceDictionary>
    </Application.Resources>
</Application>

您可能需要查看 ObservableObject 类:

public class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }

        protected void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    }

然后我运行应用程序,我实际上可以看到我在 MainViewModel 构造函数中分配给 CurrentView 的视图:

        public MainViewModel()
        {
            CorrVM = new CorrespondensesViewModel();
            ProfileVM = new ProfileViewModel();
            
            CurrentView = CorrVM;
        }

如果我将 CorrVM 或 ProfileVM 分配给 CurrentView,我实际上会看到 CorrespondensesView 或 ProfileView,但我无法动态更改视图:

RadioButton 命令绑定正常工作,每次点击相应按钮时都会重新分配 CurrentView,但在 MainWindow 中看不到任何变化。

所以我认为问题出在绑定上,您有什么解决方法的想法吗?

更新: 这个属性的get部分只在初始化的时候调用,所以肯定是绑定的问题。

public ObservableObject CurrentView
        {
            get { return currentView; }
            set
            {
                currentView = value;
                this.OnPropertyChanged("CurrentView");
            }
        }

尝试使用不同的绑定模式和更新触发器,但无济于事。

<ContentControl Grid.Row="1"
                            Content="{Binding Path=CurrentView, UpdateSourceTrigger=PropertyChanged,  Mode=OneWay}"/>

【问题讨论】:

    标签: c# wpf xaml mvvm


    【解决方案1】:

    我相信这是因为您在 MainWindow 中为单选按钮和内容视图使用了两个不同的视图模型实例。

    这里你使用了窗口资源中的一个实例。

     <StackPanel Grid.Column="2" Orientation="Horizontal"
                                DataContext="{Binding Source={StaticResource vm}}">
    

    这里使用 MainWindow 的数据上下文中的一个实例。

     <ContentControl Grid.Row="1" Grid.Column="0"
                            Content="{Binding CurrentView}"  />
    

    所以修复很简单,只使用其中一个。

    【讨论】:

      【解决方案2】:

      尝试以下方法:

      创建以下文件夹:

      • 型号
      • 主题
      • 查看
      • 视图模型

      将下面的文件添加到适当的文件夹(见上图)。

      HeadButtonTheme.xaml(资源字典)

      <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
      
          <Style BasedOn="{StaticResource {x:Type ToggleButton}}"
                 TargetType="{x:Type RadioButton}"
                 x:Key="HeadButtonTheme">
              <Style.Setters>
                  <Setter Property="Template">
                      <Setter.Value>
                          <ControlTemplate TargetType="RadioButton">
                              <Grid VerticalAlignment="Stretch"
                                   HorizontalAlignment="Stretch"
                                   Background="{TemplateBinding Background}">
      
                                  <TextBlock Text="{TemplateBinding Property=Content}"
                                             VerticalAlignment="Center" 
                                             Margin="5, 0, 0, 0"/>
                              </Grid>
                          </ControlTemplate>
                      </Setter.Value>
                  </Setter>
      
                  <Setter Property="Background" Value="Transparent" />
                  <Setter Property="BorderThickness" Value="0" />
              </Style.Setters>
      
              <Style.Triggers>
                  <Trigger Property="IsChecked" Value="True">
                      <Setter Property="Background" Value="#22202f" />
                  </Trigger>
              </Style.Triggers>
      
          </Style>
      
      </ResourceDictionary>
      

      ObservableObject.cs(类)

      using System;
      using System.ComponentModel;
      using System.Windows.Input;
      using System.Runtime.CompilerServices;
      
      namespace Vernam
      {
          class ObservableObject : INotifyPropertyChanged
          {
              public event PropertyChangedEventHandler PropertyChanged;
      
      
              protected void OnPropertyChanged(string propertyName)
              {
                  this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
              }
      
              protected void OnPropertyChanged(PropertyChangedEventArgs e)
              {
                  var handler = this.PropertyChanged;
                  if (handler != null)
                  {
                      handler(this, e);
                  }
              }
          }
      }
      

      RelayCommand.cs(类)

      using System;
      using System.Windows.Input;
      
      namespace Vernam
      {
          class RelayCommand : ICommand
          {
              private Action<object> _execute;
              private Func<object, bool> _canExecute;
      
              public event EventHandler CanExecuteChanged
              {
                  add { CommandManager.RequerySuggested += value; }
                  remove { CommandManager.RequerySuggested += value; }
              }
      
              public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
              {
                  _execute = execute;
                  _canExecute = canExecute;
              }
      
              public bool CanExecute(object parameter)
              {
                  return _canExecute == null || _canExecute(parameter);
              }
      
              public void Execute(object parameter)
              {
                  _execute(parameter);
              }
          }
      }
      

      CorrespondencesViewModel.cs(类)

      namespace Vernam.ViewModel
      {
          class CorrespondencesViewModel
          {
          }
      }
      

      CorrespondencesView.xaml(用户控件)

      <UserControl x:Class="Vernam.View.CorrespondencesView"
                   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" 
                   xmlns:local="clr-namespace:Vernam.View"
                   mc:Ignorable="d" 
                   d:DesignHeight="450" d:DesignWidth="800">
      
          <Grid>
              <TextBlock Text="Correspondences" Foreground="Red"/>
          </Grid>
      </UserControl>
      

      ProfileViewModel.cs(类)

      namespace Vernam.ViewModel
      {
          class ProfileViewModel
          {
          }
      }
      

      ProfileView.xaml(用户控件)

      <UserControl x:Class="Vernam.View.ProfileView"
                   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" 
                   xmlns:local="clr-namespace:Vernam.View"
                   mc:Ignorable="d" 
                   d:DesignHeight="450" d:DesignWidth="800">
      
          <Grid>
              <TextBlock Text="Profile" Foreground="Red"/>
          </Grid>
      </UserControl>
      

      MainViewModel.cs(类)

      namespace Vernam.ViewModel
      {
          class MainViewModel : ObservableObject
          {
              public RelayCommand CorrCommand { get; set; }
              public RelayCommand ProfileCommand { get; set; }
      
              public CorrespondencesViewModel CorrVM { get; set; }
              public ProfileViewModel ProfileVM { get; set; }
      
              private object currentView;
      
              public object CurrentView
              {
                  get { return currentView; }
                  set
                  {
                      currentView = value;
                      OnPropertyChanged("CurrentView");
                  }
              }
      
              public MainViewModel()
              {
                  //create new instance
                  CorrVM = new CorrespondencesViewModel();
                  ProfileVM = new ProfileViewModel();
      
                  CorrCommand = new RelayCommand(obj =>
                  {
                      CurrentView = CorrVM;
                  });
      
                  ProfileCommand = new RelayCommand(obj =>
                  {
                      CurrentView = ProfileVM;
                  });
      
      
                  //set default view
                  CurrentView = CorrVM;
              }
          }
      }
      

      App.xaml

      <Application x:Class="Vernam.App"
                   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                   xmlns:local="clr-namespace:Vernam"
                   xmlns:viewModel="clr-namespace:Vernam.ViewModel"
                   xmlns:view="clr-namespace:Vernam.View"
                   StartupUri="MainWindow.xaml">
          <Application.Resources>
              <ResourceDictionary>
                  <ResourceDictionary.MergedDictionaries>
                      <ResourceDictionary Source="Theme\HeadButtonTheme.xaml" />
                  </ResourceDictionary.MergedDictionaries>
      
                  <DataTemplate DataType="{x:Type viewModel:CorrespondencesViewModel}">
                      <view:CorrespondencesView />
                  </DataTemplate>
      
                  <DataTemplate DataType="{x:Type viewModel:ProfileViewModel}">
                      <view:ProfileView />
                  </DataTemplate>
                                
              </ResourceDictionary>
          </Application.Resources>
      </Application>
      

      MainWindow.xaml

      <Window x:Class="Vernam.MainWindow"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:local="clr-namespace:Vernam"
              xmlns:viewModel="clr-namespace:Vernam.ViewModel"
              xmlns:view="clr-namespace:Vernam.View"
              mc:Ignorable="d"
              Title="MainWindow" Height="450" Width="800">
      
          <Window.Resources>
              <viewModel:MainViewModel x:Key="vm"></viewModel:MainViewModel>
          </Window.Resources>
          
          <Border Background="LightGray">
              <Grid DataContext="{Binding Source={StaticResource vm}}">
      
                  <Grid.RowDefinitions>
                      <RowDefinition Height="50" />
                      <RowDefinition />
                  </Grid.RowDefinitions>
                  
                  <Grid Grid.Row="0">
      
                      <Grid.ColumnDefinitions>
                          <ColumnDefinition Width="350" />
                          <ColumnDefinition Width="350" />
                          <ColumnDefinition />
                      </Grid.ColumnDefinitions>
                      
                      <StackPanel Orientation="Horizontal">
                          <RadioButton x:Name="CorrRadioButton"
                              Content="Correspondences"
                              Width="176"
                              Foreground="White"
                              FontSize="18"
                              Style="{StaticResource HeadButtonTheme}"
                              GroupName="Head"
                              IsChecked="True"
                              Command="{Binding CorrCommand}"/>
      
                          <RadioButton x:Name="ProfileRadioButton"
                              Content="Profile"
                              Width="89"
                              Foreground="White"
                              FontSize="18"
                              Style="{StaticResource HeadButtonTheme}"
                              GroupName="Head"
                              Command="{Binding ProfileCommand}"/>
                      </StackPanel>
                  </Grid>
                  
                  <ContentControl Grid.Row="1" Grid.Column="0"
                                  Content="{Binding CurrentView}"  />
              </Grid>
          </Border>
      </Window>
      

      资源

      【讨论】:

      • >WPF C# Professional Modern Flat UI Tutorial... 这就是我在自己的项目中使用的示例。我的代码其实是一样的,我复查了好几遍。
      • 它对我有用。尝试删除“obj”文件夹(Vernam\obj)并重建。
      • 没有成功。我更新了我的问题,看看它是否不麻烦。
      • 我对 WPF 比较陌生,但我确实得到了我发布的教程,可以成功使用。如果我重命名文件/文件夹,我确实遇到了问题。我发现如果我只是创建一个具有正确名称的新文件并复制相关代码,然后删除不需要的文件,效果最好。
      猜你喜欢
      • 1970-01-01
      • 2013-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-28
      • 2015-11-18
      相关资源
      最近更新 更多