WPF控件的组成

时间:2015-04-07 08:41:41

标签: c# wpf composition

我想创建一个允许操作字节中特定位的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); }
        }
    }
}

4 个答案:

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

用户控制xaml

为了更容易编辑,我将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();
    }
}

主窗口xaml

<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];
    ...
}