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

时间:2017-06-13 02:46:18

标签: c# wpf xaml data-binding

我一直在寻找一种方法来存储数字作为对象的属性,并在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)); }
    }
}

MainWindow.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>

我的方法涉及使用新颖的BitField对象作为主窗口的属性。我设置DataContext = this然后构造BitField属性。

BitField通知其Value属性何时更改。 Value属性存储表示位序列的数字。对于要用作绑定到复选框的属性的值列表,我给BitField一个ObservableCollection<Bit> Values属性。 Bit个对象的集合也会在其State bool属性发生更改时通知每个对象。我还存储Bit Index Bit对象在ObservableCollection<Bit> Values内的每个CheckBox。您可以在.xaml文件的Tag模板中看到,我将复选框的Index属性与其Bit的{​​{1}}相关联。

是否有更简单的方法?

1 个答案:

答案 0 :(得分:4)

在ViewModel方面,我只是暴露一个整数/字节属性进行绑定 - 例如int为32位值。称之为&#34; MyValue&#34;讨论。是的,当它发生变化时仍然会通知。

在View方面,我会双向绑定&#34; IsChecked&#34;每个Checkbox权限到MyValue的属性。对于&#34; bit&#34;绑定你会使用转换器,例如称之为BinaryValueToBitConverter

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

return (value & (1 << bitParam)) != 0;

在哪里&#34; bitParam&#34;是传递的ConverterParameter(当然)。

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

希望有用