使用带有IValueConverter的ItemsSource的TwoWay绑定

时间:2014-03-31 09:18:46

标签: c# wpf mvvm ivalueconverter itemssource

在我的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中进行转换,对吗?

2 个答案:

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