Again, I apologize in advance for any mistakes, English is not my native. I'm making an MVVM app, and I want to dynamically change views using ContentControl in the MainWindow, here is a necessary part of the code to understand:
Firstly, views: 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>
and MainWindow.xaml.cs:
public partial class MainWindow : Window
{
bool isMinimized = false;
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
Two views, that I want to be shown in 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();
}
}
Here are MainWindow view model
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 and ProfileViewModel are empty.
And, finally, 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>
You may need to look at the ObservableObject class:
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);
}
}
}
Then I run the app, I can actually see view, that I assign to CurrentView in MainViewModel constructor:
public MainViewModel()
{
CorrVM = new CorrespondensesViewModel();
ProfileVM = new ProfileViewModel();
CurrentView = CorrVM;
}
If I assign CorrVM or ProfileVM to CurrentView, I actually see CorrespondensesView or ProfileView, but I can't change view dynamically:
RadioButton Command binding works properly, CurrentView is reassigned every time I click on the corresponding button, but I can't see any changes in MainWindow.
So I think the problem is in the binding, do you have any ideas how to fix it?
UPD: Get section of this property is called only during the initialization, so the problem is definitely with binding.
public ObservableObject CurrentView
{
get { return currentView; }
set
{
currentView = value;
this.OnPropertyChanged("CurrentView");
}
}
Tried to use different binding modes and update triggers, but to no avail.
<ContentControl Grid.Row="1"
Content="{Binding Path=CurrentView, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
Try the following:
Create the following folders:
Add the files below to the appropriate folder (see image above).
HeadButtonTheme.xaml (ResourceDictionary)
<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 (Class)
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 (Class)
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 (Class)
namespace Vernam.ViewModel
{
class CorrespondencesViewModel
{
}
}
CorrespondencesView.xaml (UserControl)
<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 (Class)
namespace Vernam.ViewModel
{
class ProfileViewModel
{
}
}
ProfileView.xaml (UserControl)
<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 (Class)
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>
Resources :
I believe that because you are using two different view model instances for the radio buttons and the content view in MainWindow.
Here you use an instance from the window resource.
<StackPanel Grid.Column="2" Orientation="Horizontal"
DataContext="{Binding Source={StaticResource vm}}">
Here you use an instance from the data context of MainWindow.
<ContentControl Grid.Row="1" Grid.Column="0"
Content="{Binding CurrentView}" />
So the fixing is quite simple, use only one of them.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.