Multiple Decorators
Now that we see how a single decorator works, what about multiple decorators? It could be that we’d like to decorate our CoolButtons with another decoration— say, a diagonal red line.
This is only slightly more complicated because we just need to enclose the CoolDecorator inside yet another Decorator panel for more decoration to occur. The only real change is that we not only need the instance of the panel we are wrapping in another but also the central object (here a button) being decorated, since we have to attach our paint routines to that central object’s paint method.
So we need to create a constructor for our Decorator that has both the enclosing panel and the button as controls.
public class CoolDecorator :Panel, Decorator { protected Control contl; protected Pen bPen, wPen, gPen; private bool mouse_over; protected float x1, y1, x2, y2; //---------------------------------- public CoolDecorator(Control c, Control baseC) { //the first control is the one laid out //the base control is the one whose paint method we extend //this allows for nesting of decorators contl = c; this.Controls.AddRange(new Control[] {contl} );
Then when we add the event handlers, the paint event handler must be attached to the base control.
//paint handler catches button's paint baseC.Paint += new PaintEventHandler( paint);
We make the paint method virtual so we can override it as we see here.
public virtual void paint(object sender, PaintEventArgs e){ //draw over button to change its outline Graphics g = e.Graphics;
It turns out that the easiest way to write our SlashDecorator, which draws that diagonal red line, is to derive it from CoolDecorator directly. We can reuse all the base methods and extend only the paint method from the CoolDecorator and save a lot of effort.
public class SlashDeco:CoolDecorator { private Pen rPen; //---------------- public SlashDeco(Control c, Control bc):base(c, bc) { rPen = new Pen(Color.Red , 2); } //---------------- public override void paint(object sender, PaintEventArgs e){ Graphics g = e.Graphics ; x1=0; y1=0; x2=this.Size.Width ; y2=this.Size.Height ; g.DrawLine (rPen, x1, y1, x2, y2); } }
This gives us a final program that displays the two buttons, as shown in Figure 17-2. The class diagram is shown in Figure 17-3.
Figure 17-2. The A CoolButton is also decorated with a SlashDecorator.
Figure 17-3. The UML class diagram for Decorators and two specific Decorator implementations