【问题标题】:How do I implement a custom Brush in WPF?如何在 WPF 中实现自定义画笔?
【发布时间】:2009-04-27 17:38:22
【问题描述】:

我在哪里可以找到有关画笔如何实现我自己的 System.Windows.Media.Brush 的足够信息?我可以处理所有可冷冻的行李,但我需要覆盖什么才能让它工作并不是很明显。


是的,所以我并不是说我想使用预定义的画笔。我想扩展 System.Windows.Media.Brush,它是一个抽象类。这一切纯粹是为了我自己的熏陶。我什至不确定我能做什么样的刷子。我只是想了解刷子是如何工作的。如:

public AwesomeBrush : Brush
{

    protected override Freezable CreateInstanceCore()
    {
        return new AwesomeBrush();
    }

    ... // concrete brush stuff

}

【问题讨论】:

标签: .net wpf brush


【解决方案1】:

我使用Reflector 快速浏览了现有的画笔。看起来他们的实现非常封闭,并且依赖于很多 internal 管道。虽然可以实现自己的画笔,但它似乎不是一个受支持的选项。甚至可能 WPF 控件与现有画笔紧密相关,无法与自定义画笔一起使用。

实现类似于自定义画笔的最佳方法很可能是使用带有一些复杂绘图逻辑的DrawingBrush。您可以使用其他画笔从复杂形状组成绘图画笔,这样您就可以实现所需的目标。

编辑后更新

由于这是用于教育,您最好下载反射器并使用它来查看画笔的工作原理。它们并不是要自行实现的,因为它们依赖于程序员通常无法访问的一些 internal 类,因此很难这样做。

虽然有趣的是,Brush documentation 确实有一个注释供继承者以正确的方式从 Brush 继承。

更多更新

当我四处寻找时,我发现了一种在chinese blog 上实现类似功能的非常巧妙的方法。诀窍是使用标记扩展,所以它看起来就像一个画笔。实际上,它会根据其属性创建一个新的图像画笔。似乎他得出了相同的结论,即有一些内部类阻碍了简单的实现。

【讨论】:

  • 我怀疑情况也是如此。我无法想象框架中提供的画笔实现无法完成任何事情,我只是想知道是否有有趣的方法可以将这些东西组件化以使其真正易于重用。
  • 添加了一个更新,可以在不扩展 Brush 的情况下实现类似的效果。
  • 我想知道刷机文档中给继承者的注释是否是针对 Microsoft 个人的,不应该将其写入最终文档。当我查看 Reflector 时,看起来 Brush 和 GradientBrush 中的许多基本字段都标记为内部。所以我们无法接近他们。
  • 感谢您的有用回答;可惜这不是公认的。我希望有一种更灵活的方式来实现自定义Brush 派生类。在某些情况下使用DrawingBrush 的想法很好,但我不知道如何将它用于其他情况。例如,如果我想实现 LinearGradientBrush 的变体,其中包含某种伽玛控制(即,停止之间的插​​值不是严格线性的)?
【解决方案2】:

要回答为什么您可能想要自定义画笔的问题,请考虑一个复杂的几何图形,并且您希望使用与形状匹配的渐变填充它,同时从形状中心(或任何任意起点)径向更改颜色点)。

没有办法用静态填充来做到这一点(这几乎消除了所有现有的画笔)。没有一遍又一遍地复制对象并将它们嵌套(并计算出外壳以使其看起来都正确),用不同的颜色填充每个对象,唯一实用的方法是使用可以查看正在填充的对象的画笔.

这实际上在 Forms 中是可行的。

但在 WPF 中似乎没有任何方法可以做到这一点,这很不幸。

事实上,WPF 的画笔同时具有惊人的强大功能和惊人的局限性。它们显然是为某些概念模型设计的,但我完全不确定它是否有用,除了一些非常微不足道的视觉效果,而且像往常一样,它变得更加复杂,带有奇怪的(记录不充分的)参数,这使得做简单,有点复杂。

【讨论】:

    【解决方案3】:

    我尝试了所有可能的角度来寻找制作自定义画笔的解决方案,从使用 MarkupExtensions 到使用 TypeConverter 属性,然后它让我大吃一惊:你只需创建一个基于 DependencyObject 的包装类,创建一个 Brush 类型的 DependencyProperty,实现您的自定义,然后绑定到 Brush DependencyProperty。

    然后,将自定义画笔放入资源中

      <l:CustomBrush x:Key="CustomBrush" Brush="Magenta" />
    

    然后绑定到它:

      <Line X1="0" Y1="-5" X2="200" Y2="-5" 
            Stroke="{Binding Source={StaticResource CustomBrush}, Path=Brush}" 
            StrokeThickness="12"/>
    

    我认为这比 MarkupExtension 解决方案更好,因为您不能在 Resources 中放置一个,因此每次使用它时都必须重新定义自定义设置。

    无论如何,这似乎是从任何 Wpf 对象继承的简单解决方案,并且借助 Bindings 的灵活性,您还可以绑定到被包装的 Wpf 对象本身的成员。

    这是一个简单的类:

    public class CustomBrush : DependencyObject
    {
       public CustomBrush()
       {
          this.Brush = Brushes.Azure;      
       }
    
       #region Brush DependencyProperty
    
       [BindableAttribute(true)]
       public Brush Brush
       {
         get { return (Brush)GetValue(BrushProperty); }
         set { SetValue(BrushProperty, value); }
       }
       public static readonly DependencyProperty BrushProperty =
          DependencyProperty.Register(
             "Brush",
             typeof(Brush),
             typeof(CustomBrush),
             new UIPropertyMetadata(null));
    
       #endregion         
    }
    

    【讨论】:

      【解决方案4】:

      您不能通过从 System.Windows.Media.Brush 类继承来制作自定义 WPF 画笔,因为该类包含内部的抽象成员。

      如果你使用Reflector查看System.Windows.Media.Brush类的源码,你会看到如下内部抽象方法:

      internal abstract DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel);
      internal abstract DUCE.Channel GetChannelCore(int index);
      internal abstract int GetChannelCountCore();
      internal abstract DUCE.ResourceHandle GetHandleCore(DUCE.Channel channel);
      internal abstract void ReleaseOnChannelCore(DUCE.Channel channel);
      

      这些必须被覆盖,但不能被覆盖,因为它们是内部的。

      注意:编辑了我的答案,因为之前的答案是关于 System.Drawing.Brush 并且与这个问题无关。特别感谢 Mikko Rantanen 的评论。

      【讨论】:

      • 你可以从 Brush 类继承——正如你所说,它是抽象类,抽象类必须被继承。
      • 该死的内部构造函数!我以前被你挫败过!
      • @M. Jahedbozorgan:您在这里所说的 System.Drawing.Brush 类是正确的...... WPF 版本可以派生自。请参阅 System.Windows.Media.Brush。
      • 构造函数不是内部的。受保护的画笔(); /声明类型:System.Windows.Media.Brush /程序集:PresentationCore,版本= 3.0.0.0 - 但是你接近问题了。画笔确实有内部抽象方法,只是没有构造函数。最糟糕的情况之一是internal abstract DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel);,其中参数也是内部类型。这些必须被覆盖,但不能因为它们是内部的。
      • @Josh。这是完全错误的。没有什么可以阻止您从 System.Drawing.Brush 类派生。但是,从 WPF 画笔派生是不可能的。不是因为构造函数,而是因为其他抽象的内部方法。
      猜你喜欢
      • 1970-01-01
      • 2018-10-04
      • 1970-01-01
      • 1970-01-01
      • 2020-10-02
      • 2014-12-16
      • 1970-01-01
      • 1970-01-01
      • 2016-12-18
      相关资源
      最近更新 更多