編集: ここで私のために働く解決策を見つけました: http://social.msdn.microsoft.com/Forums/en/wpf/thread/c1fd21b2-424b-4536-be8c-335cee94596a
次のように:
private void TextBoxLoaded(object sender, RoutedEventArgs e)
{
Binding bd = new Binding(TextBoxText.ToString());
bd.ValidationRules.Add(new DataErrorValidationRule());
bd.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
TextBox tb = sender as TextBox;
tb.SetBinding(TextBox.TextProperty, bd);
}
と
<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:wa="clr-namespace:WpfApplication1"
mc:Ignorable="d">
<DockPanel
VerticalAlignment="Center">
<Label
VerticalAlignment="Center"
Content="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type wa:UserControl1}},Path=LabelContent}"/>
<TextBox
VerticalAlignment="Center"
MinWidth="96"
Loaded="TextBoxLoaded">
</TextBox>
</DockPanel>
と
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wa="clr-namespace:WpfApplication1"
Title="MainWindow">
<Window.Resources>
<DataTemplate x:Key="personDisplay">
<DockPanel>
<wa:UserControl1 LabelContent="First name:" TextBoxText="fname"/>
<wa:UserControl1 LabelContent="Last name:" TextBoxText="lname"/>
<wa:UserControl1 LabelContent="Age:" TextBoxText="age"/>
</DockPanel>
</DataTemplate>
</Window.Resources>
<ContentControl
VerticalAlignment="Center"
Name="personCcl"
ContentTemplate="{StaticResource personDisplay}"/>
終わり
ラベルとテキストボックスを含める必要があるユーザーコントロールがあり、テキストボックスのエントリには、入力された値が有効であることを確認する検証規則が必要です。ユーザーコントロールを使用して複数のフィールドを持つクラスを表示するデータテンプレートがあります。クラスは IDataErrorInfo を実装します。ただし、私が抱えている問題は、テキスト ボックスがクラス IDataErrorInfo にアクセスしていないことと、さらに、無効なコントロールを囲む赤いアウトラインが、ユーザー コントロール内のテキスト ボックスだけでなく、ユーザー コントロール全体を囲んでいることです。
以下に、私がやろうとしていることのより簡単な例を作成しました。
クラスはこちら
public class Person : IDataErrorInfo
{
public string fname { get; set; }
public string lname { get; set; }
public int age { get; set; }
public Person(string f, string l, int a)
{
fname = f;
lname = l;
age = a;
}
public string Error
{
get
{
if (age < 18) return "Too young";
else if (fname == null || fname.Length == 0) return "Needs first name";
else if (lname == null || lname.Length == 0) return "Needs last name";
return null;
}
}
public string this[string name]
{
get
{
if (name == "age") { return age < 18 ? "Too young" : null; }
else if (name == "fname") { return fname == null || fname.Length == 0 ? "Needs first name" : null ; }
else if (name == "lname") { return lname == null || lname.Length == 0 ? "Needs last name" : null; }
return null;
}
}
}
ユーザーコントロールは次のとおりです。
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
static readonly public DependencyProperty LabelContentProperty = DependencyProperty.Register(
"LabelContent",
typeof(string),
typeof(UserControl1),
new FrameworkPropertyMetadata("")
);
public string LabelContent
{
get { return GetValue(LabelContentProperty) as string; }
set { SetValue(LabelContentProperty, value); }
}
static readonly public DependencyProperty TextBoxTextProperty = DependencyProperty.Register(
"TextBoxText",
typeof(object),
typeof(UserControl1),
new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnTextBoxTextChanged), new CoerceValueCallback(CoerceTextBoxText))
);
public object TextBoxText
{
get { return GetValue(TextBoxTextProperty); }
set { SetValue(TextBoxTextProperty, value); }
}
static private void OnTextBoxTextChanged(DependencyObject dob, DependencyPropertyChangedEventArgs ea)
{
var uc = dob as UserControl1;
uc.OnTextBoxTextChanged(new RoutedPropertyChangedEventArgs<object>(ea.OldValue, ea.NewValue, TextBoxTextChangedEvent));
}
static private object CoerceTextBoxText(DependencyObject dob, object o)
{
return o;
}
static readonly public RoutedEvent TextBoxTextChangedEvent = EventManager.RegisterRoutedEvent(
"TextBoxTextChanged",
RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventArgs<object>),
typeof(UserControl1));
public event RoutedPropertyChangedEventHandler<object> TextBoxTextChanged
{
add { AddHandler(TextBoxTextChangedEvent, value); }
remove { RemoveHandler(TextBoxTextChangedEvent, value); }
}
protected virtual void OnTextBoxTextChanged(RoutedPropertyChangedEventArgs<object> ea) { RaiseEvent(ea); }
}
ユーザーコントロールの xaml は次のとおりです。
<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:wa="clr-namespace:WpfApplication1"
mc:Ignorable="d">
<DockPanel>
<Label
VerticalAlignment="Center"
Content="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type wa:UserControl1}},
Path=LabelContent}"/>
<TextBox
VerticalAlignment="Center"
MinWidth="96">
<TextBox.Text>
<Binding
RelativeSource="{RelativeSource FindAncestor,
AncestorType={x:Type wa:UserControl1}}"
Path="TextBoxText"
Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<DataErrorValidationRule ValidatesOnTargetUpdated="True"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DockPanel>
ウィンドウの xaml (更新) は次のとおりです。
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wa="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="personDisplay">
<DockPanel>
<wa:UserControl1 LabelContent="First name:" TextBoxText="{Binding fname,Mode=TwoWay}"/>
<wa:UserControl1 LabelContent="Last name:" TextBoxText="{Binding lname,Mode=TwoWay}"/>
<wa:UserControl1 LabelContent="Age:" TextBoxText="{Binding age,Mode=TwoWay}"/>
</DockPanel>
</DataTemplate>
</Window.Resources>
<ContentControl
VerticalAlignment="Center"
Name="personCcl"
ContentTemplate="{StaticResource personDisplay}"/>
</Window>
ここに Window コンストラクターがあります
public MainWindow()
{
InitializeComponent();
personCcl.Content = new Person("John", "Smith", 33);
}