65.9K
CodeProject 正在变化。 阅读更多。
Home

控件库: 带列映射的扩展 ListView

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.86/5 (28投票s)

2011年9月21日

CPOL

5分钟阅读

viewsIcon

88309

downloadIcon

7708

停靠窗口容器,扩展列表视图,扩展属性编辑器。

ctrlset/listedit.jpg

编辑属性/子属性

引言

一组非常容易使用的 WinForms GUI 类。它以 100% C# 编写,并负责所有绘制!这将使我们能够专注于任务,而不是如何在窗体中呈现它们。这意味着能够以最少的设置显示和/或编辑任何对象或集合,同时为开发人员和最终用户提供各种灵活的配置。

另有 Gtk 上的替代 GUI 版本。在 Mono 平台上实现,使用 Gtk# 和 Gtk.net(预 alpha 版)。

该库包含一些控件...

控件

PList

库中的 主类。 具有多种模式, 可以将其用作 带有大量 列的列表, 或者 详细显示 一项, 或者 将列表 展开为树。

模式.列表

显示任何集合的列表(详细模式下 ListView 的替代品,虚拟模式下 DataGridView 的替代品)

  • 内联属性(object.Property1.Property2...)
  • 通用,最少配置
  • 扩展列操作(拖放、通过鼠标隐藏和调整列大小,以及通过上下文菜单配置)
  • 大型集合支持
  • 排序。分组
  • 可序列化(PListInfo 类易于保存,并包含列表的所有信息)
  • 编辑打印
  • 保存配置,并引用项目类型

通过实现IPColumn的两个类实现列映射:PListColumnPListColumnMap。列列表从列表项类型信息生成。

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;
    }
}  

模式.字段

  • 内联属性(object.Property1.Property2...)
  • 排序。分组。隐藏
  • 编辑打印
  • 保存配置,并引用项目类型

此模式创建对象属性的字段列表,并在两列中显示:“Header”和“Value”。它还有第三列“Group”,用于分组。

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

模式.树

PList的模式,将其设置为树模式。它使用Node列表并以过滤视图显示;过滤器检查节点的父节点是否处于活动状态(可见和展开)。

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

工具窗体

控件ToolWindow, ToolTipWindow

实现列表中的自定义编辑,类似ComboBox的控件。支持:包含多个打开状态的窗体,并与调用控件对齐显示。通过ToolTipForm类扩展,以实现类似ToolTip的控件。

停靠窗口

控件DockBox, DockPageBox, DockPanel

原始停靠窗口系统,支持

  • 停靠任何派生自System.Windows.Form.Control的对象
  • 左、右、上、下和中心内容对齐
  • 映射项目布局

基于DockBoxPanel的简单实现,其作用类似于TabPage,并使用MapLayout来对齐面板上的每个框。

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);
    }
} 

分组框

控件GroupBoxMap

用于对齐用户控件上项目的分组工具,支持

  • 停靠任何派生自System.Windows.Form.Control的对象
  • 左、右、上、下和中心内容对齐
  • 映射项目布局

行为类似于 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); 

工具

多种工具使PList通用、可定制、可移植且有用

控件服务

此类主要任务是绘制所有列表(列、字形、文本、图像)以及相关任务。

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);
} 

扩展列表

一个特殊列表,可以提高性能并支持多项相关任务(过滤、索引、更改通知、自定义排序)。

类型服务

您可以处理System.Type, System.Reflection。并从System.ComponentModel获取自定义属性。

//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.PropertyInfo.Get/SetValue访问比直接访问慢得多,但直接访问无法提供灵活性。为了消除这种限制,创建了一个特殊的包装器ReflectionAccessor,它创建一个动态方法并将调用委托给该方法,从而大大减少了访问对象属性的延迟。并创建了创建的访问器的静态缓存。与FieldInfo, MethodInfo, PropertyInfoConstructorInfo一起工作(在测试应用程序中,您可以比较延迟)。

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;
} 

本地化系统

一个简单的本地化(可以保存在文件中且易于编辑)用户 API 通过一个static函数Localize.Get("category", "name")实现。下面的代码本地化stringCString和类LocalizeItem,用于存储Category(例如Form的名称)和原始NameLocalizeItemList类通过为categoryname生成索引来实现快速数据检索。

//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);
    }    
}

杂项工具

ais.tool.Serialization.中完成byte[]、图像、日期等的格式化和解析。

//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;
}

序列化

目标是快速、紧凑地对对象进行 XML 序列化,使用ToolsTypeService。为了支持类加载器:使用特殊的类型命名:Type.NameAssembly.Name。对集合和字典有特殊指令。支持 DOM 和 SAX 模式。

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 });

         }
... 

映射布局

...
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);
} 

背景

这是一个计划中的通用控件库。开发始于数据库和文档流项目。

实现自己的控件经历了一个漫长的过程。工作始于列表视图。第一步是基于虚拟模式下的DataGridView的控件,但它占用了太多内存。为了避免这种情况,第二步是派生自ListView,但它缺乏虚拟模式下的列配置和分组。因此,当我有足够的时间并查看 CodeProject 时,我意识到这是可能的。希望这篇文章能帮助所有想开发自己控件的人!

非常感谢整个 CodeProject 社区。我从这个网站获取了许多概念和代码,这篇文章是我回馈社区的方式。

Using the Code

初始化本地化和列表信息

// 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 ();

使用 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");
}

使用停靠窗口

//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);

使用 PList

//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;
...

历史

  • 2011.09.16
    • 第一篇帖子
  • 2011.09.23
    • 更改默认样式
    • 列编辑(移动、调整大小、高亮显示和自动分组)
    • 列上下文菜单(允许添加子列并显示隐藏的列)
    • 索引属性
    • 鼠标选择
    • PList的内联子属性(如object.Prop1.Prop2...)
    • 支持System.Data.DataViewais.ctrl.PDataList
    • PDataList的测试
  • 2011.10.05
    • 主要用于调试
    • 样式(完全更改,允许用户保存和编辑,针对每个列)
    • Windows 上的 ToolForm 行为(待办:在 mono 中进行调试)
    • CellEditorFontCellEditorColor
  • 2012.08.30
    • API 已更改
    • 合并PListFList,添加新属性ListSourceFieldSource
    • 添加PTree,它类似于TreeView
    • 默认样式和样式系统完全更改,兼容序列化
    • 在本地化Localize.GetImage中插入图像缓存
    • 提高序列化性能
      • XmlWriter/Reader然后XmlDocument,提高内存利用率
      • 字段访问动态方法,提高速度
      • 优化列表项类型,减小文件大小
  • 2012.09.17
    • 缩放作为内部选项(Ctrl+鼠标滚轮)
    • 透明背景
    • 列表编辑器过滤并改进 Tree 模式,使用ListExtendView<T>
    • AutoSize 重写GetPreferedSize
  • 2013.01.02
    • Debug
    • Gtk# 版本预 alpha
    • 工具库中的文本差异
    • GroupBoxItem扩展GroupBox
  • 2014.03.37
    • 渲染优化
    • 扩展排序、分组和过滤
    • Gtk# 版本 beta
    • 新测试:“文件”
© . All rights reserved.