2012年7月6日金曜日

[WPF][C#]ビットイメージ配列位置選択数値変換ユーザコントロール

タイトル長!

通信系のデータを扱う場合、こういうエディタがあると喜ばれるので、作ってみた。
絵でいうとこんなかんじ。


16個の四角枠のところがユーザコントロール。
ビットが立っているところが水色。
マウスオーバーはオレンジ。

ToggleButton で ON/OFF を表現している。

int <=> byte[4] <=> BitArray(16)

ToggleButton の IsChecked とBitArray[0] 〜 BitArray[15] がデータバインドしている。

ちょっと長くなるが、コードを一式貼付ける。





    /// <summary>
    /// ビット配列イメージ
    /// </summary>
    public partial class BitArraySelect : UserControl
    {
        /// <summary>
        /// データプロパティ
        /// </summary>
        public static readonly DependencyProperty DataProperty = DependencyProperty.Register("Data", typeof(int),
                                                                 typeof(BitArraySelect), 
                                                                 new FrameworkPropertyMetadata(0, new PropertyChangedCallback(OnDataChanged)));
        /// <summary>
        /// データを取得または設定する
        /// </summary>
        public int Data
        {
            get { return (int)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }
        /// <summary>
        /// データ変更イベント
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void OnDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((BitArraySelect)d).Update();
        }
        /// <summary>
        /// ビット配列イメージ
        /// </summary>
        private BitImageWrapper bitArrayImage = new BitImageWrapper();
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public BitArraySelect()
        {
            InitializeComponent();
            this.DataContext = this.bitArrayImage;
            this.Loaded += (sender, e) => { Update(); };
        }
        /// <summary>
        /// トグルボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ToggleButton_Click(object sender, RoutedEventArgs e)
        {
            byte[] data = new byte[4];
            this.bitArrayImage.Data.CopyTo(data, 0);
            this.Data = BitConverter.ToInt32(data, 0);
        }
        /// <summary>
        /// 更新
        /// </summary>
        public void Update()
        {
            this.bitArrayImage.Data = new BitArray(BitConverter.GetBytes(this.Data));
        }
    }
    /// <summary>
    /// ビットイメージラッパー
    /// </summary>
    public class BitImageWrapper : INotifyPropertyChanged
    {
        /// <summary>
        /// ビット配列
        /// </summary>
        private BitArray data = new BitArray(16);
        /// <summary>
        /// ビット配列データを取得または設定します。
        /// </summary>
        public BitArray Data
        {
            get { return data; }
            set
            {
                data = value;
                OnPropertyChanged("Data");
            }
        }
        /// <summary>
        /// プロパティ変更イベントハンドラ
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// プロパティ変更処理
        /// </summary>
        /// <param name="name">プロパティ名</param>
        public void OnPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }





<UserControl x:Class="WpfValueBitImage.BitArraySelect"
             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" 
             xmlns:local="clr-namespace:WpfValueBitImage"
             mc:Ignorable="d" 
             d:DesignHeight="60" d:DesignWidth="480" Width="480" Height="30">
    <UserControl.Resources>
        <LinearGradientBrush x:Key="PressedGradientBrush" 
            StartPoint="0.5,0" EndPoint="0.5,1">
            <GradientStop Color="White" Offset="0" />
            <GradientStop Color="LightBlue" Offset="1" />
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="MouseOverGradientBrush" 
            StartPoint="0.5,0" EndPoint="0.5,1">
            <GradientStop Color="White" Offset="0" />
            <GradientStop Color="#FFFFD452" Offset="1" />
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="SelectedGradientBrush" 
            StartPoint="0.5,0" EndPoint="0.5,1">
            <GradientStop Color="#FFEBFAFF" Offset="0" />
            <GradientStop Color="#FFA2E0FB" Offset="1" />
        </LinearGradientBrush>
        <Style TargetType="{x:Type ToggleButton}">
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Foreground" Value="Black"/>
            <Setter Property="Background" Value="White"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                        <Grid>
                            <Border BorderBrush="Gray" BorderThickness="0" CornerRadius="0"  Width="{TemplateBinding Property=Button.Width}" Height="{TemplateBinding Property=Button.Height}" Background="{TemplateBinding Property=Foreground}">
                                <Rectangle  Fill="{TemplateBinding Property=Background}"/>
                            </Border>
                            <TextBlock Text="{TemplateBinding ToggleButton.Content}" HorizontalAlignment="Center" VerticalAlignment="Center"  />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="IsPressed" Value="true">
                    <Setter Property="Foreground" Value="Red" />
                    <Setter Property="Background" Value="{StaticResource PressedGradientBrush}" />
                </Trigger>
                <Trigger Property="IsChecked" Value="true">
                    <Setter Property="Background" Value="{StaticResource SelectedGradientBrush}" />
                </Trigger>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsMouseOver" Value="True" />
                        <Condition Property="IsChecked" Value="False" />
                        <Condition Property="IsPressed" Value="False" />
                    </MultiTrigger.Conditions>
                    <Setter Property="Background" Value="{StaticResource MouseOverGradientBrush}" />
                </MultiTrigger>
            </Style.Triggers>
        </Style>
        <Style x:Key="BorderStyle" TargetType="{x:Type Border}">
            <Setter Property="Width" Value="30" />
            <Setter Property="Height" Value="30" />
            <Setter Property="BorderBrush" Value="Gray" />
            <Setter Property="BorderThickness" Value="1,1,0,1" />
        </Style>
    </UserControl.Resources>
    <Grid>
        <StackPanel Orientation="Horizontal">
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="F" IsChecked="{Binding Path=Data[15], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="E" IsChecked="{Binding Path=Data[14], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="D" IsChecked="{Binding Path=Data[13], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="C" IsChecked="{Binding Path=Data[12], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="B" IsChecked="{Binding Path=Data[11], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="A" IsChecked="{Binding Path=Data[10], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="9" IsChecked="{Binding Path=Data[9], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="8" IsChecked="{Binding Path=Data[8], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="7" IsChecked="{Binding Path=Data[7], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="6" IsChecked="{Binding Path=Data[6], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="5" IsChecked="{Binding Path=Data[5], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="4" IsChecked="{Binding Path=Data[4], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="3" IsChecked="{Binding Path=Data[3], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="2" IsChecked="{Binding Path=Data[2], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}">
                <ToggleButton Content="1" IsChecked="{Binding Path=Data[1], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
            <Border Style="{StaticResource BorderStyle}" BorderThickness="1">
                <ToggleButton Content="0" IsChecked="{Binding Path=Data[0], Mode=TwoWay}" Click="ToggleButton_Click"/>
            </Border>
        </StackPanel>
    </Grid>
</UserControl>







サンプルの画像は上部にあるテキストボックスの Text プロパティ とこのユーザコントロールの Data プロパティがバインディングで連結されている。



Data="{Binding Path=Text, Mode=TwoWay, ElementName=textBox}"