Applying the Decorator Pattern to Forms
The problem here is that you're not actually implementing the decorator pattern. For a proper implementation of the pattern, you need to subclass Form
to create your decorator, and then intercept all operations taken on your decorator and forward them to your private Form
instance. You sort of do that, except that aside from assigning a reference in the FormDecorator
constructor, you never again use that private Form
instance. The net result is that you create a GreenForm
, then wrap it in a NoMaximizeDecorator
, and then you wrap that in a NoMinimizeDecorator
. But because you never forward operations taken against the NoMinimizeDecorator
to the wrapped Form
instance, only the NoMinimizeDecorator
instance actually applies any behavior to the instance that's used. This fits with what you observe when you run your code: a standard window with a disabled Minimize button.
Form
is a really bad example for creating decorators in C#, because most of its properties and methods are non-virtual, meaning if you're accessing the decorated form via a Form
reference, you have no way to intercept the base class's properties - you can't effectively "wrap" Form
.
EDIT
It occurs to me that the statement "Form is a really bad example for creating decorators in C#" really begs the question of what is a good example. Typically, you'll use the decorator pattern to provide a custom interface implementation without implementing the entire implementation from scratch. A very common example is generic collections. Most everything that wants list functionality doesn't depend on, e.g., List<String>
, but rather on IList<String>
. So, if you for example want a custom collection that won't accept strings shorter than 5 characters, you would use something like the following:
public class MinLengthList : IList<String>
{
private IList<string> _list;
private int _minLength;
public MinLengthList(int min_length, IList<String> inner_list)
{
_list = inner_list;
_minLength = min_length;
}
protected virtual void ValidateLength(String item)
{
if (item.Length < _minLength)
throw new ArgumentException("Item is too short");
}
#region IList<string> Members
public int IndexOf(string item)
{
return _list.IndexOf(item);
}
public void Insert(int index, string item)
{
ValidateLength(item);
_list.Insert(index, item);
}
public void RemoveAt(int index)
{
_list.RemoveAt(index);
}
public string this[int index]
{
get
{
return _list[index];
}
set
{
ValidateLength(value);
_list[index] = value;
}
}
#endregion
#region ICollection<string> Members
public void Add(string item)
{
ValidateLength(item);
_list.Add(item);
}
public void Clear()
{
_list.Clear();
}
public bool Contains(string item)
{
return _list.Contains(item);
}
public void CopyTo(string[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _list.Count; }
}
public bool IsReadOnly
{
get { return _list.IsReadOnly; }
}
public bool Remove(string item)
{
return _list.Remove(item);
}
#endregion
#region IEnumerable<string> Members
public IEnumerator<string> GetEnumerator()
{
return _list.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((IEnumerable)_list).GetEnumerator();
}
#endregion
}
public class Program
{
static void Main()
{
IList<String> custom_list = new MinLengthList(5, new List<String>());
custom_list.Add("hi");
}
}
This is a misapplication of the decorator pattern. The decorator pattern is concerned with the behavior of objects. You're constructing objects which falls under the creational umbrella. While you might be able to wrap your head around "not having a maximize button" being a behavior it sounds a little off kilter.
I don't think there's a real way to fix your design though. The decorator pattern just doesn't fit. Any attempt to fix this up is just going to be incredibly crufty when you could just use a Builder.
What I could see doing is decorating the Builder of a form to perform these actions while building. It would look something like this...
public interface IFormBuilder {
public Form BuildForm();
}
public class FormBuilder : IFormBuilder {
public Form BuildForm(){
return new Form();
}
}
public class NoMaximizeFormBuilder : IFormBuilder {
private IFormBuilder _builder;
public NoMaximizeFormBuilder (IFormBuilder builder){
_builder = builder;
}
public Form BuildForm(){
f = _builder.BuildForm();
f.MaximizeBox = false;
return f;
}
}
And you could use it like this...
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(CreateForm());
}
static Form CreateForm() {
var b = new FormBuilder();
var b = new NoMaximizeFormBuilder(b);
return b.Build();
}
But even that is a little ugly. You might be able to transform this into a fluent interface for building forms.