Reputation: 81
I'm trying to change which Grid within my PDF is visible based on the value of an enum. The value of the enum itself is based on a menu and when selecting an option of the menu, the enum value changes and the PropertyChangedEventHandler is called.
The code of the class that includes the PropertyChangedEventHandler is as follows:
public class ScreenToShow : INotifyPropertyChanged
{
public enum MenuState { MenuPage, MenuChoice1, MenuChoice2, MenuChoice3};
MenuState state;
public MenuState _State
{
get { return state; }
set
{
state = value;
this.NotifyPropertyChanged("_State");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
To change the content of my WPF Window, I've binded the visibility of 4 grids (each for one option of the menu + the start page) to this converter, as follows: Resources:
<Window.Resources>
<local:ScreenToShow x:Key="myScreenToShow"></local:ScreenToShow>
<local:VisibilityScreenConverter x:Key="myVisibilityScreenConverter"></local:VisibilityScreenConverter>
</Window.Resources>
XAML code of 1 of the four grids:
<Grid DataContext="{Binding Source={StaticResource myScreenToShow}}" Name="MenuPage" Grid.Row="1" Visibility="{Binding Path= _State, Converter={StaticResource myVisibilityScreenConverter}, ConverterParameter='menuPage', UpdateSourceTrigger=Default, Mode=TwoWay}">
And the code of the converter is as follows:
class VisibilityScreenConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is ScreenToShow.State)
{
if((string)parameter == "menuPage")
{
switch(value)
{
case ScreenToShow.State.MenuPage:
return Visibility.Visible;
default:
return Visibility.Hidden;
}
}
else if ((string)parameter == "menuChoice1")
{
switch (value)
{
case ScreenToShow.State.MenuChoice1:
return Visibility.Visible;
default:
return Visibility.Hidden;
}
}
else if ((string)parameter == "menuChoice2")
{
switch (value)
{
case ScreenToShow.State.MenuChoice2:
return Visibility.Visible;
default:
return Visibility.Hidden;
}
}
else // Menu choice 3
{
switch (value)
{
case ScreenToShow.State.MenuChoice3:
return Visibility.Visible;
default:
return Visibility.Hidden;
}
}
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
In the MainWindow.cs, the following code is used:
ScreenToShow screenToShowEnum = new ScreenToShow();
public MainWindow()
{
DataContext = this;
InitializeComponent();
screenToShowEnum._State = ScreenToShow.State.MenuPage;
}
private void MenuChoice1_Click(object sender, RoutedEventArgs e)
{
screenToShowEnum._State = ScreenToShow.State.MenuChoice1;
}
private void MenuChoice2_Click(object sender, RoutedEventArgs e)
{
screenToShowEnum._State = ScreenToShow.State.MenuChoice2;
}
private void MenuChoice3_Click(object sender, RoutedEventArgs e)
{
screenToShowEnum._State = ScreenToShow.State.MenuChoice3;
}
On startup, the code works fine and both the NotifyPropertyChanged and the Converter are being called (checked it with console.writeline). Moreover, the visibility of the different grids is handled as expected. When selecting one of the menu options, the PropertyChangedHandler is also called, as expected. However, the visibility of the different grids doesn't change. What am I doing wrong that the visibility changes based on my code at start-up, but not when changing the value of my enum in a later stage?
Thanks!
Upvotes: 0
Views: 454
Reputation: 26085
Problem is you are referring to two completely different ViewModel
objects, one being used by your xaml view and another by code behind file.
Either set DataContext
property in the code behind file or set it in the xaml file suing:
<Window.DataContext>
<StaticResource ResourceKey="myScreenToShow" />
</Window.DataContext>
if you use second approach make sure to cast and use your DataContext
in event handlers:
private void MenuChoice1_Click(object sender, RoutedEventArgs e)
{
(DataContext as ScreenToShow)._State = ScreenToShow.MenuState.MenuChoice1;
}
private void MenuChoice2_Click(object sender, RoutedEventArgs e)
{
(DataContext as ScreenToShow)._State = ScreenToShow.MenuState.MenuChoice2;
}
private void MenuChoice3_Click(object sender, RoutedEventArgs e)
{
(DataContext as ScreenToShow)._State = ScreenToShow.MenuState.MenuChoice3;
}
Demo Code:
<Window
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:WpfApplicationTest"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2" x:Class="WpfApplicationTest.MainWindow"
mc:Ignorable="d"
x:Name="win"
WindowStartupLocation="CenterOwner"
Title="MainWindow" Height="300" Width="300">
<Window.Resources>
<ResourceDictionary>
<local:ScreenToShow x:Key="myScreenToShow"></local:ScreenToShow>
<local:VisibilityScreenConverter x:Key="myVisibilityScreenConverter"></local:VisibilityScreenConverter>
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<StaticResource ResourceKey="myScreenToShow" />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button Margin="5" Content="Menu 1" Click="MenuChoice1_Click" />
<Button Margin="5" Content="Menu 2" Click="MenuChoice2_Click" />
<Button Margin="5" Content="Menu 3" Click="MenuChoice3_Click" />
</StackPanel>
<Grid DataContext="{Binding Source={StaticResource myScreenToShow}}"
Name="MenuPage1"
Grid.Row="1"
Visibility="{Binding Path= _State, Converter={StaticResource myVisibilityScreenConverter}, ConverterParameter='menuChoice1', UpdateSourceTrigger=Default, Mode=TwoWay}">
<TextBlock Text="Menu 1" FontSize="24" />
</Grid>
<Grid DataContext="{Binding Source={StaticResource myScreenToShow}}"
Name="MenuPage2"
Grid.Row="1"
Visibility="{Binding Path= _State, Converter={StaticResource myVisibilityScreenConverter}, ConverterParameter='menuChoice2', UpdateSourceTrigger=Default, Mode=TwoWay}">
<TextBlock Text="Menu 2" FontSize="24" />
</Grid>
<Grid DataContext="{Binding Source={StaticResource myScreenToShow}}"
Name="MenuPage3"
Grid.Row="1"
Visibility="{Binding Path= _State, Converter={StaticResource myVisibilityScreenConverter}, ConverterParameter='menuChoice3', UpdateSourceTrigger=Default, Mode=TwoWay}">
<TextBlock Text="Menu 3" FontSize="24" />
</Grid>
</Grid>
</Window>
Code behind files:
class VisibilityScreenConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is ScreenToShow.MenuState)
{
var state = (ScreenToShow.MenuState)value;
if ((string)parameter == "menuPage")
{
switch (state)
{
case ScreenToShow.MenuState.MenuPage:
return Visibility.Visible;
default:
return Visibility.Hidden;
}
}
else if ((string)parameter == "menuChoice1")
{
switch (state)
{
case ScreenToShow.MenuState.MenuChoice1:
return Visibility.Visible;
default:
return Visibility.Hidden;
}
}
else if ((string)parameter == "menuChoice2")
{
switch (state)
{
case ScreenToShow.MenuState.MenuChoice2:
return Visibility.Visible;
default:
return Visibility.Hidden;
}
}
else // Menu choice 3
{
switch (state)
{
case ScreenToShow.MenuState.MenuChoice3:
return Visibility.Visible;
default:
return Visibility.Hidden;
}
}
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class ScreenToShow : INotifyPropertyChanged
{
public enum MenuState { MenuPage, MenuChoice1, MenuChoice2, MenuChoice3 };
MenuState state;
public MenuState _State
{
get { return state; }
set
{
state = value;
this.NotifyPropertyChanged("_State");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MenuChoice1_Click(object sender, RoutedEventArgs e)
{
(DataContext as ScreenToShow)._State = ScreenToShow.MenuState.MenuChoice1;
}
private void MenuChoice2_Click(object sender, RoutedEventArgs e)
{
(DataContext as ScreenToShow)._State = ScreenToShow.MenuState.MenuChoice2;
}
private void MenuChoice3_Click(object sender, RoutedEventArgs e)
{
(DataContext as ScreenToShow)._State = ScreenToShow.MenuState.MenuChoice3;
}
}
Upvotes: 1