在 INotifyPropertyChanged 上调用 IValueConverter 仅在开始时有效
Calling IValueConverter on INotifyPropertyChanged only works at start
我正在尝试根据枚举值更改我的 PDF 中的哪个网格可见。枚举本身的价值基于菜单,选择菜单的选项时,枚举值更改和属性ChangeDeventHandler被调用。
包含PropertyChangedEventHandler的class代码如下:
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));
}
}
为了更改我的 WPF Window 的内容,我已经将 4 个网格(每个用于菜单的一个选项 + 起始页)的可见性绑定到此转换器,如下所示:
资源:
<Window.Resources>
<local:ScreenToShow x:Key="myScreenToShow"></local:ScreenToShow>
<local:VisibilityScreenConverter x:Key="myVisibilityScreenConverter"></local:VisibilityScreenConverter>
</Window.Resources>
XAML四格之一的代码:
<Grid DataContext="{Binding Source={StaticResource myScreenToShow}}" Name="MenuPage" Grid.Row="1" Visibility="{Binding Path= _State, Converter={StaticResource myVisibilityScreenConverter}, ConverterParameter='menuPage', UpdateSourceTrigger=Default, Mode=TwoWay}">
而转换器的代码如下:
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();
}
}
在MainWindow.cs中使用了以下代码:
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;
}
启动时,代码运行良好,NotifyPropertyChanged 和 Converter 都被调用(用 console.writeline 检查)。此外,不同网格的可见性按预期处理。
选择其中一个菜单选项时,也会按预期调用 PropertyChangedHandler。但是,不同网格的可见性不会改变。
我做错了什么,可见性在启动时根据我的代码发生变化,但在后期更改我的枚举值时却没有?
谢谢!
问题是您指的是两个完全不同的 ViewModel
对象,一个被您的 xaml 视图使用,另一个被文件隐藏代码使用。
在代码隐藏文件中设置 DataContext
属性 或在 xaml 文件中设置 suing:
<Window.DataContext>
<StaticResource ResourceKey="myScreenToShow" />
</Window.DataContext>
如果您使用第二种方法,请确保在事件处理程序中转换并使用您的 DataContext
:
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;
}
演示代码:
<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>
文件背后的代码:
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;
}
}
我正在尝试根据枚举值更改我的 PDF 中的哪个网格可见。枚举本身的价值基于菜单,选择菜单的选项时,枚举值更改和属性ChangeDeventHandler被调用。
包含PropertyChangedEventHandler的class代码如下:
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));
}
}
为了更改我的 WPF Window 的内容,我已经将 4 个网格(每个用于菜单的一个选项 + 起始页)的可见性绑定到此转换器,如下所示: 资源:
<Window.Resources>
<local:ScreenToShow x:Key="myScreenToShow"></local:ScreenToShow>
<local:VisibilityScreenConverter x:Key="myVisibilityScreenConverter"></local:VisibilityScreenConverter>
</Window.Resources>
XAML四格之一的代码:
<Grid DataContext="{Binding Source={StaticResource myScreenToShow}}" Name="MenuPage" Grid.Row="1" Visibility="{Binding Path= _State, Converter={StaticResource myVisibilityScreenConverter}, ConverterParameter='menuPage', UpdateSourceTrigger=Default, Mode=TwoWay}">
而转换器的代码如下:
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();
}
}
在MainWindow.cs中使用了以下代码:
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;
}
启动时,代码运行良好,NotifyPropertyChanged 和 Converter 都被调用(用 console.writeline 检查)。此外,不同网格的可见性按预期处理。 选择其中一个菜单选项时,也会按预期调用 PropertyChangedHandler。但是,不同网格的可见性不会改变。 我做错了什么,可见性在启动时根据我的代码发生变化,但在后期更改我的枚举值时却没有?
谢谢!
问题是您指的是两个完全不同的 ViewModel
对象,一个被您的 xaml 视图使用,另一个被文件隐藏代码使用。
在代码隐藏文件中设置 DataContext
属性 或在 xaml 文件中设置 suing:
<Window.DataContext>
<StaticResource ResourceKey="myScreenToShow" />
</Window.DataContext>
如果您使用第二种方法,请确保在事件处理程序中转换并使用您的 DataContext
:
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;
}
演示代码:
<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>
文件背后的代码:
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;
}
}