tags:

views:

1612

answers:

4

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

}
A: 

You can not make a custom brush (by inheriting from the Brush class) because the Brush class and all its derivatives are either abstract or sealed.

To take advantage of the WPF Brushes visit: MSDN: WPF Brushes Overview

EDIT: What is it about Brush that makes it unable to be inherited?

We can not inherit from the Brush class. This is because the designer of System.Drawing.Brush class do not want us to inherit from it. If you use Reflector to view the source code of System.Drawing.Brush class, you will see that its constructor is defined like this: internal Brush() { }

Yes, it is marked with "internal", which means that only classes in the same assembly can access it. That is why the following classes inherited from System.Drawing.Brush class:

  • System.Drawing.Drawing2D.HatchBrush
  • System.Drawing.Drawing2D.LinearGradientBrush
  • System.Drawing.Drawing2D.PathGradientBrush
  • System.Drawing.SolidBrush
  • System.Drawing.TextureBrush

Because they all reside in the System.Drawing.dll assembly.

But if we want to inherit from Brush class, we will get the following compile time error: "'System.Drawing.Brush.Brush()' is inaccessible due to its protection level"

This issue is documented in the KB below: You receive error messages when you try to inherit from a class that contains only private constructors in Visual Studio .NET

So we can not inherit from the Brush class, nor we can inherit from any of its derivatives because they are all sealed. -- so we can not create custom brushes!

M. Jahedbozorgan
You can inherit from Brush class - as you said, it's abstract and abstract classes MUST be inherited.
Mikko Rantanen
What is it about Brush that makes it unable to be inherited? I know all of the implementations are sealed, but we should be able to derive from Brush, right?
MojoFilter
Damn you internal constructors! I've been foiled by you before!
MojoFilter
@M. Jahedbozorgan: What you state here is correct of the System.Drawing.Brush class... The WPF version CAN be derived from. See System.Windows.Media.Brush.
Josh G
The constructor is not internal. protected Brush(); / Declaring Type: System.Windows.Media.Brush / Assembly: PresentationCore, Version=3.0.0.0 - however you are close to the problem. The brush does have internal abstract methods, just not the constructor. One of the worst is `internal abstract DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel);` in which the parameters are of internal type as well. These must be overridden but cannot be as they are internal.
Mikko Rantanen
@Josh. That is quite wrong. There is nothing stopping you deriving from the System.Drawing.Brush class. Deriving from the WPF Brush is impossible though. Not because of the constructor but because of the other abstract internal methods.
Mikko Rantanen
@Mikko Rantanen: Maybe so, but the point is that System.Drawing.Brush is different than System.Windows.Media.Brush and is irrelevant to this discussion. As you said in your answer, it appears that only Microsoft can implement child classes for Brush even though the documentation refers to inheritors.
Josh G
+5  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.

Mikko Rantanen
I suspect that's the case, as well. I can't imagine anything that couldn't be done with the brush implementations provided in the framework, I was just wondering if there might be fun ways to componentize such things to make it really slick for reuse.
MojoFilter
Added an update which achieves something similar without extending Brush.
Mikko Rantanen
I wonder if the note in the brush documentation to inheritors was for Microsoft personal and was not supposed to make it into the final doc. As I look at Reflector, it looks like a lot of the essential fields in Brush and also GradientBrush are marked internal. So we can't get at them.
Josh G
+1  A: 

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         
}
countzero1984