

WPF(.NET4.5/C#)でユーザコントロールを作成しています。  
テキストボックスを拡張したもので、入力値の検証も実装します。  
この検証ロジックに、次のような形でパラメータを渡したいと考えています。

[code]

    <local:UserControl1 Value="{Binding Foo}" TargetType="{x:Type local:AnyType}" />
    
[/code]

`TargetType`プロパティに、検証ロジックをカスタマイズするクラスを指定できる仕組みです。  
この仕組みを実装するにあたり、[このサイト](http://oita.oika.me/2014/12/21/binding-to-validation-
rule/)の解説記事を参考にしました。  
できあがったコードが次の通りです。

UserControl1.xaml

[code]

    <UserControl x:Class="WpfApplication1.UserControl1"
                 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:WpfApplication1"
                 mc:Ignorable="d" 
                 d:DesignHeight="300" d:DesignWidth="300">
        <Grid>
            <TextBox>
                <TextBox.Text>
                    <Binding Path="Value"
                             UpdateSourceTrigger="LostFocus"
                             RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type UserControl}}"
                             NotifyOnValidationError="True"
                             >
                        <Binding.ValidationRules>
                            <local:HogeRule>
                                <local:HogeRule.TargetType>
                                    <local:DependencyType Value="{Binding TargetType, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>
                                </local:HogeRule.TargetType>
                            </local:HogeRule>
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>
            </TextBox>
        </Grid>
    </UserControl>
    
[/code]

コードビハインド

[code]

    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }
    
        // Value 依存関係プロパティ
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
            "Value",
            typeof(object),
            typeof(UserControl1),
            new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
    
        // Value CLRプロパティ
        public object Value
        {
            get { return this.GetValue(ValueProperty); }
            set { this.SetValue(ValueProperty, value); }
        }
    
        // TargetType 依存関係プロパティ
        public static readonly DependencyProperty TargetTypeProperty = DependencyProperty.Register(
            "TargetType",
            typeof(Type),
            typeof(UserControl1),
            new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
    
        /// TargetType CLRプロパティ
        public Type TargetType
        {
            get { return (Type)this.GetValue(TargetTypeProperty); }
            set { this.SetValue(TargetTypeProperty, value); }
        }
    }
    
[/code]

基本となる検証ルール

[code]

    public class HogeRule : ValidationRule
    {
        public HogeRule()
        {
            this.TargetType = new DependencyType();
        }
    
        public DependencyType TargetType { get; set; }
    
        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            // TargetType.Valueを使って処理をカスタマイズ
        }
    }
    
[/code]

Type型をデータバインドで渡すために使うDependencyObject

[code]

    public class DependencyType : DependencyObject
    {
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
            "Value",
            typeof(Type),
            typeof(DependencyType),
            new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
    
        public Type Value
        {
            get { return (Type)this.GetValue(ValueProperty); }
            set { this.SetValue(ValueProperty, value); }
        }
    }
    
[/code]

しかし、このコードを動かしても、`HogeRule.Validate`が実行された時点で、`TargetType.Value`は`null`になっています。  
`UserControl1.xaml`の

[code]

    <local:DependencyType Value="{Binding TargetType, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"/>
    
[/code]

の行を

[code]

    <local:DependencyType Value="{x:Type local:AnyType}"/>
    
[/code]

と固定で書いた場合には正常に動作するので、ここのバインディングがうまくいっていないのだと思います。  
しかし、どこか違っているのか分かりません。  
ただ、やりたいことは、ユーザコントロールの利用者が検証用のパラメータを指定できるようにしたいだけなので、全く別のアプローチでも構いません。


CC BY-SA 3.0
2015-04-17T06:41:31.260
9256
2016-01-12T19:38:34.097
2015-04-17T12:06:43.733
8870
8078
question
c# .net wpf xaml
WPF ユーザーコントロールの検証ロジックにパラメータを渡したい


検証はしていませんが、`UserControl`と`local:DependencyType`が親子関係になっていないように思います。  
UserControlにx:Nameで名前を付け、DependencyType側はElementNameで参照してはどうでしょうか。

[code]

    <UserControl x:Class="WpfApplication1.UserControl1"
             x:Name="UserControl"
             (省略)
             d:DesignHeight="300" d:DesignWidth="300">
    
    <local:DependencyType Value="{Binding TargetType, ElementName=UserControl}"/>
    
[/code]


CC BY-SA 3.0
2015-04-17T08:18:21.803
9260
2015-04-17T08:18:21.803
3022
9256
answer


スコープに依存する`Binding.ElementName`ではおそらく無理です。[参照先の記事](http://oita.oika.me/2014/12/21/binding-
to-validation-
rule/)は`{StaticResource}`を利用しているためビジュアルツリーを無視してVMが解決できているのだと思います。

なので無理やり解決するのであれば`CollectionViewSource`のような特殊なクラスを用いて、例えば

[code]

    <UserControl.Resources> 
        <CollectionViewSource
            x:Key="cvs">
            <CollectionViewSource.Source>
                <CompositeCollection>
                    <local:DependencyType
                        Value="{Binding TargetType}" />
                </CompositeCollection>
            </CollectionViewSource.Source>
        </CollectionViewSource>
    </UserControl.Resources>
    
[/code]

とリソースを定義しておいて

[code]

    <local:HogeRule.TargetType>
        <local:DependencyType
            Value="{Binding Source={StaticResource cvs}, Path=CurrentItem.Value}" />
    </local:HogeRule.TargetType>
    
[/code]

するなどの酷い方法を使う必要があります。

またビジュアルツリーを変更しても良いのであれば、

[code]

    <UserControl>
        <UserControl.Resources>
            <Grid x:Key="grid" Visibility="Collapsed" />
        </UserControl.Resources>
        <Grid>
            <StaticResourceExtension ResourceKey="grid" />
            …
        </Grid>
    <UserControl>
    
[/code]

のように隠しコントロールを配置しておいて`{Binding Source={StaticResource grid},
Path=DataContext.…}`と同インスタンスを使う方法もあります。


CC BY-SA 3.0
2015-04-17T11:29:22.000
9265
2015-04-17T11:50:39.200
2015-04-17T11:50:39.200
5750
5750
9256
answer