WPF expander validation error not shown when expanded
Asked Answered
M

1

6

Using MVVM. I have a DataTemplate which I am using to display an expander with some controls in per object.

<DataTemplate>
    <Expander ExpandDirection="Down" IsExpanded="False">
        <Expander.Header>
            <TextBlock>
                <TextBlock.Text>
                    <MultiBinding StringFormat="Platform Group {0} {1}">
                        <Binding Path="PlatformGroupCode"/>
                        <Binding Path="PlatformGroupName"/>
                    </MultiBinding>
                </TextBlock.Text>
            </TextBlock>
        </Expander.Header>
        <vw:PlatformGroup HorizontalAlignment="Left"/>
    </Expander>
</DataTemplate>

Inside that view is 2 textboxes bound to those 2 properties. I'm using IDataErrorInfo in my VM to do validation and I've a style in my main application resources to display error messages as tooltips:

<Style TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self},Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
        </Trigger>
    </Style.Triggers>
</Style>

When a new group is added the 2 properties have default values, which is invalid so I want the textboxes to be red to prompt the user to enter data. This works if the Expander's IsExpanded is set to true. But if it is false I have to expand AND change the value in one of the textboxes in order to get the red border and tooltip to show.

I don't want to set the expander to be expanded because there will eventually be quite a few controls. How can I get the red border to show as soon as the expander is expanded? Even better, is there a way to make the newly added expander expand (when user adds a new group I'm adding a PlatformGroupviewModel to an observablecollection of PlatformGroupviewModels)?

EDIT more detail: the top level view:

 <StackPanel Orientation="Vertical">
        <ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="630">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="5*" />
                    <ColumnDefinition Width="5*" />
                </Grid.ColumnDefinitions>
                <StackPanel Orientation="Vertical" Grid.ColumnSpan="2" Grid.Row="1" HorizontalAlignment="Stretch">
                    <Expander ExpandDirection="Down" IsExpanded="True" Header="Header" HorizontalAlignment="Stretch" Name="expHeader" VerticalAlignment="Top">
                        <vw:Header DataContext="{Binding HeaderVM}"/>
                    </Expander>
                    <Expander ExpandDirection="Down" IsExpanded="True" Header="Platform Groups" HorizontalAlignment="Stretch" Name="expPlatformGroups" VerticalAlignment="Top">
                        <AdornerDecorator>
                            <vw:PlatformGroups DataContext="{Binding PlatformGroupsVM}"/>
                        </AdornerDecorator>
                    </Expander>
                </StackPanel>
            </Grid>
        </ScrollViewer>
</StackPanel>

the PlatformGroups view:

 <StackPanel Orientation="Vertical" HorizontalAlignment="Stretch" Margin="10,10,10,10">
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" Margin="0,10">
        <Label Content="Number of platform groups" VerticalAlignment="Center"/>
        <vw:IntegerInput MinValue="0" MaxValue="50" MaxLength="2"  Text="{Binding Path=NumPlatformGroups, Mode=TwoWay,ValidatesOnDataErrors=True}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
    </StackPanel>
    <ItemsControl IsTabStop="False" ItemsSource="{Binding PlatformGroups}" Margin="20,10" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Expander ExpandDirection="Down" IsExpanded="False">
                    <Expander.Header>
                        <TextBlock>
                            <TextBlock.Text>
                                <MultiBinding StringFormat="Platform Group {0} {1}">
                                    <Binding Path="PlatformGroupCode"/>
                                    <Binding Path="PlatformGroupName"/>
                                </MultiBinding>
                            </TextBlock.Text>
                        </TextBlock>
                    </Expander.Header>
                    <AdornerDecorator>
                        <vw:PlatformGroup HorizontalAlignment="Left"/>
                    </AdornerDecorator>
                </Expander>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.Template>
            <ControlTemplate>
                <Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}"
        BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True">
                    <ScrollViewer VerticalScrollBarVisibility="Auto" MaxHeight="400" CanContentScroll="True" Padding="{TemplateBinding Control.Padding}" Focusable="False">
                        <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </ItemsControl.Template>
    </ItemsControl>
</StackPanel>
Mesics answered 11/6, 2012 at 10:16 Comment(2)
Do you have ValidatesOnDataErrors=True set on your TextBox bindings?Erector
Yes I do (although I haven't shown the code for the 2 textboxes). The validation error displays if IsExpanded = true. This seems to be a known problem with expanders, but the known solution akjoshi posted doesn't seem to work in this case.Mesics
T
3

As per this post, wrapping your Expander content inside an AdornerDecorator should solve this problem -

<DataTemplate>
    <Expander ExpandDirection="Down" IsExpanded="False">
        <Expander.Header>
           ...
        </Expander.Header>

        <AdornerDecorator>
            <vw:PlatformGroup HorizontalAlignment="Left"/>
        </AdornerDecorator>

    </Expander>
</DataTemplate>

Another SO thread which confirms this - Issue with WPF validation(IDataErrorInfo) and tab focusing

Some other WorkArounds are also mentioned on this connect bug -

TabControl doesn't display Validation error information correctly when switching tabs back and forth

Tamqrah answered 11/6, 2012 at 11:33 Comment(3)
I tried and it made no difference. The expander is itself nested in another expander but putting the adornerdecorator on that didn't help either. I'll update my question with a bit more code.Mesics
I've finally come back to this. It seems you have to take care exactly where you put the AdornerDecorator. Inside my PlatformGroup view was a stack panel, with a grid inside, and the textboxes were inside the grid. When I put the AdornerDecorator around the grid, rather than the entire contents of the expander, it worked.Mesics
Glad you found the solution, yes sometimes it becomes tricky in WPF and to understand the exact reason behind those things is tough.Tamqrah

© 2022 - 2024 — McMap. All rights reserved.