WPF 和 Prism:RadioButton 选择在多个 UserControl 上消失
WPF & Prism: RadioButton selection disappears on multiple UserControls
我在多个用户控件中使用 Prism 的 InteractionRequestTrigger
来显示使用我自己的 window 内容的通知,例如TestControl
包含三个RadioButtons
。在 TestControl
的 VM 创建时间中,我将选择设置为第一个单选按钮。
问题是,当显示 last window 时,单选按钮是
选择应该的样子。但是之前创建的所有其他 windows 都没有任何选择。我发现我的 EnumToBooleanConverter
的 ConvertBack
函数被调用并在下一个用户控件的第一个单选按钮被选中时再次取消选择它。等等……你知道为什么吗?如何解决这个问题?
这是重现此问题的简单代码:
主窗口:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:prism="http://www.codeplex.com/prism"
xmlns:local="clr-namespace:WpfApp1"
xmlns:vm="clr-namespace:WpfApp1.ViewModels" Title="MainWindow">
<StackPanel>
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding StretchModeRequest1}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<local:TestControl/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
<prism:InteractionRequestTrigger SourceObject="{Binding StretchModeRequest2}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<local:TestControl/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
<prism:InteractionRequestTrigger SourceObject="{Binding StretchModeRequest3}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<local:TestControl/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
<Button Content="Command 1" Margin="5" Command="{Binding ClickCommand}" CommandParameter="1"/>
<Button Content="Command 2" Margin="5" Command="{Binding ClickCommand}" CommandParameter="2"/>
<Button Content="Command 3" Margin="5" Command="{Binding ClickCommand}" CommandParameter="3"/>
</StackPanel>
MainViewModel:
public class ViewModel : BindableBase
{
public InteractionRequest<Confirmation> StretchModeRequest1 { get; private set; }
public InteractionRequest<Confirmation> StretchModeRequest2 { get; private set; }
public InteractionRequest<Confirmation> StretchModeRequest3 { get; private set; }
public ICommand ClickCommand { get; private set; }
public ViewModel()
{
StretchModeRequest1 = new InteractionRequest<Confirmation>();
StretchModeRequest2 = new InteractionRequest<Confirmation>();
StretchModeRequest3 = new InteractionRequest<Confirmation>();
ClickCommand = new DelegateCommand<object>(OnClicked);
}
private void OnClicked(object obj)
{
int index = Convert.ToInt32(obj);
var confirmation = new Confirmation() { Content = "Test", Title = string.Format("Bla {0}", index) };
switch (index)
{
case 1: StretchModeRequest1.Raise(confirmation); break;
case 2: StretchModeRequest2.Raise(confirmation); break;
case 3: StretchModeRequest3.Raise(confirmation); break;
default:
break;
}
}
}
测试控制:
<UserControl x:Class="WpfApp1.TestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
xmlns:vm="clr-namespace:WpfApp1.ViewModels">
<UserControl.Resources>
<ResourceDictionary>
<local:EnumToBooleanConverter x:Key="EnumToBooleanConverter"/>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<vm:TestViewModel/>
</UserControl.DataContext>
<StackPanel Margin="3,3,3,0">
<RadioButton Margin="2" GroupName="Mode" Content="None" IsChecked="{Binding SelectedMode,
diag:PresentationTraceSources.TraceLevel=High, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.None}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<RadioButton Margin="2" GroupName="Mode" Content="Limits" IsChecked="{Binding SelectedMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.Limits}, Mode=TwoWay}"/>
<RadioButton Margin="2" GroupName="Mode" Content="Static" IsChecked="{Binding SelectedMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.Static}, Mode=TwoWay}"/>
</StackPanel>
测试视图模型:
public class TestViewModel : BindableBase
{
private StretchType _stretchMode;
public TestViewModel()
{
SelectedMode = StretchType.None;
}
public StretchType SelectedMode
{
get { return _stretchMode; }
set { SetProperty(ref _stretchMode, value); }
}
}
转换器:
public class EnumToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value) ? parameter : Binding.DoNothing;
}
}
您应该为每个视图使用唯一的 GroupName
,例如:
<RadioButton Margin="2" GroupName="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" Content="None" IsChecked="{Binding SelectedMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.None}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<RadioButton Margin="2" GroupName="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" Content="Limits" IsChecked="{Binding SelectedMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.Limits}, Mode=TwoWay}"/>
<RadioButton Margin="2" GroupName="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" Content="Static" IsChecked="{Binding SelectedMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.Static}, Mode=TwoWay}"/>
我在多个用户控件中使用 Prism 的 InteractionRequestTrigger
来显示使用我自己的 window 内容的通知,例如TestControl
包含三个RadioButtons
。在 TestControl
的 VM 创建时间中,我将选择设置为第一个单选按钮。
问题是,当显示 last window 时,单选按钮是
选择应该的样子。但是之前创建的所有其他 windows 都没有任何选择。我发现我的 EnumToBooleanConverter
的 ConvertBack
函数被调用并在下一个用户控件的第一个单选按钮被选中时再次取消选择它。等等……你知道为什么吗?如何解决这个问题?
这是重现此问题的简单代码:
主窗口:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:prism="http://www.codeplex.com/prism"
xmlns:local="clr-namespace:WpfApp1"
xmlns:vm="clr-namespace:WpfApp1.ViewModels" Title="MainWindow">
<StackPanel>
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding StretchModeRequest1}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<local:TestControl/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
<prism:InteractionRequestTrigger SourceObject="{Binding StretchModeRequest2}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<local:TestControl/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
<prism:InteractionRequestTrigger SourceObject="{Binding StretchModeRequest3}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<local:TestControl/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
<Button Content="Command 1" Margin="5" Command="{Binding ClickCommand}" CommandParameter="1"/>
<Button Content="Command 2" Margin="5" Command="{Binding ClickCommand}" CommandParameter="2"/>
<Button Content="Command 3" Margin="5" Command="{Binding ClickCommand}" CommandParameter="3"/>
</StackPanel>
MainViewModel:
public class ViewModel : BindableBase
{
public InteractionRequest<Confirmation> StretchModeRequest1 { get; private set; }
public InteractionRequest<Confirmation> StretchModeRequest2 { get; private set; }
public InteractionRequest<Confirmation> StretchModeRequest3 { get; private set; }
public ICommand ClickCommand { get; private set; }
public ViewModel()
{
StretchModeRequest1 = new InteractionRequest<Confirmation>();
StretchModeRequest2 = new InteractionRequest<Confirmation>();
StretchModeRequest3 = new InteractionRequest<Confirmation>();
ClickCommand = new DelegateCommand<object>(OnClicked);
}
private void OnClicked(object obj)
{
int index = Convert.ToInt32(obj);
var confirmation = new Confirmation() { Content = "Test", Title = string.Format("Bla {0}", index) };
switch (index)
{
case 1: StretchModeRequest1.Raise(confirmation); break;
case 2: StretchModeRequest2.Raise(confirmation); break;
case 3: StretchModeRequest3.Raise(confirmation); break;
default:
break;
}
}
}
测试控制:
<UserControl x:Class="WpfApp1.TestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
xmlns:vm="clr-namespace:WpfApp1.ViewModels">
<UserControl.Resources>
<ResourceDictionary>
<local:EnumToBooleanConverter x:Key="EnumToBooleanConverter"/>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<vm:TestViewModel/>
</UserControl.DataContext>
<StackPanel Margin="3,3,3,0">
<RadioButton Margin="2" GroupName="Mode" Content="None" IsChecked="{Binding SelectedMode,
diag:PresentationTraceSources.TraceLevel=High, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.None}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<RadioButton Margin="2" GroupName="Mode" Content="Limits" IsChecked="{Binding SelectedMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.Limits}, Mode=TwoWay}"/>
<RadioButton Margin="2" GroupName="Mode" Content="Static" IsChecked="{Binding SelectedMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.Static}, Mode=TwoWay}"/>
</StackPanel>
测试视图模型:
public class TestViewModel : BindableBase
{
private StretchType _stretchMode;
public TestViewModel()
{
SelectedMode = StretchType.None;
}
public StretchType SelectedMode
{
get { return _stretchMode; }
set { SetProperty(ref _stretchMode, value); }
}
}
转换器:
public class EnumToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value) ? parameter : Binding.DoNothing;
}
}
您应该为每个视图使用唯一的 GroupName
,例如:
<RadioButton Margin="2" GroupName="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" Content="None" IsChecked="{Binding SelectedMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.None}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<RadioButton Margin="2" GroupName="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" Content="Limits" IsChecked="{Binding SelectedMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.Limits}, Mode=TwoWay}"/>
<RadioButton Margin="2" GroupName="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}" Content="Static" IsChecked="{Binding SelectedMode, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static vm:StretchType.Static}, Mode=TwoWay}"/>