WPF与Prism问题:多UserControl中RadioButton选中状态丢失
这个问题我之前也碰到过,核心原因是多个TestControl实例共享了同一个TestViewModel实例,导致所有窗口的单选按钮绑定的是同一个SelectedMode属性——当新窗口的单选按钮触发ConvertBack更新属性值时,之前打开的窗口会同步更新选中状态,自然就取消了之前的选中效果。
为什么会共用ViewModel?看你的TestControl XAML代码,你直接在<UserControl.DataContext>里硬编码创建了<vm:TestViewModel/>,如果这个控件被Prism的PopupWindowAction复用,或者你的ViewModel被注册成了单例,就会导致所有TestControl用同一个ViewModel实例。另外,如果你的EnumToBooleanConverter内部维护了静态状态,也可能引发这个问题,但最常见的还是ViewModel实例共享的问题。
解决办法
1. 给每个弹出窗口分配独立的ViewModel(推荐)
不要在TestControl的XAML里直接设置DataContext,而是在弹出窗口时动态创建新的ViewModel实例:
步骤1:修改TestControl XAML
删掉硬编码的DataContext声明:
<!-- 移除这部分代码 --> <UserControl.DataContext> <vm:TestViewModel/> </UserControl.DataContext>
步骤2:修改MainViewModel的触发逻辑
在弹出窗口前,创建新的TestViewModel实例,并把它作为Confirmation的Content传递:
private void OnClicked(object obj) { int index = Convert.ToInt32(obj); // 每次弹出都创建新的ViewModel实例 var testVm = new TestViewModel(); var confirmation = new Confirmation() { Content = testVm, Title = $"Bla {index}" }; switch (index) { case 1: StretchModeRequest1.Raise(confirmation); break; case 2: StretchModeRequest2.Raise(confirmation); break; case 3: StretchModeRequest3.Raise(confirmation); break; } }
步骤3:绑定TestControl的DataContext
在PopupWindowAction里,把TestControl的DataContext绑定到Confirmation的Content:
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True"> <prism:PopupWindowAction.WindowContent> <local:TestControl DataContext="{Binding Content}"/> </prism:PopupWindowAction.WindowContent> </prism:PopupWindowAction>
这样每个弹出窗口都会拥有独立的ViewModel,SelectedMode属性互不干扰,自然就不会出现之前的选中状态被取消的问题。
2. 确保ViewModel不是单例
如果你用了Prism的依赖注入容器,检查TestViewModel的注册方式,确保它是瞬态(每次请求创建新实例):
// 在Module或App.xaml.cs的RegisterTypes方法中 containerRegistry.Register<TestViewModel>(); // 绝对不要用 RegisterSingleton<TestViewModel>();
3. 检查EnumToBooleanConverter的实现
确保转换器是无状态的,不要维护任何静态变量,正确的实现应该是这样的:
public class EnumToBooleanConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null || parameter == null) return false; return value.Equals(parameter); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { if (value is bool isChecked && isChecked && parameter != null) { return Enum.Parse(targetType, parameter.ToString()); } return Binding.DoNothing; } }
验证方法
你可以在TestViewModel的构造函数里加个断点,每次弹出窗口时如果都能触发断点,说明每个窗口都有独立的ViewModel;如果只触发一次,那就是共用了同一个实例,需要调整创建方式。
内容的提问来源于stack exchange,提问作者alex555

