.net - 如何解决单选按钮组的绑定(bind)问题

标签 .net wpf vb.net data-binding

如果您在数据上下文发生变化的控件内创建单选按钮组。当您将数据上下文从后来定义的单选按钮为 true 的条目更改为 false 但之前定义的单选按钮为 true 的条目时,原始项目的绑定(bind)值会更新为 false。

您如何解决这个问题? (虽然代码是用 VB 编写的,但它可以在任何风格的 .net 中工作。我使用 dotnet 4.5.2 进行复制)

您可以在 github 上找到最小问题解决方案 https://github.com/PhoenixStoneham/RadioButtonGroupBinding

主窗口

    <Window x:Class="MainWindow"
        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:WPFRadioButtonGroupBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <ListBox ItemsSource="{Binding Durations}" DisplayMemberPath="Name" SelectedItem="{Binding SelectedDuration}"/>
        <Grid Grid.Column="1" DataContext="{Binding SelectedDuration}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Label Content="Name"/>
            <TextBlock Text="{Binding Name}" Grid.Column="1"/>
            <Label Content="Duration" Grid.Row="1"/>
            <TextBox Text="{Binding Frequency}" Grid.Row="1" Grid.Column="1"/>
            <RadioButton GroupName="DurationType" IsChecked="{Binding Hourly}" Grid.Row="2" Grid.Column="1" Content="Hours"/>
            <RadioButton GroupName="DurationType" IsChecked="{Binding Daily}" Grid.Row="3" Grid.Column="1" Content="Days"/>
            <RadioButton GroupName="DurationType" IsChecked="{Binding Weekly}" Grid.Row="4" Grid.Column="1" Content="Weeks"/>
            <RadioButton GroupName="DurationType" IsChecked="{Binding Monthly}" Grid.Row="5" Grid.Column="1" Content="Months"/>
        </Grid>
    </Grid>
</Window>

主窗口 View 模型

    Imports System.Collections.ObjectModel
Imports System.ComponentModel

Public Class MainWindowViewModel
    Implements INotifyPropertyChanged

    Public ReadOnly Property Durations As ObservableCollection(Of DurationViewModel)
    Public Sub New()
        Durations = New ObservableCollection(Of DurationViewModel)
        Durations.Add(New DurationViewModel("Daily", 1, False, True, False, False))
        Durations.Add(New DurationViewModel("Weekly", 1, False, False, True, False))
        Durations.Add(New DurationViewModel("Fortnightly", 1, False, False, True, False))
        Durations.Add(New DurationViewModel("Monthly", 1, False, False, False, True))
        Durations.Add(New DurationViewModel("1/2 yearly", 6, False, False, False, True))
        Durations.Add(New DurationViewModel("Other Days", 2, False, True, False, False))
        Durations.Add(New DurationViewModel("Take Over", 1, True, False, False, False))
        Durations.Add(New DurationViewModel("1/2 Day Takeover", 12, True, False, False, False))

    End Sub
    Private _SelectedDuration As DurationViewModel
    Public Property SelectedDuration As DurationViewModel
        Get
            Return _SelectedDuration
        End Get
        Set(value As DurationViewModel)
            _SelectedDuration = value
            DoPropertyChanged("SelectedDuration")
        End Set
    End Property

    Public Sub DoPropertyChanged(name As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
    End Sub
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
End Class

持续时间 View 模型

Imports System.ComponentModel

Public Class DurationViewModel
    Implements INotifyPropertyChanged

    Private _Name As String
    Public Property Name As String
        Get
            Return _Name
        End Get
        Set(value As String)
            _Name = value
            DoPropertyChanged("Name")
        End Set
    End Property
    Private _Hourly As Boolean
    Public Property Hourly As Boolean
        Get
            Return _Hourly
        End Get
        Set(value As Boolean)
            _Hourly = value
            DoPropertyChanged("Hourly")
        End Set
    End Property

    Private _Daily As Boolean
    Public Property Daily As Boolean
        Get
            Return _Daily
        End Get
        Set(value As Boolean)
            _Daily = value
            DoPropertyChanged("Daily")
        End Set
    End Property
    Private _Weekly As Boolean
    Public Property Weekly As Boolean
        Get
            Return _Weekly
        End Get
        Set(value As Boolean)
            _Weekly = value
            DoPropertyChanged("Weekly")
        End Set
    End Property
    Private _Monthly As Boolean
    Public Property Monthly As Boolean
        Get
            Return _Monthly
        End Get
        Set(value As Boolean)
            _Monthly = value
            DoPropertyChanged("Monthly")
        End Set
    End Property

    Public Sub New(name As String, frequency As Integer, hourly As Boolean, daily As Boolean, weekly As Boolean, monthly As Boolean)
        Me.Name = name
        Me.Frequency = frequency
        Me.Hourly = hourly
        Me.Daily = daily
        Me.Weekly = weekly
        Me.Monthly = monthly
    End Sub
    Private _Frequency As Integer
    Public Property Frequency As Integer
        Get
            Return _Frequency
        End Get
        Set(value As Integer)
            _Frequency = value
            DoPropertyChanged("Frequency")
        End Set
    End Property
    Public Sub DoPropertyChanged(name As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(name))
    End Sub
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
End Class

最佳答案

绑定(bind)到单选按钮组的最简洁方法是为每个组定义一个枚举类型,然后使用值转换器进行绑定(bind)。

[抱歉,我的代码示例是 C# 语言,但您应该能够轻松地将其转换为 VB.Net。]

public enum MyEnum
{
    A,
    B,
    C
}

.

public class EnumToBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return parameter != null && parameter.Equals(value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value != null && value.Equals(true) ? parameter : DependencyProperty.UnsetValue;
    }
} 

然后在ViewModel中,定义一个枚举类型的属性

public class MainViewModel: ViewModelBase
{
    private MyEnum _e;

    public MyEnum E
    {
        get => _e;
        set => Set(nameof(E), ref _e, value);
    }
}

并使用转换器绑定(bind)在 View 中

<Window ...>
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>

    <Window.Resources>
        <local:EnumToBoolConverter x:Key="EnumConverter"/>
    </Window.Resources>

    <StackPanel>
        <RadioButton 
            Content="A"
            IsChecked="{Binding Path=E, Converter={StaticResource EnumConverter}, ConverterParameter={x:Static local:MyEnum.A}}" />

        <RadioButton 
            Content="B"
            IsChecked="{Binding Path=E, Converter={StaticResource EnumConverter}, ConverterParameter={x:Static local:MyEnum.B}}" />

        <RadioButton 
            Content="C"
            IsChecked="{Binding Path=E, Converter={StaticResource EnumConverter}, ConverterParameter={x:Static local:MyEnum.C}}" />

        <TextBlock Text="{Binding E}"/>
    </StackPanel>
</Window>

关于.net - 如何解决单选按钮组的绑定(bind)问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61890512/

相关文章:

.net - 频繁更改数据库的最佳 .NET 解决方案

wpf - 确保 WPF 窗口始终位于顶部,即使用户单击另一个最大化的应用程序也是如此

.net - Service Fabric - 使用 app.config 和 settings.xml 的区别?

c# - .NET 2 Windows 服务安装项目 (MSI) 问题

wpf - 在 WPF FlowDocument 中重复数据绑定(bind)项

c# - 在 WPF 中启动 XAML 动画

vb.net - 在 vb.Net 中实现继承的泛型接口(interface)

vb.net - 创建一个在另一个线程上运行的进度条,同时在主线程中保持计算

c# - 单击表单上任何数据 GridView 的保存按钮时提交当前脏单元格

c# - 如何复制与安装程序位于同一目录中的文件