【问题标题】:Setting a local style for an adorner为装饰者设置本地风格
【发布时间】:2022-09-23 18:15:00
【问题描述】:

我有一个装饰的,应该放在它的装饰元素旁边。根据自定义 Position 依赖属性的值,装饰器出现在元素的左侧或右侧。

我想使用一种样式来设置Position 属性的值。但是只有将样式添加到顶级控件的资源中才能做到这一点。如果我将样式放在任何子元素的资源中,它不会显示任何效果。

有没有一种方法可以像下面的示例一样基于每个元素设置装饰器样式?

<Window x:Class=\"StyledAdorner.MainWindow\"
        xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\"
        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"
        xmlns:local=\"clr-namespace:StyledAdorner\">
    <Window.Resources>
        <Style TargetType=\"local:MyAdorner\">
            <Setter Property=\"Position\" Value=\"Right\" />
        </Style>
        <Style TargetType=\"Button\">
            <Setter Property=\"Content\" Value=\"Adorn me!\" />
            <Setter Property=\"Margin\" Value=\"15\" />
            <EventSetter Event=\"Click\" Handler=\"AddAdorner\" />
        </Style>
    </Window.Resources>
    <StackPanel>
        <Button />
        <Button>
            <Button.Resources>
                <Style TargetType=\"local:MyAdorner\">
                    <!-- This setter has no effect! -->
                    <Setter Property=\"Position\" Value=\"Left\" />
                </Style>
            </Button.Resources>
        </Button>
    </StackPanel>
</Window>

我能想象的唯一解决方案是扫描装饰元素的资源以获取装饰风格。如果有,则检查是否有 Position 属性的设置器并使用此值。但这看起来像一个非常肮脏的黑客......


  • 用于创建装饰器的AddAdorner 处理程序的代码:
private void AddAdorner(object sender, RoutedEventArgs e)
{
    new MyAdorner((UIElement)sender);
}
  • MyAdorner 的构造函数
private Path _indicator = new Path { /* details omitted */ };

public MyAdorner(UIElement adornedElement) : base(adornedElement)
{
    AdornerLayer.GetAdornerLayer(AdornedElement).Add(this);
    AddVisualChild(_indicator);
    InvalidateMeasure();
    InvalidateArrange();
}
  • 装饰者是Button 的孩子吗?如果没有,您在&lt;Button.Resources&gt; 中的样式将不适用。如何添加装饰器?
  • 装饰者装饰按钮,因此它不是按钮的子元素。但即使我将样式放在StackPanel 中,它也不会应用于装饰器。我认为这是因为装饰器被添加到装饰器层而不是窗口的内容呈现器中......我在我的问题中添加了代码部分。
  • 那么,为什么不直接在添加装饰器时以编程方式设置 Position 属性呢?
  • 我可以这样做,但位置是一个与设计相关的属性,我想通过样式而不是代码来设置它。使用上面提供的解决方案,我可以为该职位定义全局(应用程序范围)样式。如果我想更改它,我只需要更改样式而不是MyAdorner 的每个构造函数调用。也许我将使用MyAdorner 中的附加属性来覆盖特定元素的位置...
  • 如果要在应用程序范围内定义属性,请设置属性的默认值?或者将Style 移动到App.xaml

标签: wpf adorner


【解决方案1】:

我可以通过将 Position 属性转换为附加属性来解决我的问题:

public static readonly DependencyProperty PositionProperty = DependencyProperty.RegisterAttached(
    "Position",
    typeof(AdornerPosition),
    typeof(MyAdorner),
    new FrameworkPropertyMetadata(AdornerPosition.Right, UpdateAdornerLayerLayout));

现在,我只需要在装饰元素上设置所需的值:

<Button local:MyAdorner.Position="Left">

在计算装饰器的位置时,在装饰器的ArrangeOverride 方法中评估该属性。

请注意,UpdateAdornerLayerLayout 属性更改回调是必要的,以在属性更改时强制对装饰层进行布局更新:

private static void UpdateAdornerLayerLayout(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    if (d is UIElement element)
    {
        var layer = AdornerLayer.GetAdornerLayer(element);
        layer?.Update();
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多