WPFマークアップ拡張の自作

色々あって更新ができない状況、何とか1ネタ。

WPFではほとんどの場合XAMLBindingしますよね。そのBindingにはマークアップ拡張を使います、マークアップ拡張ってのは”{ … }” という部分。Bindingのほか、StaticResourceやDynamicResourceの参照でも使われます。WPFの機能を使いこなすのにビヘイビアとは違うアプローチで覚えておいて損がないと思われます。ということでやってみましょう。

題材はXAMLのバインドのマークアップ拡張の自作。

マークアップ拡張はMarkupExtension抽象クラスを派生させて作ります。この抽象クラスは下のような単一のメソッドを実装します。このメソッドで戻す値はマークアップ拡張として使った時に返すべき値となります。

    public class EmulateBindingExtension : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
        }
    }

バインドするのでBindingにならって名前はEmulateBindingクラスとします。使うときは下のような感じになることを想定しています。

<Window x:Class="MarkupExtSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:me="clr-namespace:MarkupExtSample"
        Title="MainWindow" Height="142" Width="370">
    <StackPanel>
        <TextBox x:Name="TextBox1" Text="{Binding Path=StringProperty}" Height="24" VerticalAlignment="Top" Width="348" />
        <TextBox x:Name="TextBox2" Text="{me:EmulateBinding Path=StringProperty}" Height="24" VerticalAlignment="Top" Width="348" />
    </StackPanel>
</Window>

さぁ、実装。

namespace MarkupExtSample
{
    using System;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Markup;

    public class EmulateBindingExtension : MarkupExtension
    {
        public PropertyPath Path { get; set; }

        public EmulateBindingExtension(){}

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            IProvideValueTarget provider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
            if (provider == null){
                return null;
            }
            var target = provider.TargetObject as FrameworkElement;
            if (target == null){
                return null;
            }
            var property = provider.TargetProperty as DependencyProperty;
            if (property == null){
                return null;
            }

            Binding bind = new Binding();
            bind.Path = Path;
            BindingOperations.SetBinding(target, property, bind);
            return target.GetValue(property);
        }
    }
}

これに対してMainWindowのDataContextにバインドソースをセットしたらokです。

namespace MarkupExtSample
{
    using System.Windows;

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new TestModelView() { StringProperty = "初期値だよ" };
        }
    }
}

DataContextの場合はBindingのSourceプロパティに何の指定もいらないことにご注意。

動作

他にもSourceなど適宜プロパティを公開すればBindingにより似てくると思われます。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中