Tuesday, 5 February 2008

Event override in c# (with a taste of reflection)

Have you ever tried to override a event suscription in c#?

Let’s say you have a form with a control inherited from a button, and you want to INTERCEPT THE EVENT SUSCRIPTION.

For instance if you have a second button, you want that whenever somebody suscribes to the click event of this button, it also suscribes to the event click of another button or change the target event handler, or log it or whatever processing you can imagine.

In my example I’m going to use event suscription override to automatically use the event handler as the handler of the click of another button

That is, that writing this:

this.button1.Click += new System.EventHandler(this.button1_Click);

Is going to be implicitly equivalent to this:

this.button1.Click += new System.EventHandler(this.button1_Click);

this.button2.Click += new System.EventHandler(this.button1_Click);

and I’m going to do this through event overriding (Full test vs2005 project can be downloaded here )

As you probably know, this is not possible in c#

    class InheritedButton : Button

    {

        // That's not allowed

        public override Click ...

so we are going to use a little trick (including reflection) to simulate that.

First, I am going to define a new ‘EventHandler’ class that includes a virtual function to allow extension on inherited classes.

Things to note:

1.  The constructor includes both the control and the event name you want to handle (so we can use in reflection)

2. The class defines a operator + to keep the common event suscription sintaxis

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

using System.Collections;

using System.Reflection; 

namespace TestOverrideEvent

{

    // By Javier Alvarez de Pedro, if you use this code... well, I'm glad to help you :)

    public class BaseOverrideEventHandler

    {

        private Control m_ctrl; 

        public Control Ctrl

        {

            get { return m_ctrl; }

            set { m_ctrl = value; }

        }

        private String m_eventName; 

        public String EventName

        {

            get { return m_eventName; }

            set { m_eventName = value; }

        }

        public BaseOverrideEventHandler(Control ctrl,string eventName)

        {

            m_ctrl = ctrl;

            m_eventName = eventName;

        }

        public static BaseOverrideEventHandler operator +(BaseOverrideEventHandler pbEventhandler, EventHandler handler)

        {

            // Suscribe to event

            string eventName = pbEventhandler.m_eventName;

            Control ctrl = pbEventhandler.m_ctrl;

            EventInfo ei = ctrl.GetType().GetEvent(eventName);

            ei.AddEventHandler(pbEventhandler.m_ctrl, handler);                      

            // Call for aditional operations on inherited classes

            pbEventhandler.SuscribeTasks(pbEventhandler, handler);

            // Return modified event handler

            return pbEventhandler;

        }

        public static BaseOverrideEventHandler operator -(BaseOverrideEventHandler pbEventhandler, EventHandler handler)

        {

            // Suscribe to event

            string eventName = pbEventhandler.m_eventName;

            Control ctrl = pbEventhandler.m_ctrl;

            EventInfo ei = ctrl.GetType().GetEvent(eventName);

            ei.RemoveEventHandler(pbEventhandler.m_ctrl, handler);

 

            // Return modified event handler

            return pbEventhandler;

        }

        public virtual void SuscribeTasks(BaseOverrideEventHandler overrideEventHandler, EventHandler eventHandler)

        {

        }

    }

}

 

Then I write a inherited class for customizing the event handling, so when you suscribe to the click event, you are also suscribing to the click of the rest of the buttons

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

 

namespace TestOverrideEvent

{

    // By Javier Alvarez de Pedro, if you use this code... well, I'm glad to help you :)

    class MyOverrideEventHandler : BaseOverrideEventHandler

    {

        public MyOverrideEventHandler(Control ctrl)

            : base(ctrl, "Click")

        {

        }

        public override void SuscribeTasks(BaseOverrideEventHandler overrideEventHandler, EventHandler eventHandler)

        {

            base.SuscribeTasks(overrideEventHandler, eventHandler);

            Control ctrl = overrideEventHandler.Ctrl;

            foreach (Control c in ctrl.Parent.Controls)

            {

                if (c is Button && c != ctrl)

                {

                    c.Click += eventHandler;

                }

            } 

        }

    }

}

 

 

And then I create a button that ‘overrides ‘ the click event

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Forms;

using TestOverrideEvent;

 

namespace TestOverrideEvent

{

    class InheritedButton : Button

    {

        // That's not allowed

        //public override Click ...

 

        private BaseOverrideEventHandler m_Click;

 

        public InheritedButton()

        {

            m_Click = new MyOverrideEventHandler(this);

        }

        public new BaseOverrideEventHandler Click

        {

            get

            {

                return m_Click;

            }

            set

            {

                m_Click = value;

            }

        }

    }

 

If you just define a form with a Inherited button and a few other buttons, suscribe ONLY to the click of the button1 and … magic! When you click the other buttons, the event is also fired! (Note that I haven’t modified the sintaxis of the suscription, you keep using a System.EventHandler for suscribing)

 

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

 

namespace TestOverrideEvent

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

            this.button1.Click += new System.EventHandler(this.button1_Click);

        }

        private void button1_Click(object sender, EventArgs e)

        {

            MessageBox.Show("Click 1");

        }

    }

}

 

Wednesday, 29 August 2007

Dataset serialization shuffles tables

Yes, another serialization problem.

We have defined a BIG dataset, then we create a instance of it. In a certain portion of the code, we serialize it with a binary formatter, but ... surprise when we deserialize, the tables order doesn't match!

Well, so far I haven't had much time to investigate, but setting the remoting format to binary does the trick:

ProcessDataSet.RemotingFormat = SerializationFormat.Binary

Tuesday, 28 August 2007

Serialization of a class that inherit from HashTable (or any class that implements ISerializable)

This is another funny problem related to serialization. Have you serialized a class and when deserialized again all the field values are set to default values? Well, check if this is your case:

If you have a class (Class1) that inherits from another one that is ISerializable (i.e. HashTable),

then you MUST write a constructor to handle SerializationInfo:

[Serializable]
class Class1: Hashtable
{
    public Class1(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {

    }

    public Class1()
    {
    }
    protected int number;
    private int othernumber;
    public string text;

    public int OtherNumber
    {
        get
        {
            return othernumber;
        }
        set
        {
            othernumber = value;
        }
    }

    public int Number
    {
        get
        {
            return number;
        }
        set
        {
            number = value;
        }
    }
}

What’s the problem related to this?. Well, write a short win application with a button to test:

private void button1_Click(object sender, EventArgs e)
{

    Class1 obj = new Class1();
    obj.Number = 99;
    obj.OtherNumber = 100;
    obj.text = "hello";

    System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
    byte[] serializedObject;
    System.IO.MemoryStream memoryStream = new System.IO.MemoryStream();

    //formatter.FilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.
    formatter.Serialize(memoryStream, obj);
    serializedObject = memoryStream.GetBuffer();

    System.IO.MemoryStream memoryStream2 = new System.IO.MemoryStream(serializedObject);
    Class1 obj2 = (Class1)formatter.Deserialize(memoryStream2);
}

And now, check the obj2: None of the values where properly retrieved! Why?

Well, as the Class1 class inherits from hashtable and hastable implements ISerializable, you need to write the special constructor above, but once you write the constructor, you are telling .net that you will be in charge of saving the data you really want to save… If you save no information for your fields, no data is saved when serialized, so no data is retrieved when deserialized.

The quick (and dirty ;) ) way to solve this: Use reflection to automatically traverse the properties and save them instead of write code to save them one by one:

[Serializable]
class Class1: Hashtable
{
    public Class1(SerializationInfo info, StreamingContext context)
        : base(info, context)
    {
        FieldInfo[] fields = this.GetFields(this);
        foreach (FieldInfo fieldInfo in fields)
        {
            Object obj = info.GetValue(fieldInfo.Name,fieldInfo.FieldType);
            fieldInfo.SetValue(this,obj);
        }
    }
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        FieldInfo[] fields = this.GetFields(this);
        foreach (FieldInfo fieldInfo in fields)
        {
            info.AddValue(fieldInfo.Name,fieldInfo.GetValue(this));
        }
        base.GetObjectData(info, context);
    }
    public Class1()
    {
    }
    protected int number;
    private int othernumber;
    public string text;

    public int OtherNumber
    {
        get
        {
            return othernumber;
        }
        set
        {
            othernumber = value;
        }
    }

    public int Number
    {
        get
        {
            return number;
        }
        set
        {
            number = value;
        }
    }

    private FieldInfo[] GetFields(object obj)
    {
        FieldInfo[] fields =
        obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

        return fields;
    }
}

Note that I’ve include the BindingFlags I’ve added to obtain the fields. This can be modified if other kind of filters are needed.

Friday, 27 April 2007

Identifying if the next/s control in z order hides a given control

This is a quite simple c# class that lets identify if for a certain control, there is another above that hides the control (partially or completely, depending on the option).

I've uploaded a sample project here: Z order sample project

Here is the ZOrder class code:

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;

namespace ZOrderFunctions
{
///
/// Defines helper function for testing zorder relationships
///

// By Javier Alvarez, 27 april 2007
class ZOrder
{
#region GetWindow
private const int GW_HWNDNEXT = 2;
private const int GW_HWNDPREV = 3;
private const int GW_CHILD = 5;


[DllImport("User32.dll")]
private static extern IntPtr GetWindow(IntPtr hwndSibling,
int wFlag);
//Parameters
//hWnd
//[in] Handle to a window. The window handle retrieved is relative to this window, based on the value of the uCmd parameter.
//uCmd
//[in] Specifies the relationship between the specified window and the window whose handle is to be retrieved. This parameter can be one of the following values.
//GW_CHILD
//The retrieved handle identifies the child window at the top of the Z order, if the specified window is a parent window; otherwise, the retrieved handle is NULL. The function examines only child windows of the specified window. It does not examine descendant windows.
//GW_ENABLEDPOPUP
//Windows 2000/XP: The retrieved handle identifies the enabled popup window owned by the specified window (the search uses the first such window found using GW_HWNDNEXT); otherwise, if there are no enabled popup windows, the retrieved handle is that of the specified window.
//GW_HWNDFIRST
//The retrieved handle identifies the window of the same type that is highest in the Z order. If the specified window is a topmost window, the handle identifies the topmost window that is highest in the Z order. If the specified window is a top-level window, the handle identifies the top-level window that is highest in the Z order. If the specified window is a child window, the handle identifies the sibling window that is highest in the Z order.
//GW_HWNDLAST
//The retrieved handle identifies the window of the same type that is lowest in the Z order. If the specified window is a topmost window, the handle identifies the topmost window that is lowest in the Z order. If the specified window is a top-level window, the handle identifies the top-level window that is lowest in the Z order. If the specified window is a child window, the handle identifies the sibling window that is lowest in the Z order.
//GW_HWNDNEXT
//The retrieved handle identifies the window below the specified window in the Z order. If the specified window is a topmost window, the handle identifies the topmost window below the specified window. If the specified window is a top-level window, the handle identifies the top-level window below the specified window. If the specified window is a child window, the handle identifies the sibling window below the specified window.
//GW_HWNDPREV
//The retrieved handle identifies the window above the specified window in the Z order. If the specified window is a topmost window, the handle identifies the topmost window above the specified window. If the specified window is a top-level window, the handle identifies the top-level window above the specified window. If the specified window is a child window, the handle identifies the sibling window above the specified window.
//GW_OWNER
//The retrieved handle identifies the specified window's owner window, if any. For more information, see Owned Windows.
#endregion

///
/// Tells if the control is hidden by other in the z order
///

/// Control to be checked
/// if true, a control is returned even if it is only partially hidden, if false, the control must be completely hidden
/// If there is a control above, reference to the control
public static Control IsHidden(Control sender, bool partially)
{
// Check preconditions
if (sender == null) throw new ArgumentException("IsHidden recieved null as parameter");

// 'Found a control' flag
bool ret = false;

// Return value
Control topControl = null;

// Point to the display area rectangle
Rectangle senderRect = sender.DisplayRectangle; //new Rectangle(sRect.X, sRect.Y, sRect.Width, sRect.Height); senderRect.Location.Offset(
senderRect.Offset(sender.Left, sender.Top);

// Iterate through higher z-order controls to see if any above the sender control
IntPtr currentHandler = sender.Handle ;
Control currentControl = null;
do
{
// Get next control
currentHandler = GetWindow(currentHandler, GW_HWNDPREV);

if (currentHandler != IntPtr.Zero)
{
// Get associated rectangle
currentControl = Control.FromHandle(currentHandler);
Rectangle currentRectangle = currentControl.DisplayRectangle;
currentRectangle.Offset(currentControl.Left, currentControl.Top);

// Select if fully contained or partially
if (partially)
{
ret = currentControl.Visible && currentRectangle.IntersectsWith(senderRect);
}
else
{
ret = currentControl.Visible && currentRectangle.Contains(senderRect);
}
// Point to control if it matches the condition
if (ret)
{
topControl = currentControl;
}

}

} while (!ret && currentHandler != IntPtr.Zero);

return topControl;




}
}
}




Build a sample form with three buttons and add the following (or alike) code to test:

public partial class Form1 : Form
{



public Form1()
{
InitializeComponent();
}

private void Test(object sender)
{
Control top = ZOrder.IsHidden(sender as Control, true);
if (top != null)
{
MessageBox.Show("Handle of control above is : " + top.Handle.ToString());
}
else
{
MessageBox.Show("This control is the topmost");
}
}

private void button2_Click(object sender, EventArgs e)
{
Test(sender);
}

private void button1_Click(object sender, EventArgs e)
{
Test(sender);
}

private void button3_Click(object sender, EventArgs e)
{
Test(sender);
}

private void Form1_Load(object sender, EventArgs e)
{
button1.Text = button1.Handle.ToString();
button2.Text = button2.Handle.ToString();
button3.Text = button3.Handle.ToString();
}
}