在我的ViewModel中,我有一个模型类Foo
,其中包含属性Bar
class Foo
{
byte Bar { get; set; }
}
我想要的是在我的View中将该属性显示为表示该值位的复选框列表。我设法通过使用ItemsControl
和绑定转换器来实现这一目的。
<ItemsControl ItemsSource="{Binding Bar, Converter={StaticResource ValueToLed}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Style="{StaticResource LedCheckBox}"
IsChecked="{Binding Value,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ToolTip="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
我的转换器使用BitArray
从该值中获取bool
的数组。我还将每个bool
值包装在一个包装类中,以便为该值提供更多属性,如索引和名称(对于toolip)。你可以说我在IValueConverter
里面为这样的CheckBox构建了一个ViewModel。
这很好,但在其他方向不行。当我更改其中一个位时,我希望我的ConvertBack
将更新的值存储在我的ViewModel / Model中,但我的ConvertBack
永远不会被调用。我已经阅读了与此主题相关的一些内容,到目前为止我的结论是,这是不可能的,因为ConvertBack
仅在ItemsSource
本身发生变化而不是其中的项目时被调用。这是真的吗?
此方法的唯一(?)替代方法是在我的ViewModel中进行转换,而不是在IValueConverter
中进行转换,对吗?
答案 0 :(得分:2)
C#语言(以及C#开发人员)应该不了解(即过敏)内部位表示。虽然你需要任何布尔变量,但只需使用它。
如果你的程序处理某些低级接口(例如I / O流中的位掩码),只需在最接近的级别打包/解包实际的字节数组。
那就是说,WPF和比特非常过敏本身,并且绑定到一个比特的复选框需要一个无用而又凌乱的代码。我不建议这样做。
只需在MVVM模式中公开你的un / check变量,作为列表中模板化的每个项目的一部分。复选框绑定很简单,代码也很干净。
编辑:我现在明白你的问题是什么。你是否在bools&#39;中实现了INotifyPropertyChanged模式?包装器,专门用于&#34; Value&#34;属性?
EDIT2:这对我来说很好。
<ItemsControl ItemsSource="{Binding Path=Bar}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox
IsChecked="{Binding Value,Mode=TwoWay}"
ToolTip="{Binding Name}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
这是ViewModel:
class MyCollection
{
public IEnumerable<MyWrapper> Bar { get; set; }
}
class MyWrapper : INotifyPropertyChanged
{
#region PROP Value
private bool _value;
public bool Value
{
get { return this._value; }
set
{
if (this._value != value)
{
this._value = value;
this.OnPropertyChanged("Value");
Console.WriteLine("changed="+ this._value);
}
}
}
#endregion
#region EVT PropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(
this,
new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
单击复选框时,将修改相关属性。 Console.Writeline证明了这一点。
EDIT3:更正代码如下:
class MyCollection : INotifyPropertyChanged
{
#region PROP Bar
private byte _bar;
public byte Bar
{
get { return this._bar; }
set
{
if (this._bar != value)
{
this._bar = value;
this.OnPropertyChanged("Bar");
Console.WriteLine("bar="+ this._bar);
this.UpdateItems();
}
}
}
#endregion
public void UpdateItems()
{
//rebuild the children collection
var collection = new List<MyWrapper>();
for (int i = 0; i < 8; i++)
{
var item = new MyWrapper();
item.Value = ((this._bar >> i) & 1) != 0;
collection.Add(item);
}
this.Items = collection;
}
#region PROP Items
private IEnumerable<MyWrapper> _items;
public IEnumerable<MyWrapper> Items
{
get { return this._items; }
set
{
if (this._items != value)
{
this._items = value;
this.OnPropertyChanged("Items");
if (this._items != null)
{
foreach (var child in this._items)
{
child.PropertyChanged += child_PropertyChanged;
}
}
}
}
}
#endregion
void child_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//rebuild the scalar value
int value = 0;
foreach (var item in this._items)
{
value = value >> 1;
if (item.Value) value |= 0x80;
}
this.Bar = (byte)value;
}
#region EVT PropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(
this,
new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
答案 1 :(得分:0)
您应创建一个值转换器,将单个ubyte值转换为Checkbox的check / unchecked,因为如果集合中某个项的值发生更改,则不会为该集合调用Converter的ConvertBack方法。
您的XAML应如下所示:
<ItemsControl ItemsSource="{Binding Bar">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Style="{StaticResource LedCheckBox}"
IsChecked="{Binding Value,
Mode=TwoWay,
Converter={StaticResource ValueToLedConverter},
UpdateSourceTrigger=PropertyChanged}"
ToolTip="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>