WPF:如何将数字显示为复选框的位字段

Spa*_*rky 1 c# data-binding wpf xaml

我一直在寻找一种方法,将数字存储为对象的属性,并将其位模式显示为 Windows Presentation Foundation 中的一系列复选框。这是我现在使用的方法,当选中复选框时数值更新,当数字更改时复选框更新。请告诉我是否可以通过更简单的方式实现这一点。

MainWindow.xaml.cs

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Media;
using System.Windows;
using System.Windows.Controls;
namespace WPF_Interface_Tests
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public BitField BF { get; set; }
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            BF = new BitField(0, 32);
        }

        private void Plus_Click(object sender, RoutedEventArgs e)
        {
            if (BF.Value < uint.MaxValue)
                BF.Value += 1;
        }

        private void Minus_Click(object sender, RoutedEventArgs e)
        {
            if (BF.Value > uint.MinValue)
                BF.Value -= 1;
        }

        private void CheckBox_Click(object sender, RoutedEventArgs e)
        {
            SystemSounds.Beep.Play();
            var a = (CheckBox)sender;
            int checkity = 0;
            if (a.IsChecked.Value)
                checkity = 1;
            BF.Value = BF.Value | (uint)(checkity << (int)a.Tag);
        }
    }
    public class Bit : INotifyPropertyChanged
    {
        private bool state;
        public bool State { get { return state; } set { if (this.state != value) { this.state = value; NotifyPropertyChanged("State"); } } }
        public int Index { get; set; }
        public Bit()
        {
            State = false;
            Index = 0;
        }

        public event PropertyChangedEventHandler PropertyChanged; public void NotifyPropertyChanged(string propName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); }
    }
    public class BitField : INotifyPropertyChanged
    {
        private uint value;
        public uint Value { get { return value; } set { if (this.value != value) { this.value = value; NotifyPropertyChanged("Value"); UpdateValues(); } } }
        public byte ByteSize { get; set; }
        public ObservableCollection<Bit> Values { get; set; }
        public BitField(uint givenValue, byte givenByteSize)
        {
            Value = givenValue;
            ByteSize = givenByteSize;
            Values = new ObservableCollection<Bit>();
            for (int i = 0; i < ByteSize; i++)
            {
                if ((Value | (uint)(1 << i)) == Value)
                    Values.Add(new Bit { State = true, Index = i });
                else
                    Values.Add(new Bit { State = false, Index = i });
            }
        }
        private void UpdateValues()
        {
            for (int i = 0; i < Values.Count; i++)
            {
                if ((Value | (uint)(1 << i)) == Value)
                    Values[i].State = true;
                else
                    Values[i].State = false;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged; public void NotifyPropertyChanged(string propName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); }
    }
}
Run Code Online (Sandbox Code Playgroud)

主窗口.xaml

<Window x:Class="WPF_Interface_Tests.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:WPF_Interface_Tests"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="10,201,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Text="{Binding Path=BF.Value, UpdateSourceTrigger=PropertyChanged}"/>
        <Button x:Name="Minus" Content="-" HorizontalAlignment="Left" Margin="10,229,0,0" VerticalAlignment="Top" Width="20" Click="Minus_Click" />
        <ListBox x:Name="LBofChecks" HorizontalAlignment="Left" Height="186" Margin="10,10,0,0" VerticalAlignment="Top" Width="100" ItemsSource="{Binding Path=BF.Values, UpdateSourceTrigger=PropertyChanged}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding Path=State, Mode=TwoWay}" Tag="{Binding Path=Index}" Click="CheckBox_Click"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <Button x:Name="Plus" Content="+" HorizontalAlignment="Left" Margin="35,229,0,0" VerticalAlignment="Top" Width="20" Click="Plus_Click" />

    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

我的方法涉及使用一个新颖的BitField对象作为主窗口的属性。我设置DataContext = this并构建该BitField属性。

BitField当其Value属性发生变化时发出通知。该Value属性存储表示位序列的数字。对于用作绑定到复选框的属性的值列表,我给出了BitField一个ObservableCollection<Bit> Values属性。对象集合Bit还让每个对象在其Statebool 属性更改时发出通知。我还将对象所在的Bit每个存储在 中。您可以看到,在.xaml 文件的模板中,我将复选框的属性与其 .xaml 文件的相关联。IndexBitObservableCollection<Bit> ValuesCheckBoxTagIndexBit

也许有更简单的方法吗?

小智 5

在 ViewModel 方面,我只公开一个用于绑定的整数/字节属性 - 例如,int 表示 32 位值。将其称为“MyValue”以供讨论。是的,改变时仍然通知。

在视图方面,我可以通过两种方式将每个复选框的“IsChecked”属性绑定到 MyValue。对于“位”绑定,您将使用转换器,例如将其称为BinaryValueToBitConverter

转换器采用 int (或字节等)并将其转换为每个复选框的 true false。关键是对于每个复选框,您都使用复选框的位位置作为ConverterParameter转换器的 Convert() 方法:

return (value & (1 << bitParam)) != 0;
Run Code Online (Sandbox Code Playgroud)

其中“bitParam”是传递的ConverterParameter(当然)。

您可能会找到一种方法来使用 CheckBox 的父控件的子索引,例如 StackPanel/ListBox 作为 ConverterParameter。

希望有帮助