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.
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.
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).
public NodeInfo NodeInfo
{
get { return nodeInfo; }
set
{
if (nodeInfo == value)
return;
nodeInfo = value;
ListInfo = _nodeInfo.Columns;
ListSource = _nodeInfo.Nodes.DefaultView;
}
}
...
Tool Form
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
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
DockBox
Panel
works like TabPage
and uses MapLayout
for aligning each box on
a panel:
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
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
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.
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
.
public static bool IsDictionary (Type type)
{
return (type.GetInterface ("IDictionary") != null
&& type != typeof(byte[]) && type != typeof(Image));
}
...
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).
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
.
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.
.
public static Image ImageFromBase64 (string text)
{
try {
if (text == "")
return null;
byte[] memBytes = Convert.FromBase64String (text);
return ImageFromByte (memBytes);
} catch {
return null;
}
}
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.
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);
}
...
Map Layout
...
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
Localize.Load ();
Application.Run (new FormTest ());
Localize.Save ();
Using Localize
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
Form f = new Form();DockControl c = new DockControl();ReachTextBox r = new ReachTextBox();
f.Controls.Add(c);
c.Add(r,DockType.Left);
Using PList
PList list = new PList();
list.DataSource = _listDataSource
list.AutoGenerateColumn = false;
list.AutoGenerateToString = true;
list.AutoGenerateToStringSort = true;
list.Info.HeaderVisible = false;
list.Info.ColumnsVisible = false;
list.Info.TreeMode = true;
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
- 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
- Renderin
g
optimization - Extend Sorting, Groupping and Filtering
- Gtk# version beta
- New test: "Files"