'In my WPF control, can I hide the validation control based on the size of the adorned element's row?

I have a user control with many sub controls within a grid. Since there are many controls per row, I'm controlling the visibility of the controls by setting the row height of their containing row to 0 (to hide them).

I'm using a validation template on some of these controls and displaying an icon next to the control using AdornedElementPlaceholder.

Since I'm not actually setting the visibility property of the adorned control, but instead hiding the row, the validation icon is not collapsed with the rest of the control.

Here's an abridged version of my XAML code:

<UserControl
    <UserControl.Resources>
    <ControlTemplate x:Key="ValidationTemplate" TargetType="Control">
        <DockPanel>
            <Grid
                Width="16"
                Height="16"
                Margin="10,0,0,0"
                VerticalAlignment="Center"
                DockPanel.Dock="Right">
                <Image Source="{x:Static icons:Icons.ValidationIcon}" ToolTip="{Binding Path=ErrorContent}" />
            </Grid>
            <AdornedElementPlaceholder />
        </DockPanel>
    </ControlTemplate>
    </UserControl.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="32"/>
            <RowDefinition Height="{Binding Path=ScheduleType, Mode=OneWay, Converter={StaticResource ScheduleTypesToGridRowHeightConverter}, ConverterParameter={x:Static local:ScheduleTypes.Other}}"/>
            <RowDefinition Height="{Binding Path=ScheduleType, Mode=OneWay, Converter={StaticResource ScheduleTypesToGridRowHeightConverter}, ConverterParameter={x:Static local:ScheduleTypes.Fixed}}"/>
            <RowDefinition Height="{Binding Path=ScheduleType, Mode=OneWay, Converter={StaticResource ScheduleTypesToGridRowHeightConverter}, ConverterParameter={x:Static local:ScheduleTypes.Weekly}}"/>
            <RowDefinition Height="{Binding Path=ScheduleType, Mode=OneWay, Converter={StaticResource ScheduleTypesToGridRowHeightConverter}, ConverterParameter={x:Static local:ScheduleTypes.FreeText}}"/>
        </Grid.RowDefinitions>
        <controls:DateInputBox
            Grid.Column="2"
            Grid.Row="5"
            Height="28"
            HorizontalAlignment="Left"
            Watermark=""
            Width="110"
            Text="{Binding StartDateText, ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
            Validation.ErrorTemplate="{StaticResource ValidationTemplate}"
            VerticalAlignment="Center"
            ParseComplexDates="True"/>
    </Grid>
</UserControl>

From what I understand from a little research is that the validation icon is being displayed in the adorner layer, so doesn't collapse with the rest of the controls.

I'm now thinking that the "row height visibility pattern" was maybe not the best approach ;-) Is there a way I can get this to work without having to completely change my design? I do have a workaround using my view model but I'd like to explore other options first.



Solution 1:[1]

If you really whant to stick with the design, you could remove and add the adornents depending of what you whant to display. However, this would be pretty messy and defeat the point of sticking with the current desing for simplicity in the first place.

The concept of hiding elements by setting their parent element size to zero is indeed not the best option. Partly because of the exact issue you mentioned. (The adornents visibility is dependent on its owners visibility. And the owner elements visibility is visible, there is just no space to render it.)

A better option would be to only show whatever is to be shown. An easy way to accomplish this would be to encapsulate all the elements you whant to hide/show together inside one element or user control and then hide or show only that parent element or user control.

This could for instance look like this:

    
    <UserControl.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVis" /> 
    </UserControl.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="32"/>
            <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Border Grid.Row="1" Visibility="{Binding FixedIsVisible, Converter={StaticResource BoolToVis}">
            <!-- fixed shedule content here -->
        </Border>
        <Border Grid.Row="1" Visibility="{Binding WeeklyIsVisible, Converter={StaticResource BoolToVis}">
            <!-- weekly shedule content here -->
        </Border>
        <Border Grid.Row="1" Visibility="{Binding FretextIsVisible, Converter={StaticResource BoolToVis}">
            <!-- free text shedule content here -->
        </Border>
    </Grid>

By using some other converter you also yould directly bind you ScheduleType so you dont need to touch your view model.

A maybe even better option would be to make it so that only the elements you need at a time exist inside the view. This could be acomplished by defining different user controls and then have your grid line contain only the desired control at a time. (I might add an example for this later when i have time to do so.)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1