我对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>
答案 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>