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

时间:2020-03-13 10:37:10

标签: c# wpf mvvm

我对WPF和MVVM相当陌生。我要实现的目标是,我的GUI中有八个复选框,需要将其转换为一个字节值,以便可以在任何给定时间串行传输它。我是否必须创建8个bool属性,然后将它们转换为字节属性?

这是我当前的XAML。我已经将每个复选框分别绑定到一个byte属性。我可以将它们全部绑定到一个属性,以使每个复选框代表一位的字节。

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

2 个答案:

答案 0 :(得分:2)

您可以为字节的所有位创建一个ItemsControl,而不是手动创建复选框。用单比特列表表示该字节。

为单个位创建一个viewmodel类:

// 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);
    }
}

这是您在主视图模型中的字节:

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

在主视图模型中,创建一个ICommand,它将把您的位列表转换为字节并使用此值。或者,创建类型byte的属性,该属性将基于单个位即时计算值。这是实现的样子:

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

    // use the value as needed
}

这就是您的视图的样子:

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

答案 1 :(得分:1)

您只需要使用带有附加转换器的绑定即可从视图模型中读取字节值,并使用ICommand来切换字节中的特定位:

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

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
(实施取自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

<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>
相关问题