Implementing Observers and Subjects using IObserver/IObservable - c#

I want to create a class that can be used to represent a dynamically computed value, and another class that represents a value can be the source (subject) for these dynamically computed values. The goal is that when the subject changes, the computed value is updated automatically.
It seems to me that using IObservable/IObserver is the way to go. Unfortunately I can't use the Reactive Extensions library, so I am forced to implement the subject/observer pattern from scratch.
Enough blabla, here are my classes:
public class Notifier<T> : IObservable<T>
{
public Notifier();
public IDisposable Subscribe(IObserver<T> observer);
public void Subscribe(Action<T> action);
public void Notify(T subject);
public void EndTransmission();
}
public class Observer<T> : IObserver<T>, IDisposable
{
public Observer(Action<T> action);
public void Subscribe(Notifier<T> tracker);
public void Unsubscribe();
public void OnCompleted();
public void OnError(Exception error);
public void OnNext(T value);
public void Dispose();
}
public class ObservableValue<T> : Notifier<T>
{
public T Get();
public void Set(T x);
}
public class ComputedValue<T>
{
public T Get();
public void Set(T x);
}
My implementation is lifted mostly from: http://msdn.microsoft.com/en-us/library/dd990377.aspx.
So what would the "right" way to do this be? Note: I don't care about LINQ or multi-threading or even performance. I just want it to be simple and easy to understand.

If I were you I would try to implement your classes as closely as possible to the way Rx has been implemented.
One of the key underlying principles is the use of relatively few concrete classes that are combined using a large number of operations. So you should create a few basic building blocks and use composition to bring them all together.
There are two classes I would take an initial look at under Reflector.NET: AnonymousObservable<T> & AnonymousObserver<T>. In particular AnonymousObservable<T> is used through-out Rx as the basis for instantiating observables. In fact, if you look at the objects that derive from IObservable<T> there are a few specialized implementations, but only AnonymousObservable<T> is for general purpose use.
The static method Observable.Create<T>() is essentially a wrapper to AnonymousObservable<T>.
The other Rx class that is clearly a fit for your requirements is BehaviorSubject<T>. Subjects are both observables and observers and BehaviorSubject fits your situation because it remembers the last value that is received.
Given these basic classes then you almost have all of the bits you need to create your specific objects. Your objects shouldn't inherit from the above code, but instead use composition to bring together the behaviour that you need.
Now, I would suggest some changes to your class designs to make them more compatible with Rx and thus more composible and robust.
I would drop your Notifier<T> class in favour of using BehaviourSubject<T>.
I would drop your Observer<T> class in favour of using AnonymousObserver<T>.
Then I would modify ObservableValue<T> to look like this:
public class ObservableValue<T> : IObservable<T>, IDisposable
{
public ObservableValue(T initial) { ... }
public T Value { get; set; }
public IDisposable Subscribe(IObserver<T> observer);
public void Dispose();
}
The implementation of ObservableValue<T> would wrap BehaviourSubject<T> rather than inherit from it as exposing the IObserver<T> members would allow access to OnCompleted & OnError which wouldn't make too much sense since this class represents a value and not a computation. Subscriptions would use AnonymousObservable<T> and Dispose would clean up the wrapped BehaviourSubject<T>.
Then I would modify ComputedValue<T> to look like this:
public class ComputedValue<T> : IObservable<T>, IDisposable
{
public ComputedValue(IObservable<T> source) { ... }
public T Value { get; }
public IDisposable Subscribe(IObserver<T> observer);
public void Dispose();
}
The ComputedValue<T> class would wrap AnonymousObservable<T> for all subscribers and and use source to grab a local copy of the values for the Value property. The Dispose method would be used to unsubscribe from the source observable.
These last two classes are the only real specific implementation your design appears to need - and that's only because of the Value property.
Next you need a static ObservableValues class for your extension methods:
public static class ObservableValues
{
public static ObservableValue<T> Create<T>(T initial)
{ ... }
public static ComputedValue<V> Compute<T, U, V>(
this IObservable<T> left,
IObservable<U> right,
Func<T, U, V> computation)
{ ... }
}
The Compute method would use an AnonymousObservable<V> to perform the computation and produce an IObservable<V> to pass to the constructor of ComputedValue<V> that is returned by the method.
With all this in place you can now write this code:
var ov1 = ObservableValues.Create(1);
var ov2 = ObservableValues.Create(2);
var ov3 = ObservableValues.Create(3);
var cv1 = ov1.Compute(ov2, (x, y) => x + y);
var cv2 = ov3.Compute(cv1, (x, y) => x * y);
//cv2.Value == 9
ov1.Value = 2;
ov2.Value = 3;
ov3.Value = 4;
//cv2.Value == 20
Please let me know if this is helpful and/or if there is anything that I can elaborate on.
EDIT: Also need some disposables.
You'll also need to implement AnonymousDisposable & CompositeDisposable to manage your subscriptions particularly in the Compute extension method. Take a look at the Rx implementations using Reflector.NET or use my versions below.
public sealed class AnonymousDisposable : IDisposable
{
private readonly Action _action;
private int _disposed;
public AnonymousDisposable(Action action)
{
_action = action;
}
public void Dispose()
{
if (Interlocked.Exchange(ref _disposed, 1) == 0)
{
_action();
}
}
}
public sealed class CompositeDisposable : IEnumerable<IDisposable>, IDisposable
{
private readonly List<IDisposable> _disposables;
private bool _disposed;
public CompositeDisposable()
: this(new IDisposable[] { })
{ }
public CompositeDisposable(IEnumerable<IDisposable> disposables)
{
if (disposables == null) { throw new ArgumentNullException("disposables"); }
this._disposables = new List<IDisposable>(disposables);
}
public CompositeDisposable(params IDisposable[] disposables)
{
if (disposables == null) { throw new ArgumentNullException("disposables"); }
this._disposables = new List<IDisposable>(disposables);
}
public void Add(IDisposable disposable)
{
if (disposable == null) { throw new ArgumentNullException("disposable"); }
lock (_disposables)
{
if (_disposed)
{
disposable.Dispose();
}
else
{
_disposables.Add(disposable);
}
}
}
public IDisposable Add(Action action)
{
if (action == null) { throw new ArgumentNullException("action"); }
var disposable = new AnonymousDisposable(action);
this.Add(disposable);
return disposable;
}
public IDisposable Add<TDelegate>(Action<TDelegate> add, Action<TDelegate> remove, TDelegate handler)
{
if (add == null) { throw new ArgumentNullException("add"); }
if (remove == null) { throw new ArgumentNullException("remove"); }
if (handler == null) { throw new ArgumentNullException("handler"); }
add(handler);
return this.Add(() => remove(handler));
}
public void Clear()
{
lock (_disposables)
{
var disposables = _disposables.ToArray();
_disposables.Clear();
Array.ForEach(disposables, d => d.Dispose());
}
}
public void Dispose()
{
lock (_disposables)
{
if (!_disposed)
{
this.Clear();
}
_disposed = true;
}
}
public IEnumerator<IDisposable> GetEnumerator()
{
lock (_disposables)
{
return _disposables.ToArray().AsEnumerable().GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public bool IsDisposed
{
get
{
return _disposed;
}
}
}

Related

How to link IObservable and IObserver in C#?

I've been studying Observer parttern since this morning, but can't seem to figure out how to implement it with the built-in interfaces. I already looked at some examples but couldn't find any simple example yet.
Here's my code so far, inspired by the Microsoft Documentation :
class ObservableClass : IObservable<bool>, IDisposable
{
public bool observableBool;
public List<IObserver<bool>> observers;
public ObservableClass()
{
this.observableBool = false;
this.observers = new List<IObserver<bool>>();
}
public IDisposable Subscribe(IObserver<bool> observer)
{
if (!observers.Contains(observer))
{
AddObserver(observer);
}
return this;
}
public void Dispose()
{
Console.WriteLine("Disposing...");
}
public void AddObserver(IObserver<bool> obs)
{
this.observers.Add(obs);
}
public void RemoveObserver(IObserver<bool> obs)
{
this.observers.Remove(obs);
}
public void SwapBool()
{
observableBool = !observableBool;
}
}
the observable class contains an observableBool field. I want to notify the Observer when that field changes value.
Here's my Observer :
class ObserverClass : IObserver<bool>
{
public IDisposable observable;
public void OnCompleted()
{
Console.WriteLine("Completed");
}
public void OnError(Exception error)
{
Console.WriteLine("error");
}
public void OnNext(bool value)
{
Console.WriteLine("Next");
}
public virtual void Subscribe(IObservable<bool> obs)
{
if (obs != null)
observable = obs.Subscribe(this);
}
public void stopObserve()
{
observable.Dispose();
}
}
And finally my Program :
static void Main(string[] args)
{
ObservableClass observable = new ObservableClass();
ObserverClass observer = new ObserverClass();
observer.Subscribe(observable);
Console.WriteLine("subscribed observer");
observable.SwapBool();
Console.WriteLine("performed swapBool");
}
Expected output :
subscribed observer
Completed //Returned by ObserverClass.OnComplete()
performed swapBool
How to make this work ?
How to call on OnComplete and the other methods of ObserverClass everytime observableBool changes ?
I know there are other ways to do that, but my goal is to be able to use IObserver and IObservable.
You iterate over your set of observables to notify them:
public void SwapBool()
{
observableBool = !observableBool;
foreach (observable in observers)
{
observable.OnNext(observableBool);
}
}
You are meant to call OnNext when there is a new value. OnComplete is used to notify that there will be no more values.
I just noticed your observable is IDisposable...
First of all, disposing the result of Subscribe should unsubscribe that observer. Not dispose the observable.
In fact, I would expect that disposing the observable means that it will no longer be sending values (calls OnComplete on everybody and releases the list of observers).
Other concerns include:
You probably want a set type so you can add and remove observables more efficiently.
List is not thread-safe.
Why are you exposing your fields?

Count all subscriptions of a subject

I have a Subject where I subscribe methods that should be called when a determined event in a game happens.
public Subject<SomeEvent> TestSubject = new Subject<SomeEvent>();
Some instances subscribe to that Subject.
TestSubject.Subscribe(MyMethod);
My objective is to count how many methods have been subscribed to that Subject. I've seen some examples using Count() extension but I need an int as a return value so then I can use it somewhere else, and Count() returns an IObservable.
if (subjectCount > 0)
{
DoSomething();
}
Is there any way to get the number of subscriptions on a subject or do I need to keep track of them manually (having a public int SubjectSubcriptions and adding 1 everytime I subscribe a method) ?
Easiest way would be to create your own implementation of ISubject with a wrapper around a subject.
public class CountSubject<T> : ISubject<T>, IDisposable
{
private readonly ISubject<T> _baseSubject;
private int _counter;
private IDisposable _disposer = Disposable.Empty;
private bool _disposed;
public int Count
{
get { return _counter; }
}
public CountSubject()
: this(new Subject<T>())
{
// Need to clear up Subject we created
_disposer = (IDisposable) _baseSubject;
}
public CountSubject(ISubject<T> baseSubject)
{
_baseSubject = baseSubject;
}
public void OnCompleted()
{
_baseSubject.OnCompleted();
}
public void OnError(Exception error)
{
_baseSubject.OnError(error);
}
public void OnNext(T value)
{
_baseSubject.OnNext(value);
}
public IDisposable Subscribe(IObserver<T> observer)
{
Interlocked.Increment(ref _counter);
return new CompositeDisposable(Disposable.Create(() => Interlocked.Decrement(ref _counter)),
_baseSubject.Subscribe(observer));
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_disposer.Dispose();
}
_disposed = true;
}
}
}
I am curious if this is only for testing purposes or not. If so, then the Rx-Testing nuget package has the tools to give you this information.
e.g. you can validate the number of subscription in a unit test like this
TestScheduler scheduler = new TestScheduler();
var obs = scheduler.CreateColdObservable(
ReactiveTest.OnNext(1, "foo"),
ReactiveTest.OnNext(1000, "bar"),
);
//Do some work that should add subscriptions.
Assert.AreEqual(expectedSubriptionCount, obs.Subscriptions);

RegisterCallback<T>(Action<T> func) , how do I store this function pointer in a class?

I'm trying to expose an API such that, I do the following
RegisterCallback<T>(Action<T> func)
{
someObj.FuncPointer = func;
}
Later on, I call func(obj) .. and the obj is of type T that the user said.
More concrete example:
var callbackRegistrar = new CBRegistrar();
callbackRegistrar.RegisterCallback<ISomeClass>(SomeFunc);
public static void SomeFunc(ISomeClass data)
{
//
}
EDIT: So I may not have been clear, so I'll add more code:
I want to make only "one" object of CBRegistrar, and connect it with many Callbacks, as such:
var callbackRegistrar = new CBRegistrar();
callbackRegistrar.RegisterCallback<ISomeClass>(SomeFunc);
callbackRegistrar.RegisterCallback<ISomeOtherClass>(SomeFunc2);
...
In fact the above code is called by reflecting over a directory of plugins.
The user puts this in their code -->
public static void SomeFunc(ISomeClass data)
{
//
}
public static void SumFunc2(ISomeOtherClass data)
{
//
}
It looks to me as if this is not possible using Generics, etc. What it looks like I might have to do is make an interface called IPlugin or something, and ask the user to do this ..
[PluginIdentifier(typeof(ISomeClass))]
public static void SomeFunc(IPluginData data)
{
var castedStuff = data as ISomeClass; // ISomeClass inherits from IPluginData
}
Seems like asking the user to do stuff that we should take care of, but anyway ...
You need a Action<T> func to store it in. There is a semantic check to make here: if someone calls RegisterCallback twice (with different values), do you want to replace the callback, or keep both ? Assuming the latter, someObj probably wants an event (indeed, this entire API could be exposed as an event), so - in the someObj class:
public event Action<T> FuncPointer;
private void InvokeCallback(T data) {
var handler = FuncPointer;
if(handler != null) handler(data);
}
Noting that RegisterCallback could be replaced entirely, still keeping the data on obj:
public event Action<T> Completed {
add { obj.FuncPointer += value; }
remove { obj.FuncPointer -= value; }
}
Then usage would be:
var callbackRegistrar = new CBRegistrar();
callbackRegistrar.Completed += SomeFunc;
Callback functions are not much used in C#. They've been replaced by events which are more elegant and easier to work with.
class CBRegistrar
{
public delegate void ActionRequiredEventHandler(object sender, ISomeClass e);
public event ActionRequiredEventHandler ActionRequired;
void RaiseActionRequiredEvent(ISomeClass parm)
{
if ( ActionRequired != null)
{
ActionRequired(this, parm);
}
}
}
class APIConsumer
{
var callbackRegistrar = new CBRegistrar();
public APIConsumer()
{
callbackRegistrar.ActionRequired += SomeFunc;
}
public void SomeFunc(object sender, ISomeClass data)
{
}
}
If you still want to use Callbacks, you can use Delegates which are more or less function pointer.
The CBRegistrar will need to be generic (if it's OK to keep a single callback type) or it can do some internal casting (if several callback types need to be registered).
public class CBRegistrar<T>
{
private Action<T> callback;
private Dictionary<Type, object> callbackMap;
public CBRegistrar()
{
this.callbackMap = new Dictionary<Type, object>();
}
public void RegisterCallback(Action<T> func)
{
this.callback = func;
}
public void RegisterGenericCallback<U>(Action<U> func)
{
this.callbackMap[typeof(U)] = func;
}
public Action<U> GetCallback<U>()
{
return this.callbackMap[typeof(U)] as Action<U>;
}
}
public interface ISomeClass
{
string GetName();
}
public class SomeClass : ISomeClass
{
public string GetName()
{
return this.GetType().Name;
}
}
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var callbackRegistrar = new CBRegistrar<ISomeClass>();
callbackRegistrar.RegisterCallback(SomeFunc);
callbackRegistrar.RegisterGenericCallback<ISomeClass>(SomeFunc);
var someone = new SomeClass();
callbackRegistrar.GetCallback<ISomeClass>()(someone);
}
public static void SomeFunc(ISomeClass data)
{
// Do something
Console.WriteLine(data.GetName());
}
}
}

WeakReference - Am I doing it right?

I have a static class, which exposes a event:
public static class MyStaticClass
{
static bool myBool= false;
public static bool MyBool
{
get { return myBool; }
private set
{
myBool= value;
var handler = MyBoolChanged;
if (handler != null)
handler(null, null);
}
}
public static event EventHandler MyBoolChanged;
}
And then I am registering to it using this pattern:
class AnotherClass
{
WeakReference _me;
public MyMethodInAnotherClass()
{
_me = new WeakReference(this);
MyStaticClass.MyBoolChanged+=
(_me.Target as AnotherClass).MyMethodInAnotherClassCallback;
}
private void MyMethodInAnotherClassCallback(some arguments)
{
}
}
What I want to achieve is that MyStaticClass will only execute the handler if the instance of AnotherClass has not been disposed (and has not deregistered).
The best way I can see to use this is to forget about an event, and use some kind of list instead; let's say List<WeakReference>; you could then have:
interface IFoo {
void Bar(some args);
}
with:
static class Whatever {
private static readonly List<WeakReference> items=new List<WeakReference>();
public static void Add(IFoo foo) {
if(foo != null) {
var newRef = new WeakReference(foo);
lock(items) { items.Add(newRef); }
}
}
public static void DoIt(some args) {
lock(items) {
foreach(var item in items) {
IFoo foo = item.IsAlive ? item.Target as IFoo : null;
if(foo != null) foo.Bar(some args);
}
}
}
}
with additional mechanisms to remove a specific IFoo, and to remove all dead foos left todo.
Then you just need AnotherClass : IFoo, with a Bar() implementation that applies your callback.
Additional emphasis: static collections (including events) are fairly dangerous; you must have some kind of sweep occasionally to remove empty items, and try to unsubscribe promptly where possible (in Dispose(), for example). As an illustration:
public static void Remove(IFoo foo) {
lock (items) { // also remove any dead debris
items.RemoveAll(x => !x.IsAlive || x.Target == foo || x.Target == null);
}
}

Applying the Decorator Pattern to Forms

I'm trying to apply the Decorator Design Pattern to the following situation:
I've 3 different kind of forms: Green, Yellow, Red.
Now, each of those forms can have different set of attributes. They can have a minimize box disabled, a maximized box disabled and they can be always on top.
I tried to model this the following way:
Form <---------------------------------------FormDecorator
/\ /\
|---------|-----------| |----------------------|-----------------|
GreenForm YellowForm RedForm MinimizeButtonDisabled MaximizedButtonDisabled AlwaysOnTop
Here is my GreenForm code:
public class GreenForm : Form {
public GreenForm() {
this.BackColor = Color.GreenYellow;
}
public override sealed Color BackColor {
get { return base.BackColor; }
set { base.BackColor = value; }
}
}
FormDecorator:
public abstract class FormDecorator : Form {
private Form _decoratorForm;
protected FormDecorator(Form decoratorForm) {
this._decoratorForm = decoratorForm;
}
}
and finally NoMaximizeDecorator:
public class NoMaximizeDecorator : FormDecorator
{
public NoMaximizeDecorator(Form decoratorForm) : base(decoratorForm) {
this.MaximizeBox = false;
}
}
So here is the running code:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(CreateForm());
}
static Form CreateForm() {
Form form = new GreenForm();
form = new NoMaximizeDecorator(form);
form = new NoMinimizeDecorator(form);
return form;
}
The problem is that I get a form that isn't green and that still allows me to maximize it. It is only taking in consideration the NoMinimizeDecorator form. I do comprehend why this happens but I'm having trouble understanding how to make this work with this Pattern.
I know probably there are better ways of achieving what I want. I made this example as an attempt to apply the Decorator Pattern to something. Maybe this wasn't the best pattern I could have used(if one, at all) to this kind of scenario. Is there any other pattern more suitable than the Decorator to accomplish this? Am I doing something wrong when trying to implement the Decorator Pattern?
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.
try to make your pattern to apply the decorator properties to the same object, not creating new Forms:
public abstract class FormDecorator {
protected Form _decoratorForm;
protected FormDecorator(Form decoratorForm) {
this._decoratorForm = decoratorForm;
}
public abstract void Decorate();
}
public class NoMaximizeDecorator : FormDecorator
{
public NoMaximizeDecorator(Form decoratorForm) : base(decoratorForm) {
Decorate();
}
public override void Decorate() {
_decoratorForm.MaximizeBox = false;
}
}
And in your Main:
static Form CreateForm() {
Form form = new GreenForm();
new NoMaximizeDecorator(form);
new NoMinimizeDecorator(form);
return form;
}

Categories

Resources