如何在 WPF 中实现自定义画笔?

发布于 2024-07-18 06:27:49 字数 438 浏览 7 评论 0原文

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


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

public AwesomeBrush : Brush
{

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

    ... // concrete brush stuff

}

Where can I find out enough info about how Brushes work to implement my own System.Windows.Media.Brush? I can handle all of the freezable baggage, but it's not really obvious what I need to override to get it to work.


Yeah, so I didn't mean that I want to use a predefined brush. I want to extend System.Windows.Media.Brush, which is an abstract class. This is all purely for my own edification. I'm not even sure what kind of brush I could make. I was just trying to learn how brushes work. As in:

public AwesomeBrush : Brush
{

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

    ... // concrete brush stuff

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

黑色毁心梦 2024-07-25 06:27:49

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

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

编辑后更新

由于这是为了教育目的,您最好下载 Reflector 并使用它来查看画笔的工作原理。 它们并不是要自我实现的,并且由于它们依赖于程序员通常无法访问的一些内部类,因此很难做到这一点。

不过有趣的是 Brush 文档 确实有备注,供继承者指导以正确的方式继承Brush。

更多更新

在四处探索时,我发现了一种非常巧妙的方法来在 中文博客< /a>. 诀窍是使用标记扩展,使其看起来像一个画笔。 实际上,它根据其属性创建了一个新的图像画笔。 似乎他得出了同样的结论,即存在一些内部类阻碍了简单的实现。

I had a quick look at the existing brushes using the Reflector. It seems like their implementation is pretty closed up and depends on lots of internal plumbing. While it might be possible to implement your own brush it seems like it is not a supported option. It could even be that the WPF controls are tied tightly to the existing brushes and will not work with a custom one.

Most likely the best way to achieve something resembling custom brushes is to use the DrawingBrush with some complex drawing logic. You can compose drawing brush from complex shapes using other brushes so this should let you achieve the required goal.

Update after edit

As this is for education, you might be best off downloading the Reflector and using that to see how the brushes work. They are not meant to be self-implemented and since they rely on some internal classes to which programmers do not have access normally it will be quite hard to do so.

Though what makes it interesting is that the Brush documentation does have a remark for inheritors to guide in the correct way to inherit from the Brush.

More updates

When poking around I found a quite neat way to achieve something similar on a chinese blog. The trick there is to use a markup extension so it just looks like a brush. In reality it creates a new image brush based on its attributes. Seems he came to the same conclusion that there are some internal classes which prevent the easy implementation.

倒带 2024-07-25 06:27:49

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

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

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

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

事实上,WPF 的画笔功能非常强大,但同时又非常有限。 它们显然是针对某种概念模型设计的,但我完全不确定它除了一些相当微不足道的视觉效果之外还有用,并且像往常一样,它通过奇怪的(记录不详)参数变得更加复杂,这使得做非常简单,有些复杂。

To answer the question of why you might want to have a custom brush, consider a complex geometry and you want to fill it with a gradient that matches the shape while changing colours radially from the center of the shape (or any arbitrary starting point).

There is no way to do this with a static fill (which eliminates pretty much all the existing brushes). Short of replicating the object over and over and nesting them (and working out the hulling to make it all look right), filling each one with a different colour, the only practical way is to have a brush that can look at the object being filled.

This is actually doable in Forms.

But it doesn't look like there's any way to do it in WPF, which is unfortunate.

In fact, WPF's brushes are at the same time surprisingly powerful AND surprisingly limited. They're clearly designed to some concept model, but I'm not at all sure it's one that's useful beyond some pretty trivial visual effects, and as usual, it's been made more complex with strange (poorly documented) parameters that makes doing the very simple, somewhat complicated.

赢得她心 2024-07-25 06:27:49

我尝试了所有可能的角度来寻找制作自定义画笔的解决方案,从使用 MarkupExtensions 到使用 TypeConverter 属性,然后我突然意识到:您只需创建一个基于 DependencyObject 的包装类,创建一个 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 解决方案更好,因为您不能将自定义画笔放入资源中,因此每次使用时都必须重新定义自定义设置。

无论如何,这似乎是从任何 Wpf 对象继承的简单解决方案,并且借助绑定的灵活性,您还可以绑定到包装的 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         
}

I tried every possible angle for a solution to making a custom brush, from using MarkupExtensions to messing around with TypeConverter attributes, then it dawwned on me: you simply create a wrapper class based on DependencyObject, create a DependencyProperty of type Brush, implement your customization then Bind to the Brush DependencyProperty.

Afterwards, put the custom brush in Resources

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

Then Bind to it:

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

I think this is better than the MarkupExtension solution because you can't put one in Resources and therefore you have to redefine the custom settings every time you use it.

In any case, this would seem to be a simple solution to inheriting from any Wpf object, and with the flexibility of Bindings you can also bind to members of the wrapped Wpf object itself.

Here's the simple class:

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         
}
不甘平庸 2024-07-25 06:27:49

您无法通过继承 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 的评论。

You cannot make a custom WPF brush by inheriting from the System.Windows.Media.Brush class, because that class contains abstract members that are internal.

If you use Reflector to view the source code of System.Windows.Media.Brush class, you will see the following internal abstract methods:

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);

These must be overridden but cannot be because they are internal.

Note: Edited my answer because previous answer was about System.Drawing.Brush and irrelevant to this question. Special thanks to Mikko Rantanen for his comment.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文