2016年11月25日金曜日

WPF TreeView と RichTextBox

TreeView と RichTextBox を連携するサンプルを作成しました。

TreeView で選択したノードを RichTextBox に表示しています。TreeView のノードを選択したタイミングで発生する SelectedItemChanged イベントをハンドルし、コマンド経由で ViewModel 側の選択ノードを切り替えています。

2016年11月23日水曜日

WPF コマンド簡易例

コマンドの実装例をメモします。

<Window x:Class="WpfApplication5.CommandTest"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication5"
        mc:Ignorable="d"
        Title="CommandTest" Height="150" Width="300">
    <Grid>
        <TextBox x:Name="textBox"
                 HorizontalAlignment="Left"
                 Height="23"
                 Text="{Binding MyText}"
                 Width="120"/>
        <Button x:Name="button"
                Content="Clear Text"
                Command="{Binding ClearText}"
                CommandParameter="{Binding ElementName=textBox}"
                HorizontalAlignment="Left"
                Width="75"
                Margin="125,0,0,0" Height="23"/>
    </Grid>
</Window>

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfApplication5
{
    public partial class CommandTest : Window
    {
        public CommandTest()
        {
            InitializeComponent();

            MyData data = new MyData();
            data.MyText = "123 test";

            this.DataContext = data;
        }
    }

    public class MyData : NotificationObject
    {
        private string _MyText;
        public string MyText
        {
            get
            {
                return _MyText;
            }
            set
            {
                this._MyText = value;
                this.OnPropertyChanged();
            }
        }

        private ClearTextCommand _ClearText;
        public ClearTextCommand ClearText {
            get{
                if(_ClearText == null)
                {
                    _ClearText = new ClearTextCommand();
                }
                return _ClearText;
            }
        }
    }

    public class ClearTextCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            TextBox textBox = parameter as TextBox;
            textBox?.Clear();
        }
    }
}

INotifyPropertyChanged 実装サンプル(C# 6)

INotifyPropertyChanged の実装サンプルです。

バインディングソースに CLR オブジェクトを利用する際、バインディングターゲットの変更を通知するためには、INotifyPropertyChanged インターフェイスを実装します。

C# 6 で導入された Null 条件演算子を利用して OnPropertyChanged の実装を簡潔に記述することができるようになっています。
public class NotificationObject : INotifyPropertyChanged
{
    /// <summary>
    /// プロパティ値の変更をクライアントに通知する。
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// PropertyChanged イベント を発生させる。
    /// </summary>
    /// <param name="propertyName">変更されたプロパティ名</param>
    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        // C# 6 の Null 条件演算子を利用
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        
        // C# 6 以前
        //if (this.PropertyChanged != null)
        //{
        //    this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        //}
    }
}
public class MyClass : NotificationObject
{
    private string _text;
    public string Text {
        get {
            return _text;
        }
        set {
            this._text = value;
            this.OnPropertyChanged();
        }
    }
    public MyClass()
    {

    }
}
Null 条件演算子 (C# および Visual Basic)
https://msdn.microsoft.com/ja-jp/library/dn986595?f=255&MSPPError=-2147217396

2016年11月22日火曜日

WPF ItemsControl 仮想化

ItemsControl の仮想化を実装してみました。ControlTemplate の部分は TextBox のテンプレートを流用しています。

MSDNStackOverflow の情報を合わせると、下記のようになるのかなと思われます。

<ItemsControl
    VirtualizingStackPanel.IsVirtualizing="True"
    ScrollViewer.CanContentScroll="True"
    ItemsSource="{Binding Tasks}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Path=ID}" />
                <TextBlock Text="{Binding Path=Title}" />
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.Template>
        <ControlTemplate>
            <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1" SnapsToDevicePixels="true">
                <ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
                    <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                </ScrollViewer>
            </Border>
        </ControlTemplate>
    </ItemsControl.Template>
</ItemsControl>


Virtualizing an ItemsControl?
http://stackoverflow.com/questions/2783845/virtualizing-an-itemscontrol

パフォーマンスの最適化 : コントロール
https://msdn.microsoft.com/ja-jp/library/cc716879.aspx

2016年11月21日月曜日

WPF 依存関係プロパティ

こちらのサイトを参考にさせていただき、依存関係プロパティ作成の練習をしてみました。
ソースコードは下記サイトの方のものと非常に似たものになってしまいました...。

tips - 独自の依存関係プロパティを作成する
http://yujiro15.net/YKSoftware/tips_DependencyProperty.html



LabelInput.xaml.cs

"propdp" と入力し、Tab を2回押すとスニペットが自動で挿入されて雛形が出来上がります。プロパティの型情報、名前、プロパティを所有するクラス、プロパティのデフォルト値をそれぞれ変更していきます。PropertyMetadata の第1引数にはデフォルト値を、第2引数にはプロパティの変更通知を捕捉することができます。

public partial class LabelInput : UserControl
{
    public LabelInput()
    {
        InitializeComponent();
    }

    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(LabelInput), new PropertyMetadata("Text", TextChanged));

    private static void TextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        
    }
    

    public string Value
    {
        get { return (string)GetValue(ValueProperty); }
        set { SetValue(ValueProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Value.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(string), typeof(LabelInput), new PropertyMetadata("Value", ValueChanged));

    private static void ValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        
    }
}

LabelInput.xaml

Text プロパティと Value プロパティには、バインディングで外部から設定される値を参照しています。

<UserControl x:Class="MyPropertyTest.LabelInput"
             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:MyPropertyTest"
             mc:Ignorable="d" Height="24.812" Width="173.684">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="28*"/>
            <ColumnDefinition Width="59*"/>
        </Grid.ColumnDefinitions>
        <TextBlock x:Name="label"
               Text="{Binding Text, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:LabelInput}}, StringFormat='{}{0} : '}"
               Grid.Column="0"/>
        <TextBox x:Name="textBox"
                 TextWrapping="Wrap"
                 Text="{Binding Value, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:LabelInput}}}"
                 Grid.Column="1"/>

    </Grid>
</UserControl>


MainWindow.xaml

Text プロパティと Value プロパティの設定例です。

<local:LabelInput HorizontalAlignment="Left"
                  VerticalAlignment="Top"
                  Text="名前"
                  Value="としひこ"/>
<local:LabelInput HorizontalAlignment="Left"
                  VerticalAlignment="Top"
                  Text="名前"
                  Value="よしひこ" Width="174" Margin="0,25,0,0"/>