开发者

Implementing an observer pattern with winforms

开发者 https://www.devze.com 2023-01-22 06:56 出处:网络
I have one collection of objects that many of my forms (using WeifenLuo.WinFormsUI.Docking) need to interact with.

I have one collection of objects that many of my forms (using WeifenLuo.WinFormsUI.Docking) need to interact with.

i.e. If the collection has an addition (or deletion) made in one form, then the other forms respond by refreshing their views.

Obviously, an observer pattern would be a good candidate here. However, I am having issues trying to implement this in my program.

First, it seemed best to create an observer class for my collection like so:

public class DataCollectionObserver : Form
{
    internal static void DataCollectionRegister(DataCollection dataCollection)
    {
        dataCollection.ImageAdded += new EventHandler(dataAdded);
        dataCollection.ImageRemoved += new EventHandler(dataRemoved);
        dataCollection.ImageIndexChanged += new EventHandler(dataIndexChanged);
        dataCollection.ImageListCleared += new EventHandler(dataListCleared);
    }

    internal static void DataCollectionUnRegister(DataCollection dataCollection)
    {
        dataCollection.ImageAdded -= new EventHandler(dataAdded);
        dataCollection.ImageRemoved -= new EventHandler(dataRemoved);
        dataCollection.ImageIndexChanged -= new EventHandler(dataIndexChanged);
        dataCollection.ImageListCleared -= new EventHandler(dataListCleared);
    }

    internal static void dataAdded(object sender, EventArgs e) {}
    internal static void dataRemoved(object sender, EventArgs e) {}
    internal static void dataIndexChanged(object sender, EventArgs e) {}
    internal static void dataListCleared(object sender, EventArgs e) {}
}

Then override the base event handlers in the subclassed forms?

But, I cannot do this and use the WeifenLuo.WinFormsUI.Docking library...

Well, I could have DataCollectionObserver inherit DockContent from WeifenLuo.WinFormsUI.Docking, but that creates a situation where I need to have two DataCollectionObserver classes - one which inherits Form and another that inherits DockContent :-[ or, I could make the DataCollectionObserver an Interface, but that still leaves me with duplicate code laying about...

So, does anyone have a suggestion here? Am I missing something obvious, or is this a situation where duplication of code 'must be don开发者_开发问答e' for the sake of simplicity?


Edit://

I am not having problems getting notification in my forms. As a matter of fact, the whole thing is working right now. The reason that I am asking is because the whole thing "smells" due to the block copy and paste of code in these four different forms that I have which subscribe to the collection events and unsubscribe on Form.Closing().

What I would like to do is implement the behavior that I have copied and pasted to these four forms in one place and have the forms that should receive collection change notifications implement that behavior as needed.

Hope that makes things clearer?

FWIW, this is my collection class:

using System;
using System.Collections;
using System.Reflection;

namespace MyNameSpace.Collections
{
    /// <summary>
    /// Generic Collection of Objects with Events
    /// </summary>
    public class CollectionWithEvents<T> : CollectionBase
    {
        public bool SuppressEventNotification
        {
            get;
            set;
        }

        public CollectionWithEvents()
        {
            SuppressEventNotification = false;
        }

        #region Events
        /// <summary>
        /// Raises before an item is added to the list.
        /// </summary>
        public event EventHandler<ItemEventArgs<T>> BeforeItemAdded;

        /// <summary>
        /// Raises when an item is added to the list.
        /// </summary>
        public event EventHandler<ItemEventArgs<T>> ItemAdded;

        /// <summary>
        /// Raises before a collection of items is added to the list.
        /// </summary>
        public event EventHandler<ItemsEventArgs<T>> BeforeItemsAdded;

        /// <summary>
        /// Raises when a collection of items is added to the list.
        /// </summary>
        public event EventHandler<ItemsEventArgs<T>> ItemsAdded;

        /// <summary>
        /// Raises before an item is changed in the list.
        /// </summary>
        public event EventHandler<ItemEventArgs<T>> BeforeItemChanged;

        /// <summary>
        /// Raises when an item is changed in the list.
        /// </summary>
        public event EventHandler<ItemEventArgs<T>> ItemChanged;

        /// <summary>
        /// Raises before an item is removed from the list.
        /// </summary>
        public event EventHandler<ItemEventArgs<T>> BeforeItemRemoved;

        /// <summary>
        /// Raises when an item is removed from the list.
        /// </summary>
        public event EventHandler<ItemEventArgs<T>> ItemRemoved;

        /// <summary>
        /// Raises when the items are cleared from the list.
        /// </summary>
        public event EventHandler<EventArgs> ItemsCleared;
        #endregion

        public T this[int index]
        {
            get { return (T)this.List[index]; }
            set
            {
                if (!SuppressEventNotification)
                {
                    OnBeforeItemChanged(this, new ItemEventArgs<T>(value));
                }

                this.List[index] = value;

                if (!SuppressEventNotification)
                {
                    OnItemChanged(this, new ItemEventArgs<T>(value));
                }
            }
        }

        public int Add(T item)
        {
            if (!SuppressEventNotification)
            {
                OnBeforeItemAdded(this, new ItemEventArgs<T>(item));
            }

            int retValue = this.List.Add(item);

            if (!SuppressEventNotification)
            {
                OnItemAdded(this, new ItemEventArgs<T>(item));
            }

            return retValue;
        }

        public void AddRange(Collection<T> collection)
        {
            T[] tmp = new T[collection.Count];

            collection.CopyTo(tmp, 0);

            AddRange(tmp);
        }

        public void AddRange(T[] collection)
        {
            if (!SuppressEventNotification)
            {
                OnBeforeItemsAdded(this, new ItemsEventArgs<T>(collection));
            }

            this.AddRange(collection);

            if (!SuppressEventNotification)
            {
                OnItemsAdded(this, new ItemsEventArgs<T>(collection));
            }
        }

        public bool Contains(T item)
        {
            return this.List.Contains(item);
        }

        public void CopyTo(Array array, int index)
        {
            this.List.CopyTo(array, index);
        }

        public int IndexOf(T item)
        {
            return this.List.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            this.List.Insert(index, item);
        }

        public void Remove(T item)
        {
            if (!SuppressEventNotification)
            {
                OnBeforeItemRemoved(this, new ItemEventArgs<T>(item));
            }

            T tmp = (T)item;

            this.List.Remove(item);

            if (!SuppressEventNotification)
            {
                OnItemRemoved(this, new ItemEventArgs<T>(tmp));
            }

            tmp = default(T);
        }

        public void Sort(string Property, Common.SortOrder Order)
        {
            Common.GenericComparer genericComparer = new Common.GenericComparer(Property, Order);
            this.InnerList.Sort(genericComparer);
        }

        #region Event Methods
        /// <summary>
        /// Raised before an Item is added to the list.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">ItemEventArgs</param>
        protected virtual void OnBeforeItemAdded(object sender, ItemEventArgs<T> e)
        {
            if (BeforeItemAdded != null)
            {
                BeforeItemAdded(sender, e);
            }
        }

        /// <summary>
        /// Raised when an Item is added to the list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">ItemEventArgs</param>
        protected virtual void OnItemAdded(object sender, ItemEventArgs<T> e)
        {
            if (ItemAdded != null)
            {
                ItemAdded(sender, e);
            }
        }

        /// <summary>
        /// Raised before a collection of Items is added to the list.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">ItemEventArgs</param>
        protected virtual void OnBeforeItemsAdded(object sender, ItemsEventArgs<T> e)
        {
            if (BeforeItemsAdded != null)
            {
                BeforeItemsAdded(sender, e);
            }
        }

        /// <summary>
        /// Raised when a collection of Items is added to the list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">ItemEventArgs</param>
        protected virtual void OnItemsAdded(object sender, ItemsEventArgs<T> e)
        {
            if (ItemsAdded != null)
            {
                ItemsAdded(sender, e);
            }
        }

        /// <summary>
        /// Raised before an Item is changed to the list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">GenericItemEventArgs</param>
        protected virtual void OnBeforeItemChanged(object sender, ItemEventArgs<T> e)
        {
            if (BeforeItemChanged != null)
            {
                BeforeItemChanged(sender, e);
            }
        }

        /// <summary>
        /// Raised when an Item is changed to the list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">ItemEventArgs</param>
        protected virtual void OnItemChanged(object sender, ItemEventArgs<T> e)
        {
            if (ItemChanged != null)
            {
                ItemChanged(sender, e);
            }
        }

        /// <summary>
        /// Raised before an Item is removed from the list.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e">ItemEventArgs</param>
        protected virtual void OnBeforeItemRemoved(object sender, ItemEventArgs<T> e)
        {
            if (BeforeItemRemoved != null)
            {
                BeforeItemRemoved(sender, e);
            }
        }

        /// <summary>
        /// Raised when an Item is removed from the list.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">ItemEventsArgs</param>
        protected virtual void OnItemRemoved(object sender, ItemEventArgs<T> e)
        {
            if (ItemRemoved != null)
            {
                ItemRemoved(sender, e);
            }
        }

        /// <summary>
        /// Raised when the Items are cleared from this list.
        /// </summary>
        /// <param name="sender">object</param>
        /// <param name="e">EventArgs</param>
        protected virtual void OnItemsCleared(object sender, EventArgs e)
        {
            if (ItemsCleared != null)
            {
                ItemsCleared(sender, e);
            }
        }
        #endregion
    }

    public class ItemEventArgs<T> : EventArgs
    {
        /// <summary>
        /// Item
        /// </summary>
        public T Item { get; private set; }

        /// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="Item"></param>
        public ItemEventArgs(T Item)
        {
            this.Item = Item;
        }
    }

    public class ItemsEventArgs<T> : EventArgs
    {
        /// <summary>
        /// Items
        /// </summary>
        public T[] Items { get; private set; }

        /// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="Items"></param>
        public ItemsEventArgs(T[] Items)
        {
            this.Items = Items;
        }
    }
}


I might be mistaken. But you could do it this way and your inheritance problem would not be present. I'll try to give a simple example :

You collection class could be of this kind :

public class MyCollection
    {
        IList<string> MyList { get; set; }

        public event EventHandler<StringEventArgs> OnAdded;
        public event EventHandler<StringEventArgs> OnRemoved;

        public MyCollection()
        {
            MyList = new List<string>();
        }

        public void Add(string s)
        {
            MyList.Add(s);

            if (OnAdded != null)
                OnAdded(this, new StringEventArgs() { StringAddedOrRemoved = s });
        }

        public void Remove(string s)
        {
            MyList.Remove(s);
            if (OnRemoved != null)
                OnRemoved(this, new StringEventArgs() { StringAddedOrRemoved = s });
        }
    }

Really simple class with two customized EventHandlers :

public class StringEventArgs : EventArgs
    {
        public string StringAddedOrRemoved;


        public override string ToString()
        {
            return StringAddedOrRemoved;
        }
    }

Nothing hard to understand here and then based on the three forms you could use your forms this way.

The first one holds two buttons to interact with the collection and creates the two forms which will observe your collection :

public partial class Form1 : Form
    {
        public static MyCollection collection;


        public Form1()
        {

            InitializeComponent();

            collection = new MyCollection();

            Form2 form2 = new Form2();
            form2.Show();

            Form3 form3 = new Form3();
            form3.Show();

            collection.OnAdded += form2.MyCollectionAdded;
            collection.OnRemoved += form2.MyCollectionRemoved;

            collection.OnAdded += form3.MyCollectionAdded;
            collection.OnRemoved += form3.MyCollectionRemoved;

        }

        private void Add_Click(object sender, EventArgs e)
        {
            collection.Add("test add");
        }

        private void button1_Click(object sender, EventArgs e)
        {
            collection.Remove("test add");
        }


    }

The collection gets linked to each of the functions of the forms that will be observed here :

collection.OnAdded += form2.MyCollectionAdded;
                collection.OnRemoved += form2.MyCollectionRemoved;

                collection.OnAdded += form3.MyCollectionAdded;
                collection.OnRemoved += form3.MyCollectionRemoved;

And so we need to implement those forms :

public partial class Form2 : Form
    {


        public string Name { get; set; }
        public bool Flag { get; set; }

        public Form2()
        {
            InitializeComponent();
        }

        public void MyCollectionAdded(object sender, StringEventArgs e)
        {
            //Some action
            Flag = true;
            label1.Text = string.Format("{0} has added {1} to its list, flag={2}", Name, e.StringAddedOrRemoved, Flag);
        }

        public void MyCollectionRemoved(object sender, StringEventArgs e)
        {
            //Some action
            Flag = false;
            label1.Text = string.Format("{0} has removed {1} from its list, flag={2}", Name, e.StringAddedOrRemoved, Flag);
        }
    }

I am inheriting my stuff from Form but it could be inherited form whatever you want actually. If you want to share some code among different forms, think of helper functions in a static class, or of whatever pattern that might suit your need.

Hope it helps a bit, and that I am not completely out of scope!

[EDIT] Oops didn't see the Edit, sorry pal![/EDIT]


Which version of .net ,you are using if it is >= 3.5 , you can make use of ObservableCollection to achive what ever you are doing


There is an 'ObservableCollection' in the Framework just for this type of thing. See here.

0

精彩评论

暂无评论...
验证码 换一张
取 消