简体   繁体   English

如何将 MainWindow.xaml 中的 8 个复选框绑定到 ViewModel 的字节属性的每一位?

[英]How can I bind 8 checkboxes in MainWindow.xaml to each bit of a byte property of ViewModel?

I am fairly new to WPF and MVVM.我对 WPF 和 MVVM 相当陌生。 What I'm trying to achieve is that I have eight checkboxes in my GUI which I need to convert to a single byte value so that I can serially transmit it at any given time.我想要实现的是我的 GUI 中有八个复选框,我需要将它们转换为单个字节值,以便我可以在任何给定时间串行传输它。 Will I have to create 8 bool properties and then convert them to a byte property?我是否必须创建 8 个 bool 属性,然后将它们转换为字节属性?

This is my XAML currently.这是我目前的 XAML。 I have binded each checkbox individually to a byte property.我已将每个复选框单独绑定到一个字节属性。 Can I bind them all to one property somehow to make a byte where each checkbox represents a bit.我可以以某种方式将它们全部绑定到一个属性以创建一个字节,其中每个复选框代表一个位。

<Window x:Class="ModelClassTest.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Title="{Binding Title}" Height="350" Width="525">
    <Grid>
        <ContentControl prism:RegionManager.RegionName="ContentRegion" />
        <CheckBox Margin="10,10,0,0"  Content="Bit 0" IsChecked="{Binding Path Bit0}"/>
        <CheckBox Margin="10,30,0,0" Content="Bit 1" IsChecked="{Binding Path Bit1}"/>
        <CheckBox Margin="10,50,0,0" Content="Bit 2" IsChecked="{Binding Path Bit2}"/>
        <CheckBox Margin="10,70,0,0" Content="Bit 3" IsChecked="{Binding Path Bit3}"/>
        <CheckBox Margin="10,90,0,0" Content="Bit 4" IsChecked="{Binding Path Bit4}"/>
        <CheckBox Margin="10,110,0,0" Content="Bit 5" IsChecked="{Binding Path Bit5}"/>
        <CheckBox Margin="10,130,0,0" Content="Bit 6" IsChecked="{Binding Path Bit6}"/>
        <CheckBox Margin="10,150,0,0" Content="Bit 7" IsChecked="{Binding Path Bit7}"/>
        <Button Width="100" Height="20" Margin="10,173,408.723,128.076" Content="Transmit" Command="{Binding ClickMe}"/>
    </Grid>
</Window>

Instead of creating the checkboxes manually, you could create an ItemsControl for all the bits of your byte.您可以为字节的所有位创建一个ItemsControl ,而不是手动创建复选框。 Represent the byte with a list of single bits.用单个位列表表示字节。

Create a viewmodel class for the sigle bit:为单个位创建一个视图模型类:

// The ViewModelBase base class implements INotifyPropertyChanged
// and provides the SetProperty method
class Bit : ViewModelBase 
{
    private bool _isSet;

    public Bit(int bitNumber)
    {
        BitNumber = bitNumber;
    }

    public int BitNumber { get; }

    public bool IsSet
    {
        get => _isSet;
        set => SetProperty(ref _isSet, value);
    }
}

Here is your byte in the main viewmodel:这是主视图模型中的字节:

public IReadOnlyCollection<Bit> Byte { get; }
    = new List<Bit>(Enumerable.Range(0, 8).Select(v => new Bit(v)));

In the main viewmodel, create an ICommand that will convert your list of bits to a byte and use this value.在主视图模型中,创建一个ICommand ,它将您的位列表转换为一个字节并使用该值。 Alternatively, create a property of type byte that will calculate the value on-the-fly based on the single bits.或者,创建一个byte类型的属性,该属性将根据单个位即时计算值。 Here is how the implementation could look like:下面是实现的样子:

void UseByte()
{
    var byteValue = Byte.Select((v, i) => (1 << i) * (v.IsSet ? 1 : 0)).Sum();

    // use the value as needed
}

And this is how your view could look like:这就是您的视图的样子:

<ItemsControl ItemsSource="{Binding Byte}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <CheckBox IsChecked="{Binding IsSet}">
                <TextBlock>
                    <Run Text="Bit "/><Run Text="{Binding BitNumber, Mode=OneTime}"/>
                </TextBlock>
            </CheckBox>        
         </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

You just have to use the binding with a converter attached in order to read the byte value from the view model and a ICommand to toggle a specific bit in the byte:您只需要使用带有转换器的绑定,以便从视图模型中读取字节值,并使用ICommand来切换字节中的特定位:

ByteToBitConverter.cs ByteToBitConverter.cs

class ByteToBiteConverter : IValueConverter
{    
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture) => 
    value is byte byteValue 
      && int.TryParse(parameter.ToString(), out int bitPosition)
        ? new BitArray(new Byte[] {byteValue}).Get(bitPosition - 1)
        : Binding.DoNothing;

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    => throw new NotSupportedException();  
}

ViewModel.cs视图模型.cs

class ViewModel : INotifyPropertyChanged
{ 
  private byte byteValue;
  public byte ByteValue 
  {
    get => this.byteValue;
    set
    {
      this.byteValue = value;
      OnPropertyChanged();
    }
  }

  public ICommand ToggleBitCommand => new RelayCommand(ToggleBit);
  public int BitCount { get; set; }

  public ViewModel()
  {
    this.ByteValue = 128;
  }

  private void ToggleBit(object commandParameter)
  {
    if (!int.TryParse(commandParameter.ToString(), out int bitPosition))
    {
      return;
    }

    var bytes = new Byte[] { this.ByteValue };
    var boolArray = new bool[this.BitCount];
    var bits = new BitArray(bytes);
    int toggleBitIndex = bitPosition - 1;

    bits.Set(toggleBitIndex, !bits.Get(toggleBitIndex));

    bytes = new Byte[1];
    bits.CopyTo(bytes, 0);
    this.ByteValue = bytes.FirstOrDefault();
  }

  #region INotifyPropertyChanged

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
    this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }  

  #endregion
}

RelayCommand.cs中继命令.cs
(Implementation taken from Microsoft Docs: Relaying Command Logic ) (实现取自Microsoft Docs: Relaying Command Logic

public class RelayCommand : ICommand
{
    #region Fields 
    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;
    #endregion // Fields 
    #region Constructors 
    public RelayCommand(Action<object> execute) : this(execute, null) { }
    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");
        _execute = execute; _canExecute = canExecute;
    }
    #endregion // Constructors 
    #region ICommand Members 
    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    public void Execute(object parameter) { _execute(parameter); }
    #endregion // ICommand Members 
}

MainWindow.xaml主窗口.xaml

<Window>
  <Window.DataContext>
    <ByteToBitByteConverter />
  </Window.DataContext>

  <Window.Resources>
    <ViewModel />
  </Window.Resources>

  <StackPanel>

    <!-- 
      CommandParameter is the bit position. 
      Binding.Mode of IsChecked has to be explicitely set to BindingMode.OneWay
    -->
    <CheckBox x:Name="Bit0" 
              Command="{Binding ToggleBitCommand}"
              CommandParameter="1" 
              IsChecked="{Biding ByteValue, Mode=OneWay, Converter={StaticResource ByteToBitConverter}, ConverterParameter=1}" />
    <CheckBox x:Name="Bit1" 
              Command="{Binding ToggleBitCommand}"
              CommandParameter="2" 
              IsChecked="{Biding ByteValue, Mode=OneWay, Converter={StaticResource ByteToBitConverter}, ConverterParameter=2}" />
    <CheckBox x:Name="Bit2" 
              Command="{Binding ToggleBitCommand}"
              CommandParameter="3" 
              IsChecked="{Biding ByteValue, Mode=OneWay, Converter={StaticResource ByteToBitConverter}, ConverterParameter=3}" />
    <CheckBox x:Name="Bit3" 
              Command="{Binding ToggleBitCommand}"
              CommandParameter="4" 
              IsChecked="{Biding ByteValue, Mode=OneWay, Converter={StaticResource ByteToBitConverter}, ConverterParameter=4}" />
    <CheckBox x:Name="Bit4" 
              Command="{Binding ToggleBitCommand}"
              CommandParameter="5" 
              IsChecked="{Biding ByteValue, Mode=OneWay, Converter={StaticResource ByteToBitConverter}, ConverterParameter=5}" />
    <CheckBox x:Name="Bit5" 
              Command="{Binding ToggleBitCommand}"
              CommandParameter="6" 
              IsChecked="{Biding ByteValue, Mode=OneWay, Converter={StaticResource ByteToBitConverter}, ConverterParameter=6}" />
    <CheckBox x:Name="Bit6" 
              Command="{Binding ToggleBitCommand}"
              CommandParameter="7" 
              IsChecked="{Biding ByteValue, Mode=OneWay, Converter={StaticResource ByteToBitConverter}, ConverterParameter=7}" />
    <CheckBox x:Name="Bit7" 
              Command="{Binding ToggleBitCommand}"
              CommandParameter="8" 
              IsChecked="{Biding ByteValue, Mode=OneWay, Converter={StaticResource ByteToBitConverter}, ConverterParameter=8}" />
  </StackPanel>
</Window>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM