Question

So, I got a huge style template dictionary for every control and for ValidationErrorTemplate as well. The problem is, that we should show validation error below the control when there is no place above the control. Basicly for controls at the top of the window. For controls at the bottom of the window the validation should be shown above the control.

Since it a resource dictionary where every styles are defined there is no code-behind and also no data binding is possible.

One idea would be to determine the AdornedElementPlaceholder's position and hide / show the template respectivly. But I haven't find any solution to do that in XAML.

    <ControlTemplate x:Key="ValidationErrorTemplate">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid Grid.Row="1">
                <Border>
                    <AdornedElementPlaceholder />
                </Border>                
            </Grid>
            <AdornerDecorator Grid.Row="????">
                <Border >
                    <!-- some style comes here ...  -->
                </Border>
            </AdornerDecorator>
        </Grid>
    </ControlTemplate>

Grid.Row="????" should be either 0 or 1, depending on the top of the control.

Was it helpful?

Solution 2

So I finally have found the solution: attached property. I created an attach property and on the property change callback method subscribed on the AdornerDecorator.Loaded event. In the method you can check the real position and change properties if needed.

[Sample code-snippet, in the real source it is more outsourced and rechecked due code-specific issues]

private static void DecoratorLoaded(object obj, RoutedEventArgs e)
{
     var decorator = obj as Decorator;
     if (decorator != null && decorator.IsVisible)
     {
        // get the position
        Point renderedLocation = decorator.TranslatePoint(new Point(0, 0), Application.Current.MainWindow);
        if (renderedLocation != new Point(0, 0))
        {
           // check width
           var maxAllowedWidth = Application.Current.MainWindow.ActualWidth - renderedLocation.X - 40;               
           decorator.SetValue(FrameworkElement.MaxWidthProperty, maxAllowedWidth);

           // check place above the control
           var isEnoughPlaceAbove = renderedLocation.Y > decorator.ActualHeight + 10;
           decorator.SetValue(Grid.RowProperty, isEnoughPlaceAbove ? 0 : 2);

           // invalidate to re-render
           decorator.InvalidateVisual();               
        }
     }
}

You need to use Loaded event to make sure the renderLocation will deliver you the actual position and not something else (e.g zero or some relative positions).

Finally you need to attach the attached property to the decorator in XAML:

<AdornerDecorator Behaviors:AdornerPositionCalculator.AllowDynamicPosition="True">
  <!-- custom style here -->
</AdornerDecorator>

OTHER TIPS

Have two separate templates( one opposite of the other), and use one for the items on top and one for the items on bottom as deemed fit by whatever object contains the controls you are speaking of.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top