2015-04-07 1 views
2

Я хочу создать элемент управления WPF, который позволяет манипулировать определенными битами в байте (разметка прикреплена к нижней части этого сообщения).Состав элементов управления WPF

Он должен использоваться следующим образом

<ns:BitManipulator BitByte={Binding Path=...} /> 

, но я не могу понять, как организовать (многолетние) привязки, чтобы следующие три значения то же самое: а) байты-значения в модели, что BitByte будет связан с б) байт-значение BitByte, которые либо должны обновить свою ценность, если либо ценность модели или ценность BitVector меняет C) бит-Представление BitByte в TextBoxes имени order_i

Любой намек на приложение reciated

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); } 
     } 
    } 
} 

ответ

1

Извините за долгое время. Но кажется, что в MSDN есть ошибка в CollectionChanged, и он не стреляет для отдельных изменений элементов. Для наброска вашего дела я просто оставлю образец. 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 constructor. Все изменения будут отображаться в пользовательском интерфейсе (не забудьте указать для этого изменения или изменения его типа до observableCollection).

0

Вам может понадобиться MultiBinding следовать 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 привязок обновит его источник.

Надеется, что это помогает

+2

Я не уверен, что это то, что нуждается в ОП. Насколько я понимаю, ему нужна сборка адаптеров (оболочка 'INotifyCollectionChanged' над свойством байта), которая позволит проецировать изменения в базовом байте на элементы управления бит и наоборот. –

+0

Многосвязывание - хороший способ для отдельных свойств. – Neil

+2

Все три свойства имеют один и тот же источник, поэтому многосвязывание не требуется. На самом деле есть только одно значение, которое повлияет на все упомянутые свойства. –

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

Чтобы обеспечить более легкое редактирование я изменил TextBoxes к 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> 

манипуляция Bit помощник

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>