Creating user controls in WPF (.NET 4.5/C#).
A text box extension that also implements input validation.
We would like to pass parameters to this validation logic in the following ways:
<local:UserControl1Value="{Binding Foo}"TargetType="{x:Typelocal:AnyType}"/>
The TargetType
property allows you to specify a class to customize the validation logic.
In implementing this mechanism, we used the explanatory articles on this site.
Here's the code:
UserControl 1.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:local="clr-namespace:WpfApplication1"
mc —Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBox>
<TextBox.Text>
<Binding Path="Value"
UpdateSourceTrigger="LostFocus"
RelativeSource="{RelativeSourceFindAncestor,AncestorType={x:TypeUserControl}}"
NotifyOnValidationError="True"
>
<Binding.ValidationRules>
<local:HogeRule>
<local: HogeRule.TargetType>
<local:DependencyTypeValue="{BindingTargetType,RelativeSource={RelativeSourceFindAncestor,AncestorType={x:TypeUserControl}}}"/gt;
</local: HogeRule.TargetType>
</local: HogeRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
</UserControl>
Code Behind
public partial class UserControl1:UserControl
{
public UserControl1()
{
InitializeComponent();
}
// Value Dependency Properties
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value",
type of (object),
type of (UserControl1),
new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
// Value CLR Properties
public object value
{
get {return this.GetValue(ValueProperty);}
set {this.SetValue(ValueProperty, value);}
}
// TargetType Dependency Properties
public static readonly DependencyProperty TargetTypeProperty=DependencyProperty.Register(
"TargetType",
type of (Type),
type of (UserControl1),
new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
/// TargetType CLR Properties
public Type TargetType
{
get {return(Type)this.GetValue(TargetTypeProperty);}
set {this.SetValue (TargetTypeProperty, value);}
}
}
Basic Validation Rules
public class HogeRule:ValidationRule
{
public HogeRule()
{
this.TargetType = new DependencyType();
}
public DependencyTypeTargetType{get;set;}
public override ValidationResultValidate(object value, System.Globalization.CultureInfo)
{
// Customize the process using TargetType.Value
}
}
DependencyObject
used to pass Type type by data bindpublic class DependencyType:DependencyObject
{
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value",
type of (Type),
type of (DependencyType),
new FrameworkPropertyMetadata(default(object), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
public Type Value
{
get {return(Type)this.GetValue(ValueProperty);}
set {this.SetValue(ValueProperty, value);}
}
}
However, even if you move this code, TargetType.Value
is null
when HogeRule.Validate
is executed.
UserControl1.xaml
<local:DependencyTypeValue="{BindingTargetType,RelativeSource={RelativeSourceFindAnceptor,AnceptorType={x:TypeUserControl}}}}"/gt;
The row in is
<local:DependencyTypeValue="{x:Type local:AnyType}"/>
If you write fixedly, it works fine, so I think the binding here is not working well.
But I don't know what's wrong.
All you want to do is allow user control users to specify validation parameters, so you can take a completely different approach.
This is probably not possible with scope-dependent Binding.ElementName
.The reference article uses {StaticResource}
, so I think the VM has been able to resolve the issue by ignoring the visual tree.
So if you want to force a solution, use a special class like CollectionViewSource
for example
<UserControl.Resources>
<CollectionViewSource
x —Key = "cvs" >
<CollectionViewSource.Source>
<CompositeCollection>
<local:DependencyType
Value="{Binding TargetType}"/>
</CompositeCollection>
</CollectionViewSource.Source>
</CollectionViewSource>
</UserControl.Resources>
and resources defined.
<local:HogeRule.TargetType>
<local:DependencyType
Value="{Binding Source={StaticResource cvs}, Path=CurrentItem.Value}"/>
</local: HogeRule.TargetType>
You need to do something terrible, such as .
Also, if you don't mind changing the visual tree,
<UserControl>
<UserControl.Resources>
<Gridx: Key="grid" Visibility="Collapsed"/>
</UserControl.Resources>
<Grid>
<StaticResourceExtensionResourceKey="grid"/>
…
</Grid>
<UserControl>
You can also deploy hidden controls and use the same instance as {Binding Source={StaticResource grid}, Path=DataContext.…}
as shown in .
I have not verified it, but I do not think UserControl
and local:DependencyType
are parent-child relationships.
Why don't you name UserControl with x:Name and refer to ElementName on the DependencyType side?
<UserControl x: Class="WpfApplication1.UserControl1"
x: Name = "UserControl"
(Omitted)
d:DesignHeight="300" d:DesignWidth="300">
<local:DependencyTypeValue="{BindingTargetType,ElementName=UserControl}"/>
© 2024 OneMinuteCode. All rights reserved.