Click here to Skip to main content
15,867,488 members
Articles / Desktop Programming / Windows Forms

Controls Library: Extended ListView with Column Mapping

Rate me:
Please Sign up or sign in to vote.
4.86/5 (29 votes)
27 Mar 2014CPOL5 min read 82.2K   7.6K   110   23
Docking windows container, extended listview, extended property editor.

ctrlset/listedit.jpg

Edit properties/ sub properties

Introduction

A really easy and simple to use set of GUI classes for WinForms. It is written in 100% C# and does all the painting! This would enable us to work on tasks and not on how to present them in a form. This implies the ability to display and\or edit any object or collection with minimum preset and at the same time have all kinds and flexible configurations for developers and for end-users.

Also alternative GUI version on Gtk . Implemented on Mono platform, using Gtk# and Gtk.net (pre alfa).

The library contains a few bits and pieces of controls...

Controls

PList

The main class in the library. Has several modes that may make it useful as a list with tons of columns or display one item in detail or to expand the list as a tree.

Mode.List

View list of any collection (alternative for ListView in detailed mode, DataGridView in virtual mode):

  • Inline properties (object.Property1.Property2...)
  • Universal, minimal configuration
  • Extended column manipulation (drag&drop, hide and size columns by mouse and config by context menu)
  • Large collection support
  • Sorting. Grouping
  • Serializable (PListInfo class easy to save and has all information about list)
  • Editing Printing
  • Save configuration with reference to item type

Column mapping realized by two classes that implement IPColumn: PListColumn and PListColumnMap. Columns list generated from list item type information.

C#
public PListInfo ListInfo
{
     get { return _listInfo; }
     set
     {
          if (_listInfo == value)
              return;
          if (_listInfo != null)
              _listInfo.BoundChanged -= _handleColumnsBoundChanged;
          _listInfo = value;

          if (_listInfo != null)
              _listInfo.BoundChanged += _handleColumnsBoundChanged;
    }
}  

Mode.Fields

  • Inline properties (object.Property1.Property2...)
  • Sorting. Grouping. Hiding
  • Editing Printing
  • Save configuration with reference to item type

This mode creates a list of Fields from properties from the passed object and shows it in two columns: "Header" and "Value". And it has a third column "Group" used for grouping.

C#
public FieldsInfo FieldInfo
{
    get { return this._fieldInfo; }
    set
    {
         if (_fieldInfo == value)
              return;
          _fieldInfo = value;
          ListInfo = _fieldInfo.Columns;
          ListSource =  _fieldInfo.Nodes.DefaultView;
       }
    }
} 

Mode.Tree

Mode of PList that sets it to tree mode. It uses a list of Node's and shows it in filtered view; the filter checks if parents of a node are active (visible and expanded).

C#
public NodeInfo NodeInfo
{
    get { return nodeInfo; }
    set
    {
        if (nodeInfo == value)
            return;
        nodeInfo = value;
        ListInfo = _nodeInfo.Columns;
        ListSource = _nodeInfo.Nodes.DefaultView;
    }
}
...   

Tool Form

<code>Controls: ToolWindow, ToolTipWindow

To implement custom editing in list, ComboBox like controls. Supports: forms containing several open states and shows with alignment to calling control. Extended by ToolTipForm class to implement ToolTip like controls.

Docked Windows

<code>Controls: DockBox, DockPageBox,DockPanel

Primitive docking windows system, supports:

  • Dock any object derived from System.Windows.Form.Control
  • Left, right, top, bottom, and center content align
  • Mapped item layout

Simple realization based on <code>DockBoxPanel works like TabPage and uses MapLayout for aligning each box on a panel:

C#
public void OnSizeAllocated(Rectangle allocation)
{
    List<ILayoutMapItem> list = LayoutMapTool.GetVisibleItems(map);
    map.GetBound(allocation.Width, allocation.Height);
    foreach (DockMapItem item in list)
    {
        item.Panel.Bounds = map.GetBound(item);
    }
} 

Group Box

<code>Controls: GroupBoxMap

Grouping tool for align items on user control, supports:

  • Dock any object derived from System.Windows.Form.Control
  • Left, right, top, bottom, and center content align
  • Mapped item layout

Work like DockBox

C#
var boxParam = new GroupBoxItem(){Control = panel, FillWidth = true, Text = "Parameters"};
var boxResult = new GroupBoxItem(){Control = result,Row = 1, FillHeight = true,Text = "Result"};
var map = new GroupBoxMap();
map.Add(boxParam); map.Add(boxResult);

Tools

Several tools that make PList universal, customizable, portable and useful

Control Service

The primary task of this class is to paint all lists (columns, glyph, text, image) and related tasks.

C#
public static void PaintGliph (Graphics graphics, Rectangle r, float angle)
{
    if (gliphTexture == null) {

        GraphicsPath gliphpath = new GraphicsPath ();
        gliphpath.AddLine (2, 8, 2, 4);
        gliphpath.AddLine (0, 4, 4, 0);
        gliphpath.AddLine (8, 4, 6, 4);
        gliphpath.AddLine (6, 8, 2, 8);
        gliphpath.CloseFigure ();

        Bitmap bmp = new Bitmap (8, 8);
        Graphics g = Graphics.FromImage (bmp);

        Brush gb = new SolidBrush (Color.Black);
                
        g.DrawPath (Pens.Black, gliphpath);
        gliphTexture = new TextureBrush (bmp, WrapMode.Clamp);

        g.FillPath (gb, gliphpath);
        gliphFillTexture = new TextureBrush (bmp, WrapMode.Clamp);
                
        gb.Dispose ();
        gliphpath.Dispose ();
        g.Dispose ();
    }
            
    gliphFillTexture.ResetTransform ();
    Matrix m = new Matrix ();
    if (angle != 0) {
        m.RotateAt (angle, new PointF (4, 4));
    }
    Matrix m2 = new Matrix ();
    m2.Translate (r.X, r.Y);
    gliphFillTexture.MultiplyTransform (m, MatrixOrder.Append);
    gliphFillTexture.MultiplyTransform (m2, MatrixOrder.Append);
    graphics.FillRectangle (gliphFillTexture, r);
} 

Extended List

A special list that can improve performance and support several related tasks (filtering, indexing, change notification, custom sorting).

Type Service

You can slice work with System.Type, System.Reflection. And get custom attributes from System.ComponentModel.

C#
//several methods from TypeService class
//check is specified type implement IDictionary interface
public static bool IsDictionary (Type type)
{
    return (type.GetInterface ("IDictionary") != null 
      && type != typeof(byte[]) && type != typeof(Image));
}
...
//check is specified property have [BrowAable(false/true)] attribute
public static bool GetBrowsable (PropertyInfo property)
{
    object[] dscArray = 
       property.GetCustomAttributes (typeof(BrowsableAttribute), false);
    if (dscArray.Length == 0)
        return true;
    return ((BrowsableAttribute)dscArray [0]).Browsable;
}
...

Reflection Access

Created for performance reasons (large collection sorting, get value of properties direct from paint event). Access through Reflection.PropertyInfo.Get/SetValue is much slower than direct access, but direct access can't give us flexibility. To eliminate this restriction, create a special wrapper: ReflectionAccessor which creates a dynamic method and delegates to this method, which significantly reduces the delay to appeal to the properties of objects. And create a static cache of created accessors. Works with FieldInfo, MethodInfo, PropertyInfo, and ConstructorInfo (in Test application, you can compare delays).

C#
public static ReflectionAccessor InitAccessor(MemberInfo info, bool cache)
{
     if (info == null)
          return null;
     string name = GetName(info);
     ReflectionAccessor accessor;
     if (cache)
     {                
          if (cacheAccessors.TryGetValue(name, out accessor))
              return accessor;
     }
     accessor = new ReflectionAccessor(info, null);
     if (cache)
         cacheAccessors.Add(name, accessor);

     return accessor;
} 

Localization System

A simple localization (that can be saved in a file and can be easily edited) user API is realized with only one static function Localize.Get("category", "name"). The code below localizes string CString and class LocalizeItem for storing Category (an example can be the name of a Form) and original Name. Fast data retrieval is implemented by the LocalizeItemList class by generating indexes for category and name.

C#
//create index for value on LocalizeItemList class
public void AddIndex (string category, string name, LocalizeItem value)
{
    Dictionary<string, LocalizeItem > categoryIndex = null;
 
    if (index.ContainsKey (category))
        categoryIndex = index [category];
    else {
        categoryIndex = new Dictionary<string, LocalizeItem> ();
        index.Add (category, categoryIndex);
    }
    if (categoryIndex.ContainsKey (name))
        categoryIndex [name] = value;
    else {
        categoryIndex.Add (name, value);
    }    
}

Misc Tools

Formatting and parsing byte[], images, date, etc., is done in ais.tool.Serialization..

C#
//parse System.Drawing.Image from base64 from xml 
public static Image ImageFromBase64 (string text)
{
    try {
        if (text == "")
            return null;
        byte[] memBytes = Convert.FromBase64String (text);
        return ImageFromByte (memBytes);
    } catch {
        return null;
    }
}
//parse System.Drawing.Image from byte[]    
public static Image ImageFromByte (byte[] bytes)
{
    Image img = null;
    using (MemoryStream stream = new MemoryStream (bytes)) {
        img = Image.FromStream (stream);
    }
    return img;
}

Serialization

Targets fast and compact XML serialization of objects, using Tools and TypeService. For support class loader: use special type naming: Type.Name with Assembly.Name. Has special instructions for collection and dictionaries. Supports DOM and SAX mode.

C#
if (o is IList)
{
    IList list = (IList)o;
    list.Clear();
    string typeName = xr.GetAttribute("DefaultItemType");
    Type defaultType = typeName == null ? TypeService.GetItemType(list) : GetVT(typeName);
    while (xr.Read() && xr.NodeType != XmlNodeType.EndElement)
    {
         if (xr.NodeType == XmlNodeType.Element)
         {
              typeName = xr.GetAttribute("VT");
              Type tt = typeName == null ? defaultType : GetVT(typeName);
              object newobj = ReflectionAccessor.CreateObject(tt, true);
              if (IsXmlAttribute(tt))
                  newobj = TextParce(xr.GetAttribute(xr.Name), tt);
              else
                  newobj = ParceXmlReader(xr, newobj);

               list.Add(newobj);//rAdd.Get(newobj, new object[] { newobj });

         }
... 

Map Layout

C#
...
public static void Grouping(ILayoutMapItem newItem, ILayoutMapItem oldItem, LayoutAlignType anch)
{
     ILayoutMap map = (ILayoutMap)ReflectionAccessor.CreateObject(oldItem.Map.GetType(), true);
     Replace(oldItem, map);

     oldItem.Row = 0;
     oldItem.Col = 0;
     newItem.Row = 0;
     newItem.Col = 0;

     if (anch == LayoutAlignType.Top)
         oldItem.Row = 1;
     else if (anch == LayoutAlignType.Bottom)
         newItem.Row = 1;
     else if (anch == LayoutAlignType.Right)
         oldItem.Col = 1;
     else if (anch == LayoutAlignType.Left)
         newItem.Col = 1;

     Add(map, oldItem);
     Add(map, newItem);
} 

Background

This is planned as a universal controls library. The development was started on a database and documents flow projects.

It was a long way to implement my own control. Work started on a list view. The first step was a control based on DataGridView in Virtual mode, but it used too much memory. To avoid this, the second step was to derive from ListView, but lacked columns configuration and grouping in virtual mode. So when I had enough time and looked on CodeProject, I decided that it was possible. Hope this article helps everyone who wants to develop their own controls!

Many thanks to the entire CodeProject community. I take a lot of concepts, code from this site, and this article is the way in which I can bring it back.

Using the Code

Initialize Localization and List Information

C#
// load data on startup 
Localize.Load ();
//or use
//Serialization.Load(Localize.Data, 
//  Path.Combine(Environment.CurrentDirectory, "localize.xml"))
Application.Run (new FormTest ());
//and save on exit
Localize.Save ();

Using Localize

C#
public void Localizing ()
{
 this.button1.Text = Localize.Get ("Test", "List Editor");
 this.button2.Text = Localize.Get ("Test", "Test Reflection");
 this.button2.ToolTipText = Localize.Get ("Test", 
    "Test Reflection Accesor\nCompare properties access speed");
 this.button4.Text = Localize.Get ("Test", "Option Editor");
}

Using Docking Window

C#
//initialize three controls
Form f = new Form();//main form
DockControl c = new DockControl();//container control
ReachTextBox r = new ReachTextBox();//simple editing control

//simple add it to your form
f.Controls.Add(c);

//and now add edit control to container, for example to left side
c.Add(r,DockType.Left);

Using PList

C#
//to create list like System.Windows.Form.ListBox: 
PList list = new PList();
list.DataSource = _listDataSource
//now ready to use with all PList futures        
//extended properties
list.AutoGenerateColumn = false;
list.AutoGenerateToString = true;
list.AutoGenerateToStringSort = true;
 //columns info
list.Info.HeaderVisible = false;
list.Info.ColumnsVisible = false;
list.Info.TreeMode = true;
//style setting            
list.StyleCell.Default.BorderColor = Color.Transparent;
list.StyleCell.Alter.BackColor = list.StyleCell.Default.BackColor;
list.StyleCell.Alter.BorderColor = list.StyleCell.Default.BorderColor;
 /custom properties 
list.SelectedItem = value;
...

History

  • 2011.09.16
    • First post
  • 2011.09.23
    • Default styles change
    • Column editing (move, sizing highlighting and auto grouping)
    • Column context menu (allow adding sub columns and show hided columns)
    • Indexed properties
    • Selection by mouse
    • Inline sub properties for PList (like object.Prop1.Prop2...)
    • Support System.Data.DataView (ais.ctrl.PDataList)
    • Test for PDataList
  • 2011.10.05
    • Debug mostly
    • Styles (completely changed, allow save and edit by user, on each column)
    • ToolForm behavior on Windows (TODO: debug in mono)
    • CellEditorFont and CellEditorColor
  • 2012.08.30
    • API changed
    • Merge PList and FList, add new properties ListSource and FieldSource
    • Add PTree it is like TreeView
    • Default styles, and styles system completely changed, compatibility serialization
    • Insert images cache to localization Localize.GetImage
    • Improve serialization performance
      • XmlWriter/Reader then XmlDocument, improve memory utilization
      • Dynamic method for fields access, improve speed
      • Optimization for List item type, decrease file size
  • 2012.09.17
    • Scaling as internal option (Ctrl+Mouse Scroll)
    • Transparent Background
    • List Editor filtering and improve Tree mode with ListExtendView<T>
    • AutoSize overwrite GetPreferedSize
  • 2013.01.02
    • Debug
    • Gtk# version pre alfa
    • Text Diff in tools lib
    • GroupBoxItem extending GroupBox
  • 2014.03.37
    • Rendering optimization
    • Extend Sorting, Groupping and Filtering
    • Gtk# version beta
    • New test: "Files"

License

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


Written By
Kazakstan Kazakstan
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionpurpose? Pin
Mizan Rahman9-Nov-13 6:48
Mizan Rahman9-Nov-13 6:48 
AnswerRe: purpose? Pin
alexandrvslv4-Mar-14 23:21
alexandrvslv4-Mar-14 23:21 
GeneralMy vote of 1 Pin
bj_pony7-Nov-13 21:04
bj_pony7-Nov-13 21:04 
QuestionCopyrighted images? Pin
jeffb4230-Aug-12 8:18
jeffb4230-Aug-12 8:18 
AnswerRe: Copyrighted images? Pin
alexandrvslv30-Aug-12 17:53
alexandrvslv30-Aug-12 17:53 
GeneralRe: Copyrighted images? Pin
jeffb424-Sep-12 6:45
jeffb424-Sep-12 6:45 
QuestionThank! New Source? Pin
RAND 45586630-Aug-12 6:18
RAND 45586630-Aug-12 6:18 
AnswerRe: Thank! New Source? Pin
alexandrvslv30-Aug-12 18:22
alexandrvslv30-Aug-12 18:22 
AnswerRe: Thank! New Source? Pin
alexandrvslv6-Sep-12 20:55
alexandrvslv6-Sep-12 20:55 
GeneralThank you! This project is for Mono? Pin
RAND 45586617-Sep-12 4:23
RAND 45586617-Sep-12 4:23 
GeneralRe: Thank you! This project is for Mono? Pin
alexandrvslv17-Sep-12 19:45
alexandrvslv17-Sep-12 19:45 
GeneralRe: Thank! New Source? Pin
Brisingr Aerowing1-Jan-13 16:41
professionalBrisingr Aerowing1-Jan-13 16:41 
I am unable to download the code, as git gives me an error and there is no download button on the 'Code' page there. How should I download this?

Bob Dole
The internet is a great way to get on the net.

D'Oh! | :doh: 2.0.82.7292 SP6a

AnswerRe: Thank! New Source? Pin
alexandrvslv1-Jan-13 19:04
alexandrvslv1-Jan-13 19:04 
GeneralRe: Thank! New Source? Pin
Brisingr Aerowing1-Jan-13 19:23
professionalBrisingr Aerowing1-Jan-13 19:23 
QuestionNew Version? Pin
RAND 45586623-Aug-12 7:49
RAND 45586623-Aug-12 7:49 
AnswerRe: New Version? Pin
alexandrvslv23-Aug-12 8:07
alexandrvslv23-Aug-12 8:07 
GeneralMy vote of 1 Pin
Toli Cuturicu24-Sep-11 3:29
Toli Cuturicu24-Sep-11 3:29 
GeneralRe: My vote of 1 PinPopular
alexandrvslv25-Sep-11 18:03
alexandrvslv25-Sep-11 18:03 
GeneralRe: My vote of 1 Pin
Paul Selormey25-Sep-11 18:16
Paul Selormey25-Sep-11 18:16 
GeneralRe: My vote of 1 Pin
Toli Cuturicu25-Sep-11 22:14
Toli Cuturicu25-Sep-11 22:14 
GeneralMy vote of 5 Pin
Paul Selormey23-Sep-11 16:41
Paul Selormey23-Sep-11 16:41 
GeneralRe: My vote of 5 Pin
alexandrvslv25-Sep-11 18:06
alexandrvslv25-Sep-11 18:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.