C# WPF ValidationErrorTemplate样式动态位置

C# WPF ValidationErrorTemplate样式动态位置,c#,wpf,validation,controltemplate,resourcedictionary,C#,Wpf,Validation,Controltemplate,Resourcedictionary,因此,我为每个控件和ValidationErrorTemplate都提供了一个巨大的样式模板字典。问题是,当控件上方没有位置时,我们应该在控件下方显示验证错误。基本上用于窗口顶部的控件。对于窗口底部的控件,验证应显示在控件上方 由于它是一个定义了所有样式的资源字典,因此没有代码隐藏,也没有数据绑定 一个想法是确定装饰元素占位符的位置并分别隐藏/显示模板。但在XAML中,我还没有找到任何解决方案 <ControlTemplate x:Key="ValidationErrorTempl

因此,我为每个控件和ValidationErrorTemplate都提供了一个巨大的样式模板字典。问题是,当控件上方没有位置时,我们应该在控件下方显示验证错误。基本上用于窗口顶部的控件。对于窗口底部的控件,验证应显示在控件上方

由于它是一个定义了所有样式的资源字典,因此没有代码隐藏,也没有数据绑定

一个想法是确定
装饰元素占位符的位置并分别隐藏/显示模板。但在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=“?”应为0或1,具体取决于控件的顶部

有两个单独的模板(一个相反),一个用于顶部的项目,另一个用于底部的项目,只要对象包含您所说的控件,就可以认为它们是合适的。

因此我终于找到了解决方案:attached property。我在AdornerDecorator.Loaded事件上订阅的属性更改回调方法上创建了一个attach属性和。在该方法中,您可以检查实际位置,并根据需要更改属性

[示例代码片段,在真实源代码中,由于特定于代码的问题,它更容易外包和重新检查]

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();               
        }
     }
}
您需要使用
Loaded
事件来确保渲染位置将为您提供实际位置,而不是其他位置(例如零或某些相对位置)

最后,您需要将附加属性附加到XAML中的装饰器:

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


听起来不错,但我们不想为每个带有键的控件定义明确的样式。特别是,因为我们开发了一个框架,所以不能保证他们不会忘记添加正确的样式键。您是否考虑过使用DataTrigger根据AdorneDeletePlaceholder的值设置Grid.Row?我以前从未尝试过这种方法,但我认为它会按照你描述的方式工作。