简体   繁体   English

用winforms实现观察者模式

[英]Implementing an observer pattern with winforms

I have one collection of objects that many of my forms (using WeifenLuo.WinFormsUI.Docking) need to interact with. 我有一个对象集合,我的许多表单(使用WeifenLuo.WinFormsUI.Docking)都需要与之交互。

ie 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... 但是,我不能这样做,只能使用WeifenLuo.WinFormsUI.Docking库...

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... 好吧,我可以让DataCollectionObserver从WeifenLuo.WinFormsUI.Docking继承DockContent,但是这会导致我需要两个DataCollectionObserver类-一个继承Form的类,另一个继承DockContent的类:-[或者,我可以使DataCollectionObserver成为接口,但那仍然给我留下了重复的代码……

So, does anyone have a suggestion here? 那么,有人在这里有建议吗? Am I missing something obvious, or is this a situation where duplication of code 'must be done' 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(). 我要问的原因是,由于代码的块复制和粘贴(这是我拥有的这四种不同形式的代码,它们全都“闻起来”),这些四种形式订阅了收集事件并在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: FWIW,这是我的收藏课:

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 : 具有两个自定义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. 我从Form继承了我的东西,但是无论您想要什么,都可以继承它。 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] [EDIT]糟糕,看不到“编辑”,抱歉,朋友![/ EDIT]

如果使用的是.net的哪个版本,如果> = 3.5,则可以使用ObservableCollection来实现您正在执行的操作

There is an 'ObservableCollection' in the Framework just for this type of thing. 框架中仅针对此类事物有一个“ ObservableCollection”。 See here . 这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM