2
\$\begingroup\$

I am working on a WPF application consisting of a tree view and multiple "view parts" that display details about the selected node.

The content of the view parts are loaded asynchronously which may fail (e.g. some data are loaded from a web server which will become unavailable and so on). Of course, failure is an exception and should usually not happen.

My approach to handle failures in a decent manner (something between hiding it from the user and showing an annoying message box) is to show an error message within the view part.

For that use case, I wrote a simple control that works as a wrapper around the view part and displays the error message.

Usage

<controls:ErrorIndicator Error="{Binding ViewPartViewModel.Error}">
    <view:ViewPart DataContext="{Binding ViewPartViewModel}" />
</controls:ErrorIndicator>

ErrorIndicator Class

[TemplatePart(Name = "PART_ErrorContainer", Type = typeof(Grid))]
[TemplatePart(Name = "PART_ErrorTextBlock", Type = typeof(TextBlock))]
public class ErrorIndicator : ContentControl
{
    private Grid PART_ErrorContainer;
    private TextBlock PART_ErrorTextBlock;

    public string Error
    {
        get { return (string)GetValue(ErrorProperty); }
        set { SetValue(ErrorProperty, value); }
    }
    public static readonly DependencyProperty ErrorProperty =
        DependencyProperty.Register("Error", typeof(string), typeof(ErrorIndicator), new PropertyMetadata(null, ErrorChanged));

    private static void ErrorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var errorIndicator = d as ErrorIndicator;
        if (errorIndicator == null
            || errorIndicator.PART_ErrorContainer == null
            || errorIndicator.PART_ErrorTextBlock == null)
            return;
        if (e.NewValue != null)
            errorIndicator.ShowErrorIndicator((string) e.NewValue);
        else
            errorIndicator.HideErrorIndicator();
    }

    private void ShowErrorIndicator(string error)
    {
        PART_ErrorTextBlock.Text = error;
        PART_ErrorContainer.Visibility = Visibility.Visible;
    }

    private void HideErrorIndicator()
    {
        PART_ErrorTextBlock.Text = "";
        PART_ErrorContainer.Visibility = Visibility.Hidden;
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        PART_ErrorContainer = Template.FindName("PART_ErrorContainer", this) as Grid;
        PART_ErrorTextBlock = Template.FindName("PART_ErrorTextBlock", this) as TextBlock;

        if (PART_ErrorContainer == null)
            throw new InvalidOperationException(
                "ErrorIndicator's control template must contain part 'PART_ErrorContainer' of type 'Grid'");

        if (PART_ErrorTextBlock == null)
            throw new InvalidOperationException(
                "ErrorIndicator's control template must contain part 'PART_ErrorTextBlock' of type 'TextBlock'");
    }
}

ErrorIndicator Default Theme (XAML)

<Style TargetType="{x:Type controls:ErrorIndicator}">
    <Style.Setters>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type controls:ErrorIndicator}">
                    <Grid>
                        <ContentPresenter />
                        <Grid x:Name="PART_ErrorContainer"
                              Background="#20FF0000"
                              Visibility="Hidden">
                            <TextBlock x:Name="PART_ErrorTextBlock"
                                       TextWrapping="Wrap"
                                       HorizontalAlignment="Center"
                                       VerticalAlignment="Center"
                                       TextAlignment="Center"
                                       Foreground="Red"
                                       FontWeight="Bold"/>
                        </Grid>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style.Setters>
</Style>
  • As always any constructive feedback is welcome :)
  • The ErrorIndicator control seems to work fine, but modifying the PART_* controls in ErrorChanged handler feels a little bit grubby!? Would you realize it the same way or would you prefer a XAML only solution?
  • Is the general error handling approach something you are fine with, or is it not reasonable to show an error permanently within the main GUI?
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.