Monday, February 6, 2012

Merged Column Headers and Vertical Column Text in DataGridView


Finally, I’ve been able to find a solution to have both vertical column headers text and merged column headers in the DataGridView in C#. The idea is to handle the Paint event and draw the column headers. In case of vertical text, just changing the string format would suffice, and the following figure shows the result obtained incorporating both Vertical text and merged headers

Vertical Header Text
The following code snippet is used to obain the vertical column header text. The code is written in the ‘Cell Painting’ Event of the DataGridView. The code check whether the column header is painted, if yes then the text format is changed to cater for vertical display.
     StringFormat l_objformat = new StringFormat();

            ///////////////////////////////////////////////////////////////
            if (e.RowIndex == -1 && e.ColumnIndex > -1)
            {
                Rectangle r2 = e.CellBounds;
                r2.Y += e.CellBounds.Height / 2;
                r2.Height = e.CellBounds.Height / 2;
                e.PaintBackground(r2, true);
                e.PaintContent(r2);

               //////////////////////////////////////////////////////////////////
                e.PaintBackground(e.ClipBounds, true);
  Rectangle rect = this.dataGridView1.GetColumnDisplayRectangle (e.ColumnIndex, true);
                Size titleSize = TextRenderer.MeasureText(e.Value.ToString(), e.CellStyle.Font);
               
  if (this.dataGridView1.ColumnHeadersHeight < titleSize.Width)
                    this.dataGridView1.ColumnHeadersHeight = titleSize.Width;
              
                rect.X += e.CellBounds.Width/2;
                rect.Y +=dataGridView1.ColumnHeadersHeight/2;
                l_objformat.FormatFlags = StringFormatFlags.DirectionVertical;
                e.Graphics.DrawString(e.Value.ToString(), e.CellStyle.Font, Brushes.Red, rect, l_objformat);
                e.Handled = true;//This is required, else the original painting of the data grid view overwrites the changes.
            }
The text is adjusted to make it center and occur after the merged headers.
Merged Columns
The merged columns are obtained by drawing the rectangle and writing strings in the rectangle. The following code snippet written in the Paint Event to achieve merged columns.
First increase the column height to cater for the merged header, this is written in the form load event.
     dataGridView1.ColumnHeadersHeight = dataGridView1.ColumnHeadersHeight * 2;
           
Now, the following code in the DataGridView paint event is used to merge columns. The code is for generating the grid view displayed in the above figure.
string[] months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
            for (int j = 0; j < 24; )
            {

                ////////////////////////////////////////////
                Rectangle r1 = dataGridView1.GetCellDisplayRectangle(j, -1, true);
                int w2 = dataGridView1.GetCellDisplayRectangle(j + 1, -1, true).Width;
                r1.X += 1;
                r1.Y += 1;
                r1.Width = r1.Width + w2 - 2;
                r1.Height = r1.Height / 2 - 2;
                e.Graphics.FillRectangle(new SolidBrush(dataGridView1.ColumnHeadersDefaultCellStyle.BackColor), r1);
             
                StringFormat format = new StringFormat();

                format.Alignment = StringAlignment.Center;
                format.LineAlignment = StringAlignment.Center;
                e.Graphics.DrawString(months[j / 2], dataGridView1.ColumnHeadersDefaultCellStyle.Font,
                    new SolidBrush(dataGridView1.ColumnHeadersDefaultCellStyle.ForeColor), r1, format);
                j += 2;


            }

Cater for Scrolling and Column Resizing
When the columns are resized or the scrolled across, repainting is required to redraw the columns. The Scroll and Column Resize events are handled and the columns are invalidated to force repainting.
Rectangle rtHeader = dataGridView1.DisplayRectangle;
            rtHeader.Height = dataGridView1.ColumnHeadersHeight / 2;
            dataGridView1.Invalidate(rtHeader);

Disclamer:
The Work is otherwise provided "as is", "where is", "as available", without warranty or guarantee of any kind. This means no express, implied or statutory warranty, including without limitation, warranties or conditions of merchantability or fitness for a particular purpose.


Sunday, February 5, 2012

Generic Communication Framework using Reflection in C#

All Project Files can be seen here https://sites.google.com/site/pragenius2012/TCPUDPTester.zip?attredirects=0&d=1

Introduction
The following article describes the implementation of Generic Communication Framework using Interfaces and Reflection in C#. The example code provided uses TCP or UDP communication for sending and receiving messages. The TCP and UDP functionalities are defined as class libraries, which are selected by the user during run-time. The program checks for the appropriate classes for sending or receiving messages.

Interface for Generic Functionalities
Every Communication module must provide functionality for sending and receiving messages. In addition, one may require provision for configuring the media (e.g. Server information for TCP), obtain statistical information and event generation. All these are generic requirements are independent of the method or medium used. The generic requirements are mapped into an interface which must be implemented by the classes providing the communication services. The interface ‘ICommunication’ defined in the project contains methods and properties catering for following functionalities.
  • The configuration parameters are provided via an XML string.
  •  Should be able to start and stop communication.
  • Should maintain the statistics of sent and received bytes.
  •  Provide the current status of the object (whether connected or not).
  •  Event to specify receipt of data
The definition of interface is as follows

namespace Communication
{
  public enum enmCommunicationMode { UNKNOWN = 0, TCPSERVER = 1, TCPCLIENT = 2,             UDPUNICAST = 3, UDPMULTICAST = 4 };
  public enum enmCommunicationStatus {UNKNOWN = 0, NOTINITIALIZED = -1, DISCONNECTED                          = -2, CONNECTED = 1,    INITIALIZED=2 };
  public enum enmCommunicationConfigurationStatus {UNKNOWN=0,
CONFIGURATIONERROR = -1, EXCEPTION = -2, SUCCESS = 1 };
    public delegate void CommunicationDataReceived(Byte[] l_bytbuffer,UInt32
   l_u32numbytes);
   
   public interface ICommunication
    {
         event CommunicationDataReceived EventCommunicationDataReceived;
        UInt32 TotalBytesReceived { get; }
        UInt32 TotalBytesSent {get; }
        enmCommunicationConfigurationStatus ConfigurationStatus { get; }
        enmCommunicationStatus CommunicationStatus {get;}
        enmCommunicationConfigurationStatus Configure(string l_strxmlstring);
        enmCommunicationStatus StartCommunication();
        enmCommunicationStatus StopCommunication();
        Int32 SendData(Byte[] l_bytbuffer, UInt32 l_u32numbytes);
        void ReceiveData();
        enmCommunicationStatus Disconnect();
    }
}

The Classes for TCP Communication (‘CTCPCommunication’) and UDP Communication (‘CUDPCommunication’) implement the interface. These two classes are defined are defined as class libraries in two separate DLL’s. The required DLL is selected at run time by the user to use the appropriate functionality. This is described in the following section.
Using Reflection for finding Communication Classes
The class libraries, defined in a DLL, are selected by the user during runtime using the ‘Load’ Button. Reflection is to determine the classes implementing the ‘ICommunication’ interface and add to the listbox for selection, see the code below.
     m_objLoadedAssembly = Assembly.LoadFile(l_strfilename);
     lstLibraries.Items.Clear();
     foreach (Type l_objtype in m_objLoadedAssembly.GetTypes())
     {
if (l_objtype.IsClass && 
    l_objtype.GetInterface("ICommunication")==typeof(ICommunication))
                    lstLibraries.Items.Add(l_objtype.Namespace + "." + l_objtype.Name);
     }
An object of type selected by the user, in the list box, is created for use with the communication, see the code below,
m_objCommunication = (ICommunication) Activator.CreateInstance(m_objLoadedAssembly. 
GetType(lstLibraries.SelectedItem.ToString()));
, where m_objCommunication is an object of type ’ICommunication’.
Configuration, Sending and Receiving information
The configuration information is generated using the user input in the ‘Configuration’ group box. The information provided is converted into XML string as passed on to the ‘Configure’ method of the interface. Upon Successful configuration, the ‘StartCommunication’ method is used to start communication.
       
                m_objCommunication.Configure(l_strxmlstring);//Configuration

The Event ‘EventCommunicationDataReceived’ must be subscribed to receive the data.
          m_objCommunication.EventCommunicationDataReceived += new                              CommunicationDataReceived ( DataReceived);


Conclusion
The article describes the use of interfaces and reflection to have a generic communication framework. The example provides TCP and UDP communication functionalities. However, this can be extended to any type of communication by implementing the ICommunication  interface.




Disclamer:
The Work is otherwise provided "as is", "where is", "as available", without warranty or guarantee of any kind. This means no express, implied or statutory warranty, including without limitation, warranties or conditions of merchantability or fitness for a particular purpose.