我想创建一个允许操作字节中特定位的WPF控件(标记附加到此帖的底部)。
应该按如下方式使用
<ns:BitManipulator BitByte={Binding Path=...} />
但我无法弄清楚如何组织(多)绑定以保持以下三个值相同: a)BitByte将绑定的模型中的字节值 b)BitByte的字节值,如果模型的值或BitVector的值发生变化,它们应更新其值 c)名为 order_i
的文本框中BitByte的位表示任何暗示赞赏
XAML
<UserControl x:Class="WpfLib.StackOverflow.BitManipulator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Name="order_0" Grid.Column="0" />
<TextBox Name="order_1" Grid.Column="1" />
<TextBox Name="order_2" Grid.Column="2" />
<TextBox Name="order_3" Grid.Column="3" />
<TextBox Name="order_4" Grid.Column="4" />
<TextBox Name="order_5" Grid.Column="5" />
<TextBox Name="order_6" Grid.Column="6" />
<TextBox Name="order_8" Grid.Column="7" />
</Grid>
</UserControl>
C#代码落后
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
namespace WpfLib.StackOverflow
{
[ContentProperty("BitByte")]
public partial class BitManipulator : UserControl
{
public static DependencyProperty BitByteProperty =
DependencyProperty.Register
(
"BitByte",
typeof(Byte),
typeof(BitManipulator),
new PropertyMetadata(null)
);
public BitManipulator()
{
InitializeComponent();
}
public Byte BitByte
{
get { return (Byte)GetValue(BitByteProperty); }
set { SetValue(BitByteProperty, value); }
}
}
}
答案 0 :(得分:1)
很抱歉。但似乎在 CollectionChanged 事件中MSDN上存在错误,并且它没有针对单个项目更改而触发。 对于你的情况草图,我只留下一些样本。 XAML用于可绑定的文本框集合
<ItemsControl ItemsSource="{Binding Bits}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
BindableBit类,用于将各个项目的更改触发到上一级。
public class BindableBit : INotifyPropertyChanged {
private int bit;
private readonly int index;
public Action<int, int> ChangedAction;
public int Value {
get { return bit; }
set {
bit = value;
OnPropertyChanged();
if (ChangedAction != null) ChangedAction(index, bit);
}
}
public BindableBit(int index) {
this.index = index;
}
...PropertyChangedImplementation
}
MainCode,我们以位为单位创建基本表示,然后在集合内的每次更改中更改 myByte 值。
public partial class MyCustomBitsControl: UserControl, INotifyPropertyChanged {
private byte myByte;
private readonly List<BindableBit> collection;
public List<BindableBit> Bits {
get { return collection; }
}
public MyCustomBitsControl() {
const byte defaultValue = 7;
myByte = defaultValue;
var index = 0;
collection = new BitArray(new[] { myByte }).Cast<bool>()
.Select(b => new BindableBit(index++) { Value = (b ? 1 : 0), ChangedAction = ChangedAction }).Reverse().ToList();
DataContext = this;
}
private void ChangedAction(int index, int value) {
var bit = (byte)Math.Pow(2, index);
if (value == 0) {
myByte &= (byte)(byte.MaxValue - bit);
}
else {
myByte |= bit;
}
}
...PropertyChangedImplementation
}
实现依赖项属性后,您可以从 myByte 字段或集合中获取所需的值。只需重新初始化 setValues 上的集合,就像我们在curent 构造函数中完成的那样。所有更改都将显示在用户界面上(不要忘记通知进行更改或将其类型修改为 observableCollection )。
答案 1 :(得分:1)
以下是使用在控件本身上定义的binding to indexer的解决方案:
这里UserControl
本身实现索引器属性和INotifyPropertyChanged
接口(允许在索引器属性中使用notification about the changes)。
[ContentProperty("BitByte")]
public partial class BitManipulator : UserControl,
INotifyPropertyChanged
{
public BitManipulator()
{
InitializeComponent();
this.grid_Items.DataContext = this;
}
#region Indexer and related
public Boolean this[Int32 index]
{
get
{
return BitConverterExt.BitAt(this.BitByte, index);
}
set
{
this.BitByte = BitConverterExt.WithSetBitAt(this.BitByte, index, value);
}
}
#endregion
#region Dependency properties
public static DependencyProperty BitByteProperty =
DependencyProperty.Register
(
"BitByte",
typeof(Byte),
typeof(BitManipulator),
new PropertyMetadata((sender, args) =>
{
if (args.Property != BitByteProperty)
return;
if (args.NewValue == args.OldValue)
return;
var This = (BitManipulator)sender;
This.OnPropertyChanged("Item[]");
})
);
public Byte BitByte
{
get { return (Byte)GetValue(BitByteProperty); }
set { SetValue(BitByteProperty, value); }
}
#endregion
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] String propertyName = null)
{
if (this.PropertyChanged == null)
return;
this.PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
#endregion
}
为了更容易编辑,我将TextBox更改为CheckBoxes:
<Grid x:Name="grid_Items">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox Name="order_0" Grid.Column="0" IsChecked="{Binding [0]}"/>
<CheckBox Name="order_1" Grid.Column="1" IsChecked="{Binding [1]}"/>
<CheckBox Name="order_2" Grid.Column="2" IsChecked="{Binding [2]}"/>
<CheckBox Name="order_3" Grid.Column="3" IsChecked="{Binding [3]}"/>
<CheckBox Name="order_4" Grid.Column="4" IsChecked="{Binding [4]}"/>
<CheckBox Name="order_5" Grid.Column="5" IsChecked="{Binding [5]}"/>
<CheckBox Name="order_6" Grid.Column="6" IsChecked="{Binding [6]}"/>
<CheckBox Name="order_8" Grid.Column="7" IsChecked="{Binding [7]}"/>
</Grid>
public static class BitConverterExt
{
public static Boolean BitAt(Byte byteValue, Int32 index)
{
if ((index < 0) || (index > 7))
throw new ArgumentNullException();
Byte mask = (Byte)(0x1 << index);
return (byteValue & mask) != 0;
}
public static Byte WithSetBitAt(Byte byteValue, Int32 index, Boolean value)
{
if ((index < 0) || (index > 7))
throw new ArgumentNullException();
Byte mask = (Byte)(0x1 << index);
if (value)
{
return (Byte)(byteValue | mask);
}
return (Byte)(byteValue & (~mask));
}
}
这是主窗口代码隐藏,它允许演示对位,用户控制字节或模型字节属性的任何更改都被正确反映。
public class ByteModel : INotifyPropertyChanged
{
private Byte m_ValueByte = 0xAA;
public Byte ValueByte
{
get
{
return this.m_ValueByte;
}
set
{
if (this.m_ValueByte == value)
return;
this.m_ValueByte = value;
if (this.PropertyChanged != null)
this.PropertyChanged(this,
new PropertyChangedEventArgs("ValueByte"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ByteModel();
}
}
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<view:BitManipulator x:Name="bitManipulator" Grid.ColumnSpan="2" BitByte="{Binding ValueByte, Mode=TwoWay}" />
<Label Grid.Row="1" Grid.Column="0"
Content="Change model BitByte:"/>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding ValueByte, Mode=TwoWay}"/>
<Label Grid.Row="2" Grid.Column="0"
Content="Change user control BitByte:"/>
<TextBox Grid.Row="2" Grid.Column="1"
DataContext="{Binding ElementName=bitManipulator}"
Text="{Binding BitByte, Mode=TwoWay}"/>
</Grid>
答案 2 :(得分:0)
您可能需要多重绑定才能遵循3个值
<ns:BitManipulator >
<ns:BitManipulator.BitByte>
<MultiBinding Converter="{StaticResource TitleSectionConverter}">
<Binding Path="PathA" />
<Binding Path="PathB" />
<Binding Path="PathC" />
</MultiBinding>
</ns:BitManipulator.BitByte>
</ns:BitManipulator>
并使用转换器来处理更改
public class BitByteConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// implement your logic to return expected value
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
在3个绑定中的一个更新源之后,将调用转换器。
希望这有帮助
答案 3 :(得分:0)
在BitManipulator
控件中,我不会使用绑定。相反,我会使用简单的事件处理程序来同步文本框和绑定字节:
<TextBox Name="order_0" Grid.Column="0" TextChanged="OnBitChanged" />
<TextBox Name="order_1" Grid.Column="1" TextChanged="OnBitChanged" />
<TextBox Name="order_2" Grid.Column="2" TextChanged="OnBitChanged" />
...
..和
public static DependencyProperty BitByteProperty =
DependencyProperty.Register
(
...
//Listen for changes in the model:
new PropertyMetadata(OnByteChanged)
);
实现:
private void OnBitChanged(object sender, TextChangedEventArgs e)
{
//Collect the bits from the textboxes, and create the new byte:
byte newByte = ...
//Update the bound model:
this.BitByte = newByte;
}
private static void OnByteChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var manipulator = (BitManipulator)sender;
var newByte = manipulator.BitByte;
//Split the byte into bits and populate the textboxes:
var bits = ...
manipulator.order_0.Text = bits[0];
manipulator.order_1.Text = bits[1];
...
}