WPF : How to get a control from the DataTemplate of the Validation.ErrorTemplate of the adorned control

480 Views Asked by At

I am using INotifyDataErrorInfo to trigger the Validation.

I am not able to get a control from the Validation.ErrorTemplate. I need the stackpanel's Actual Height, to add that height to the bottom margin of the adorned element so the errors will be shown below the adorned element and any controls below will be shifted downwards.

I have tried using visual tree inside the converter, but nothing happens. Also i have tried using XAML still nothing.

<converters:ErrorsToMarginMultiConverter x:Key="ErrorsToMarginMultiConverter"/>
    <Style TargetType="{x:Type Control}" x:Key="ValidationStyle">
        <Setter Property="VerticalAlignment" Value="Top"/>
        <Setter Property="Height" Value="25"/>
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <StackPanel>
                        <!--The Original Control-->
                        <AdornedElementPlaceholder x:Name="OriginalControl"/>
                        <!--The Errors List-->
                        <ItemsControl ItemsSource="{Binding}" x:Name="ErrorItemsControl">
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <BulletDecorator Width="{Binding ElementName=OriginalControl, Path=ActualWidth, UpdateSourceTrigger=PropertyChanged}">
                                        <BulletDecorator.Bullet>
                                            <Ellipse Fill="Red" Width="5" Height="5"/>
                                        </BulletDecorator.Bullet>
                                        <TextBlock Text="{Binding ErrorContent, StringFormat=' {0}'}" Foreground="Red" TextWrapping="Wrap"/>
                                    </BulletDecorator>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </StackPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>

        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <Setter Property="BorderThickness" Value="2" />
                <Setter Property="BorderBrush" Value="DarkRed" />
                <Setter Property="Margin">
                    <Setter.Value>
                        <MultiBinding Converter="{StaticResource ErrorsToMarginMultiConverter}" UpdateSourceTrigger="PropertyChanged">
                            <Binding Path="(Validation.Errors)" RelativeSource="{RelativeSource Self}"/>
                            <Binding RelativeSource="{RelativeSource Self}"/>
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>

I expect to get somehow the controls inside the validation error template as parameters to my converter or to get them with code inside the converter in order to extract their actual height.

Here is what is happening by default with this template Here is the expected result

I want to get that stackpanel's height in order to add it as a bottom margin to the original control (TextBox) to achieve the expected result when there are Validation Errors. Tree View Converter Code Sample

EDIT: I've managed to find a partial solution,

public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value != null)
            {
                var adornedElement = value[1] as Control;
                var adornerLayer = System.Windows.Documents.AdornerLayer.GetAdornerLayer(adornedElement);
                var templatedAdorner = adornerLayer.GetAdorners(adornedElement);

                var margin = adornedElement.Margin;
                margin.Bottom = 20;

                if (templatedAdorner != null && templatedAdorner.Length > 0)
                    margin.Bottom = templatedAdorner[0].ActualHeight;

                return margin;
            }

            return value;
        }

The problem now is that this converter is triggered two times, the first time when clearing the previous validation errors and the second time when i add the errors. The first time it find the templatedAdorner and read's it's Actual Height. But the second time if find the adorner layer but then no templatedAdorned exist (null). In my visual tree i can see them. Also the validation errors count is not correct, although they are shown correctly in the view.

The trigger is ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); from INotifyDataErrorInfo.

0

There are 0 best solutions below