Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Form Changed Control

, 26 May 2008 CPOL
A component that allows you to monitor all the controls on the form and list any that have changed (for dirty checking)
formchangecontroltest_csharp_src.zip
Form Change Control Test CSharp
bin
Debug
Properties
Settings.settings
formchangecontrol_src.zip
Form Change Control Test
Form Change Control Test
bin
Debug
Form Change Control Test.exe
Form Change Control Test.vbproj.user
My Project
Application.myapp
Settings.settings
Form Change Control Test.suo
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms; 

namespace Form_Change_Control_Test_CSharp
{
    /// <summary>
    /// A component that raises an event whenever a control being monitored's value
    /// changes
    /// </summary>
    /// <remarks>
    /// This can be used to implement "dirty" testing to indicate that a form needs saving etc.
    /// </remarks>
    [ProvideProperty("MonitorForChanges", typeof (System.Windows.Forms.Control))]
    [ProvideProperty("ChangeEventToMonitor", typeof(System.Windows.Forms.Control))]
    [ProvideProperty("ValueNameToMonitor", typeof(System.Windows.Forms.Control))]
    public partial class FormChangedComponent : Component, System.ComponentModel.IExtenderProvider
    {

        private ChangeEventMonitoringCollection _ControlChanged = new ChangeEventMonitoringCollection();

        #region Events
        public delegate void MonitoredControlChangedEventHandler(object sender, FormChangedEventArgs e);
        public event MonitoredControlChangedEventHandler MonitoredControlChanged;
        public void OnMonitoredControlChanged(FormChangedEventArgs e)
        {
            MonitoredControlChangedEventHandler handler = MonitoredControlChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
        #endregion


        /// <summary>
        /// Resets all the changed flags for the controls on the form
        /// </summary>
        /// <remarks >
        /// You should reset the dirty flags when a record is saved, for example
        /// </remarks>
        public void ResetDirtyFlags()
        {
            foreach (ChangeEventMonitoring f in _ControlChanged)
            {
                f.Changed = false;
                OnMonitoredControlChanged(new FormChangedEventArgs(f.ControlToMonitor, false)); 
            }
        }

        /// <summary>
        /// Returns the collection of controls that have changed since the last reset
        /// </summary>
        public System.Collections.Generic.List<Control> ControlsThatHaveChanged
        { get 
           { 
             System.Collections.Generic.List<Control> cRet = new System.Collections.Generic.List<Control>();
             foreach (ChangeEventMonitoring  f in _ControlChanged)
	         {
                 if (f.Changed)
                 {
                   cRet.Add(f.ControlToMonitor ); 
                 }
	         }
             return cRet ;
           } 
        }

        [Category(@"Change Monitoring")]
        public bool GetMonitorForChanges(Control ctlIn)
        {
            return _ControlChanged.Item(ctlIn).Monitor; 
        }
        [Category(@"Change Monitoring")]
        public void SetMonitorForChanges(Control ctlIn, bool value)
        {
            _ControlChanged.Item(ctlIn).Monitor = value;
        }

        [Category(@"Change Monitoring")]
        public string GetChangeEventToMonitor(Control ctlIn)
        {
            return _ControlChanged.Item(ctlIn).ChangeEventToMonitor;
        }
        [Category(@"Change Monitoring")]
        public void SetChangeEventToMonitor(Control ctlIn, string value)
        {
            _ControlChanged.Item(ctlIn).ChangeEventToMonitor = value;
        }

        [Category(@"Change Monitoring")]
        public string GetValueNameToMonitor(Control ctlIn)
        {
            return _ControlChanged.Item(ctlIn).ValueName;
        }
        [Category(@"Change Monitoring")]
        public void SetValueNameToMonitor(Control ctlIn, string value)
        {
            _ControlChanged.Item(ctlIn).ValueName = value; 
        }

        #region Public constructors
        public FormChangedComponent()
        {
            InitializeComponent();
        }

        public FormChangedComponent(IContainer container)
        {
            container.Add(this);

            InitializeComponent();

            _ControlChanged.MonitoredControlChanged += new ChangeEventMonitoringCollection.MonitoredControlChangedEventHandler(_ControlChanged_MonitoredControlChanged);
        }

        void _ControlChanged_MonitoredControlChanged(object sender, FormChangedEventArgs e)
        {
            // Pass the event up to the control site
            this.OnMonitoredControlChanged(e);
        }
        #endregion

        #region System.ComponentModel.IExtenderProvider implementation
         bool System.ComponentModel.IExtenderProvider.CanExtend(object extendee)
        {
            if (extendee is System.Windows.Forms.Form)
            {
                return false;
            }
            if (extendee is System.Windows.Forms.Control)
            {
                return true;
            }
            return false;
        }
        #endregion


        private class ChangeEventMonitoringCollection : System.Collections.Generic.List<ChangeEventMonitoring>
        {
            /// <summary>
            /// The event raised when a control on the form is changed
            /// </summary>
            public event MonitoredControlChangedEventHandler MonitoredControlChanged;
            public void OnMonitoredControlChanged(FormChangedEventArgs e)
            {
                MonitoredControlChangedEventHandler handler = MonitoredControlChanged;
                if (handler != null)
                {
                    handler(this, e);
                }
            }
            public delegate void MonitoredControlChangedEventHandler(object sender, FormChangedEventArgs e);


            public ChangeEventMonitoring Item(Control ctl)
            {
                foreach (ChangeEventMonitoring  f in this)
                {
                    if (f.ControlToMonitor == ctl)
                    {
                        return f;
                    }
                }
                // control was not found :. add it
                ChangeEventMonitoring fret = new ChangeEventMonitoring(ctl, true);
                this.Add(fret);
                return fret;
            }

            /// <summary>
            /// Adds and connects a new control to monitor in the collection
            /// </summary>
            /// <param name="item">
            /// The item to monitor for changes
            /// </param>
            public new void  Add(ChangeEventMonitoring item)
            {
                base.Add(item);
                // Add an event handler for when this control is changed
                item.MonitoredControlChanged += this.ControlChangeEventhandler; 
            }

            /// <summary>
            /// Removes and unlinks a control to monitor
            /// </summary>
            /// <param name="item">
            /// The item to remove from the set to monitor
            /// </param>
            public new void Remove(ChangeEventMonitoring item)
            {
                this.Remove(item);
                item.MonitoredControlChanged -= this.ControlChangeEventhandler;
            }

            private void ControlChangeEventhandler(object sender, FormChangedEventArgs e)
            {
                OnMonitoredControlChanged( e  );
            }

            public ChangeEventMonitoringCollection()
            {
               
            }
        }

        private class ChangeEventMonitoring
        {
            private Control  _ctlToMonitor ;
            private bool  _Monitor =false ;
            private string _ChangeEventName;
            private string _ValueName;
            private int _CheckSum;
            private bool _Changed;
            private bool _MonitoringEvent;
            private System.EventHandler _EventHandler;

            public event EventHandler<FormChangedEventArgs> MonitoredControlChanged;  

            /// <summary>
            ///  The control that is being monitored for change events
            /// </summary>
            public Control ControlToMonitor
            { get { return _ctlToMonitor; } }

            /// <summary>
            /// Whether or not to monitor this control for change events
            /// </summary>
            public bool Monitor
            {
                get
                { 
                    return _Monitor; 
                }
                set
                { 
                    _Monitor = value;
                    this.ResetMonitoringState();
                }
            }

            /// <summary>
            /// The name of the event to monitor for changes
            /// </summary>
            public string ChangeEventToMonitor
            {
                get
                { return _ChangeEventName; }
                set
                { _ChangeEventName = value; }
            }

            /// <summary>
            /// The name of the property that exposes the 'value' of the control
            /// </summary>
            public string ValueName
            {
                get
                { return _ValueName; }
                set
                { _ValueName = value; }
            }

            /// <summary>
            /// Whether or not the control's value has changed
            /// </summary>
            /// <remarks >
            /// Set this to false to reset the control checksum
            /// </remarks>
            public bool Changed
            {
                get
                { return _Changed; }
                set
                {
                    if (! value)
                    {
                        // Reset the current checksum value
                        _CheckSum = GetCurrentChecksum();
                    }
                    _Changed = value;
                }
            }

            private void ResetMonitoringState()
            {
                System.Reflection.EventInfo evi;
                Type ctlType = _ctlToMonitor.GetType();
                evi = ctlType.GetEvent(_ChangeEventName);

                if (_MonitoringEvent )
                {
                    // Remove the event handler from the control
                    if ( evi != null)
                    {
                        System.Reflection.MethodInfo mi = evi.GetRemoveMethod(false);
                        mi.Invoke(_ctlToMonitor, new object[] { this.ChangeEventhandler() });
                    }
                }
                
                if (_Monitor )
                {
                    // Add the event handler to the control
                    if (evi != null)
                    {
                        System.Reflection.MethodInfo mi = evi.GetAddMethod(false);
                        mi.Invoke(_ctlToMonitor, new object[] { this.ChangeEventhandler() });
                        _MonitoringEvent = true;
                    }
                }

            }

            /// <summary>
            /// Get the checksum for the value of this control
            /// </summary>
            /// <returns>
            /// The checksum of the <see cref="ValueName">ValueName</see> property on this control
            /// </returns>
            private int GetCurrentChecksum()
            {
                System.Reflection.PropertyInfo _pi;
                if (! _ValueName.Equals(@"",StringComparison.InvariantCultureIgnoreCase))
                {
                    _pi = _ctlToMonitor.GetType().GetProperty(_ValueName);
                    if (_pi != null)
                    {
                        object _oval = _pi.GetValue(_ctlToMonitor, null);
                        if (_oval != null)
                        {
                            return _oval.GetHashCode(); 
                        }
                        else
                        {
                            return 0;
                        }
                    }
                    else
                    {
                        return 0;
                    }
                }
                else
                {
                    return 0;
                }
            }

            private void GenericChangeEventhandler(object sender, EventArgs args)
            {
                _Changed = !(GetCurrentChecksum() == _CheckSum   );
                MonitoredControlChanged.Invoke(this, new FormChangedEventArgs(_ctlToMonitor, _Changed));  
            }

            private Delegate  ChangeEventhandler()
            {
                if (_EventHandler == null)
                {
                    _EventHandler = new EventHandler(GenericChangeEventhandler);
                }
                return _EventHandler;
            }

            #region Public constuctors
            public ChangeEventMonitoring(Control ctlIn, bool monitorIn)
            {
                _ctlToMonitor = ctlIn;
                if (ctlIn is TextBox)
                {
                    _ChangeEventName = "TextChanged";
                    _ValueName = "Text";
                }
                if (ctlIn is   CheckBox )
                {
                    _ChangeEventName = "CheckedChanged";
                    _ValueName = "Checked"; 
                }
                if (ctlIn is ComboBox)
                {
                    _ChangeEventName = "TextChanged";
                    _ValueName = "Text";
                }
                if (ctlIn is DateTimePicker) 
                {
                    _ChangeEventName = "ValueChanged";
                    _ValueName = "Value";
                }
                if (ctlIn is Label)
                {
                    _ChangeEventName = "TextChanged";
                    _ValueName = "Text";
                }
                _CheckSum = GetCurrentChecksum();
                this.Monitor = monitorIn;
            }

            #endregion

        }

    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Duncan Edwards Jones
Software Developer (Senior)
Ireland Ireland
C# / SQL Server developer
Microsoft MVP 2006, 2007
Visual Basic .NET
Follow on   Twitter   LinkedIn

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141220.1 | Last Updated 26 May 2008
Article Copyright 2007 by Duncan Edwards Jones
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid