-1

It's quite easy to check if certain container or its children have validation errors. This can be used to disable Save button.

I can use timer

public SomeUserControl()
{
    InitializeComponent();
    var timer = new DispatcherTimer
    {
        Interval = TimeSpan.FromMilliseconds(100),
        IsEnabled = true
    };
    Loaded += (s, e) => buttonSave.IsEnabled = IsValid(grid);
    Unloaded += (s, e) => timer.Stop();
}

to poll and to disable button.

<!-- container with lots of controls, bindings and validations -->
<Grid x:Name="grid">
   ...
</Grid>

<!-- save button -->
<Button x:Name="buttonSave" ... />

Is there a better way? Ideally I want an event. Unfortunately the only event I've found, Validation.Error event, can only be used on the element with bindings itself. Going through children elements and subscribing (not mentioning what I have to deal with adding new children) feels way worser than polling.

Thoughts?

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • See the code in this sample: https://gallery.technet.microsoft.com/WPF-Entity-Framework-MVVM-78cdc204 Which handles the events bubbling at a top container. The panel does this is in the resource dictionary. – Andy Oct 17 '19 at 12:02
  • 1
    The better way would be to bind the `Button` to an `ICommand` property of a view model and implement the command's `CanExecute` method to return `false` and raise the `CanExecuteChanged` event whenever you want to disable the `Button`. – mm8 Oct 17 '19 at 12:07
  • I always cringe when I see `x:Name`, but Commands were designed for that very reason, so you have control over the enabled and disabled state. If your VM would handle the validation then your job would be a lot easier, however since we are stuck with names and validation in UI with Binding I presume? Can you determine if Save Button is enabled from VM? – XAMlMAX Oct 17 '19 at 13:37
  • @XAMlMAX, sure, it will be a command at the end. I thought it's a good idea to strip view-to-viewmodel part to make [mcve] shorter. Some validations are only done in the view, e.g. when I type "aaaa" instead of `int` - the view model will not even know about it, it has previous valid number and think everything is fine, but on the screen is a red border and I want to disable "Save" button. – Sinatr Oct 17 '19 at 14:54

1 Answers1

1

The way I usually handle this is illustrated here:

https://social.technet.microsoft.com/wiki/contents/articles/28597.aspx

The errorevent will bubble to a container and you can handle that, use a behavior or command to pass it to the viewmodel.

Like:

<ControlTemplate x:Key="AddingTriggers" TargetType="ContentControl">
    <ControlTemplate.Resources>
        <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource ErrorToolTip}">
            <Setter Property="HorizontalAlignment" Value="Left"/>
        </Style>

    </ControlTemplate.Resources>
    <StackPanel>
        <i:Interaction.Triggers>
            <local:RoutedEventTrigger RoutedEvent="{x:Static Validation.ErrorEvent}">
                <e2c:EventToCommand   Command="{Binding ConversionErrorCommand, Mode=OneWay}"
                                        EventArgsConverter="{StaticResource BindingErrorEventArgsConverter}"
                                        PassEventArgsToCommand="True" />
            </local:RoutedEventTrigger>
        </i:Interaction.Triggers>
        <TextBlock Text="This would be some sort of a common header" Foreground="LightBlue" HorizontalAlignment="Right"/>
        <ContentPresenter/> <!-- This is how you can have variable content "within" the control -->
        <TextBlock Text="This would some sort of a common footer" Foreground="LightBlue"  HorizontalAlignment="Right"/>
    </StackPanel>
</ControlTemplate>

You need NotifyOnValidationError=True on any bindings.

Andy
  • 11,864
  • 2
  • 17
  • 20
  • I am still forgetting the nature of routed events. Thanks for implementation details and example. Btw, I didn't understand what you mean in comment. If you would say something simpler like "You can listed in `Grid` for its children `Validation.ErrorEvent`, because it's routed event, you only have to enable notifications in all bindings", then it would be enough. – Sinatr Oct 18 '19 at 13:12