使用相同的 ViewModel 打开两个窗口时无法设置单选按钮值

Posted

技术标签:

【中文标题】使用相同的 ViewModel 打开两个窗口时无法设置单选按钮值【英文标题】:Unable to Set Radio Button value when two windows are opened using the same ViewModel 【发布时间】:2013-02-12 11:08:44 【问题描述】:

我在同时打开的两个窗口中使用以下控件模板,并且都使用相同的视图模型。 这是模板;

    <ControlTemplate x:Key="SecurityTypeSelectionTemplate">
        <StackPanel>
            <RadioButton GroupName ="SecurityType"  Content="Equity" 
                     IsChecked="Binding Path=SecurityType, Mode=TwoWay, Converter=StaticResource EnumBoolConverter, ConverterParameter=Equity" />
            <RadioButton GroupName ="SecurityType" Content="Fixed Income" 
                     IsChecked="Binding Path=SecurityType, Mode=TwoWay, Converter=StaticResource EnumBoolConverter, ConverterParameter=FixedIncome" />
            <RadioButton GroupName ="SecurityType" Content="Futures" 
                     IsChecked="Binding Path=SecurityType, Mode=TwoWay, Converter=StaticResource EnumBoolConverter, ConverterParameter=Futures" />
        </StackPanel>
    </ControlTemplate>

这里是 viewmodel 属性:

    private SecurityTypeEnum _securityType;
    public SecurityTypeEnum SecurityType
    
        get  return _securityType; 
        set
        
            _securityType = value; RaisePropertyChanged("SecurityType");
        
    

这是枚举:

public enum SecurityType  Equity, FixedIncome, Futures 

这是转换器:

public class EnumToBoolConverter : IValueConverter

    public object Convert(object value, Type targetType, object enumTarget, CultureInfo culture)
    
        string enumTargetStr = enumTarget as string;
        if (string.IsNullOrEmpty(enumTargetStr))
            return DependencyProperty.UnsetValue;

        if (Enum.IsDefined(value.GetType(), value) == false)
            return DependencyProperty.UnsetValue;

        object expectedEnum = Enum.Parse(value.GetType(), enumTargetStr);

        return expectedEnum.Equals(value);
    

    public object ConvertBack(object value, Type targetType, object enumTarget, CultureInfo culture)
    
        string expectedEnumStr = enumTarget as string;
        if (expectedEnumStr == null)
            return DependencyProperty.UnsetValue;

        return Enum.Parse(targetType, expectedEnumStr);
    

这个问题有点奇怪。我有两个窗口显示 SAME ViewModel 的视图略有不同。上面显示的相同模板在两个视图中重复使用。 如果 Equity 最初设置为 SecurityType,我可以通过单击相关单选按钮将其更改为 FixedIncome。然后我无法将其更改回股权。 但是,我可以将其设置为期货。但在那之后,我无法通过单击相关单选按钮将其更改为 FixedIncome 或 Equity。 在我无法设置更改的情况下发生的情况是 Setter 被调用了两次。第一次将值设置为正确的选定值,但在触发 RaisePropertyChanged 的​​那一刻, 再次调用 setter,这次使用原始值。 感觉就像当 RaisePropertyChanged 时,setter 被第二个窗口的绑定调用,从而覆盖了用户进行选择的第一个窗口中设置的值。 有谁知道是否是这种情况以及在这种情况下如何避免?

【问题讨论】:

你的 EnumBoolConverter 是什么样的?我曾经遇到过同样的问题,发现是我的转换器搞砸了一切,但我不记得到底是什么问题。 我已编辑帖子以包含转换器。 迈克:看看我的回答。 EnumToBoolConverter 应该在 ConvertBack 中返回一个布尔值。试试看,让我知道。 【参考方案1】:

RadioButton 的默认行为是在属性更改时更新源,因此两个窗口都尝试更新源。一种解决方法是仅从用户单击的位置更新源。为此,请在绑定上使用 Binding.UpdateSourceTrigger Explicit。在 RadioButton 后面的代码中添加一个单击处理程序。在其中明确更新源代码。

        <StackPanel>
        <RadioButton GroupName ="SecurityType"  Content="Equity" 
                 IsChecked="Binding Path=SecurityType, Mode=TwoWay, Converter=StaticResource EnumToBoolConverter, UpdateSourceTrigger=Explicit, ConverterParameter=Equity" Click="RadioButton_Click" />
        <RadioButton GroupName ="SecurityType" Content="Fixed Income" 
                 IsChecked="Binding Path=SecurityType, Mode=TwoWay, Converter=StaticResource EnumToBoolConverter, UpdateSourceTrigger=Explicit, ConverterParameter=FixedIncome" Click="RadioButton_Click"/>
        <RadioButton GroupName ="SecurityType" Content="Futures" 
                 IsChecked="Binding Path=SecurityType, Mode=TwoWay, Converter=StaticResource EnumToBoolConverter, UpdateSourceTrigger=Explicit, ConverterParameter=Futures" Click="RadioButton_Click"/>
    </StackPanel>  

    private void RadioButton_Click(object sender, RoutedEventArgs e)
    
        BindingExpression be = ((RadioButton)sender).GetBindingExpression(RadioButton.IsCheckedProperty);
        be.UpdateSource();
    

您可能必须使用 UserControl 代替 ControlTemplate 或在 ControlTemplate 中使用,以便在您的视图中获取代码。

【讨论】:

谢谢。我最初尝试过这个,但我确实希望在新选择的选项时更新另一个窗口,所以这个解决方案没有为我做到这一点。 对于这种情况,EnumToBoolConverter 是答案,因为它最简单。不过,UpdateSourceTrigger 可能会帮助其他人。要点是 UpdateSourceTrigger 让您可以控制何时更新绑定源。如此处所示,当用户通过 RadioButton_Click 事件在一个窗口中选择新选择的选项时,它会特别更新其他窗口。【参考方案2】:

这是我的 EnumToBoolConverter 版本:

public class EnumToBoolConverter : BaseConverterMarkupExtension<object, bool>
    
        public override bool Convert(object value, Type targetType, object parameter)
        
            if (value == null)
                return false;

            return value.Equals(Enum.Parse(value.GetType(), (string)parameter, true));
        

        public override object ConvertBack(bool value, Type targetType, object parameter)
        
            return value.Equals(false) ? DependencyProperty.UnsetValue : parameter;
        
    

【讨论】:

是的,这解决了问题。那么在这种情况下,Convert 和 ConvertBack 都返回一个布尔值?当你想到“转换回来”这个词时,它有点不直观......但现在我不得不考虑它,我认为它是有道理的。非常感谢! 不,我的错。实际上 ConvertBack 返回 parameter 值(它实际上是一个字符串,仅在尝试将 RadioButton.IsChecked = true 转换回 Enum 时。我猜绑定框架能够自行将字符串转换为 Enum 值。跨度>

以上是关于使用相同的 ViewModel 打开两个窗口时无法设置单选按钮值的主要内容,如果未能解决你的问题,请参考以下文章

ViewModel 不显示

如何将相同的viewmodel设置为xamarin表单中的新mvvm中的两个视图

如何在MVVM中使用相同的ViewModel拥有多个视图?

有没有办法在 Xcode 7 的两个不同窗口中打开和查看情节提要?

SHOWMODALDIALOG表单提交时禁止打开新窗口

Flutter ViewModel 旧数据正在重新填充(使用 Provider)