I wanted to have data validation at once in WPF binding so I used PropertyChanged at UpdateSourceTrigger. At the same time I wanted to detect LostFocus, this is the time I modify the DB. Moreover, I needed this in a DataGrid
. I solved this problem by introducing a temp property. I use the PropertyChanged
at the TempProperty
and used event handler at LostFocus
. The event handler copied the TempProperty
to the MainProperty
and triggered the DB change. This method worked.
I wanted to use Behavior instead of event handler. This is the Behavior
public class TextBoxLostFocusBehavior : Behavior<TextBox>
{
public static readonly DependencyProperty LostFocusCommandProperty =
DependencyProperty.Register("LostFocusCommand", typeof(ICommand),
typeof(TextBoxLostFocusBehavior), new PropertyMetadata(null));
public ICommand LostFocusCommand
{
get { return (ICommand)GetValue(LostFocusCommandProperty); }
set { SetValue(LostFocusCommandProperty, value); }
}
protected override void OnAttached() => AssociatedObject.LostFocus += AssociatedObject_LostFocus;
protected override void OnDetaching() => AssociatedObject.LostFocus -= AssociatedObject_LostFocus;
private void AssociatedObject_LostFocus(object sender, RoutedEventArgs e)
{
if (LostFocusCommand is not null && LostFocusCommand.CanExecute(null))
LostFocusCommand.Execute(null);
}
}
Setting Behaviors in style is not easy. But this solution works for me.
Now the solution is this
<DataGridTextColumn
Binding="{Binding TempBalanceFilenameTemplate, TargetNullValue='', ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
ElementStyle="{StaticResource TextAlignmentRightElementStyle}"
Header="{x:Static r:Resource.BalanceFilenameTemplate}">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="{x:Type TextBox}">
<Setter Property="hh:BehaviorInStyleAttacher.Behaviors">
<Setter.Value>
<collections:ArrayList>
<hhb:TextBoxLostFocusBehavior LostFocusCommand="{Binding MyCommand}" />
</collections:ArrayList>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.EditingElementStyle>
</DataGridTextColumn>
MyCommand
copies the TempProperty
to the MainProperty
. There is a slight problem Visual Studio says there is a Binding problem at MyCommand
though the binding works. The binding error says the DataContext is null, it should be the object behind the selected row. MyCommand
is in that object and it is executed as expected. It seems the Binding is evaulated before the DataContext is set, that is why an error is shown. Later the DataContext is set and the binding is reevaulated (?), that is why MyCommand
works.
This seems to be a minor issue but grows if I want to build on this solution.
<hhd:DataGridTextColumnEx Width="300"
Binding="{Binding TempBalanceFilenameTemplate, TargetNullValue='', ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
ElementStyle="{StaticResource TextAlignmentRightElementStyle}"
FinalizeCommand="{Binding MyCommand}"
Header="{x:Static r:Resource.BalanceFilenameTemplate}">
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="{x:Type TextBox}">
<Setter Property="hha:BehaviorInStyleAttacher.Behaviors">
<Setter.Value>
<collections:ArrayList>
<hhb:TextBoxLostFocusBehavior LostFocusCommand="{Binding FinalizeCommand, RelativeSource={RelativeSource AncestorType={x:Type hhd:DataGridTextColumnEx}}}" />
</collections:ArrayList>
</Setter.Value>
</Setter>
</Style>
</DataGridTextColumn.EditingElementStyle>
</hhd:DataGridTextColumnEx>
DataGridTextColumnEx
contains an additional dependency property the FinalizeCommand
which is used in the TextBoxLostFocusBehavior
. This additonal level of indirectcy is needed so I do not need to create a new style for each DataGridTextColumn
where I want the validation. Now Visual Studio indicates binding error at LostFocusCommand
and FinalizeCommand
as well. The problem is again the DataContext
is null. I do not know if it is related but the OnDetaching
is never executed.
DataContext
at design time? Not much you can do about this I am afraid.DataContext
is set properly so the binding works now. I do not know how to make sure that theDataContext
is set before the Binding is evaulated.