Reputation: 3
I'm trying to achieve quite a specific thing in WPF. I need to create a verifiable control, which will be binded to object of generic class like this
public interface IVerifiable
{
bool Verified { get; set; }
}
public class VerifiableProperty<T> : INotifyPropertyChanged, IVerifiable
{
private T _value;
private bool _verified;
public T Value
{
get => _value;
set
{
if (Equals(value, _value)) return;
_value = value;
OnPropertyChanged();
}
}
public bool Verified
{
get => _verified;
set
{
if (value == _verified) return;
_verified = value;
OnPropertyChanged();
}
}
}
I bind controls this way
<TextBox Text="{Binding Path=Number.Value}" att:VerifiableControl.Verified="{Binding Path=Number.Verified}"
BorderBrush="{Binding Path=(att:VerifiableControl.Verified), Mode=OneWay,
Converter={StaticResource BackColorVerifiedConverter}}">
<inter:Interaction.Triggers>
<inter:EventTrigger EventName="LostFocus">
<inter:InvokeCommandAction Command="{Binding Path=DataContext.VerifyCurrentFieldCommand, ElementName=Root}"
CommandParameter="{Binding Path=Number}"/>
</inter:EventTrigger>
</inter:Interaction.Triggers>
</TextBox>
Generic value of Verifiable property object binds to text (or SelectedDate in case of DatePicker, etc.), border color binds to flag "Verified" via converter and also I bind interaction trigger for "FocusLost" event to ViewModel command that just set "Verified" flag of corresponding property object to true.
I really don't like the idea I have to copypaste this block many times with little changes. I know, I cannot put intercation trigger in style and I also couldn't find another way to make some kind of short pattern for that. So, is there a way to create some kind of short pattern for this xaml code where I could customize name of property to bind? Or maybe you could suggest another approach for what I want ot achieve?
Upvotes: 0
Views: 71
Reputation: 19106
Use some attached properties and attach in there to the LostFocus event.
public static class VerifiableBehaviour
{
public static bool GetVerified(UIElement obj) => (bool)obj.GetValue(VerifiedProperty);
public static void SetVerified(DependencyObject obj, bool value) => obj.SetValue(VerifiedProperty, value);
public static readonly DependencyProperty VerifiedProperty = DependencyProperty.RegisterAttached(
"Verified", typeof(bool), typeof(VerifiableBehaviour),
new FrameworkPropertyMetadata(false)
{
BindsTwoWayByDefault = true,
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
});
public static bool GetTracking(UIElement obj) => (bool)obj.GetValue(TrackingProperty);
public static void SetTracking(UIElement obj, bool value) => obj.SetValue(TrackingProperty, value);
public static readonly DependencyProperty TrackingProperty = DependencyProperty.RegisterAttached(
"Tracking", typeof(bool), typeof(VerifiableBehaviour),
new PropertyMetadata(false, Tracking_PropertyChanged));
private static void Tracking_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement element = d as UIElement;
if ((bool)e.NewValue)
element.LostFocus += Element_LostFocus;
else
element.LostFocus -= Element_LostFocus;
}
private static void Element_LostFocus(object sender, RoutedEventArgs e)
{
UIElement element = sender as UIElement;
SetVerified(element, true);
}
}
and bind it to your controls
<StackPanel>
<StackPanel.Resources>
<DataTemplate x:Key="VerifiableText">
<StackPanel>
<TextBox b:VerifiableBehaviour.Tracking="True"
b:VerifiableBehaviour.Verified="{Binding Verified}"
Text="{Binding Value}"/>
<TextBlock>(Verified: <Run Text="{Binding Verified}"/>)</TextBlock>
</StackPanel>
</DataTemplate>
</StackPanel.Resources>
<TextBlock Text="MyProperty1"/>
<ContentControl ContentTemplate="{StaticResource VerifiableText}"
Content="{Binding MyProperty1}" IsTabStop="False"/>
<Separator/>
<TextBlock Text="MyProperty2"/>
<ContentControl ContentTemplate="{StaticResource VerifiableText}"
Content="{Binding MyProperty2}" IsTabStop="False"/>
<Separator/>
<TextBlock Text="MyProperty3"/>
<ContentControl ContentTemplate="{StaticResource VerifiableText}"
Content="{Binding MyProperty3}" IsTabStop="False"/>
<Separator/>
<TextBlock Text="MyProperty4"/>
<ContentControl ContentTemplate="{StaticResource VerifiableText}"
Content="{Binding MyProperty4}" IsTabStop="False"/>
<Separator/>
</StackPanel>
Upvotes: 1