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






4.86/5 (28投票s)
停靠窗口容器,扩展列表视图,扩展属性编辑器。
引言
一组非常容易使用的 WinForms GUI 类。它以 100% C# 编写,并负责所有绘制!这将使我们能够专注于任务,而不是如何在窗体中呈现它们。这意味着能够以最少的设置显示和/或编辑任何对象或集合,同时为开发人员和最终用户提供各种灵活的配置。
另有 Gtk 上的替代 GUI 版本。在 Mono 平台上实现,使用 Gtk# 和 Gtk.net(预 alpha 版)。
该库包含一些控件...
控件
PList
库中的 主类。 具有多种模式, 可以将其用作 带有大量 列的列表, 或者 详细显示 一项, 或者 将列表 展开为树。
模式.列表
显示任何集合的列表(详细模式下 ListView 的替代品,虚拟模式下 DataGridView 的替代品)
- 内联属性(
object.Property1.Property2
...) - 通用,最少配置
- 扩展列操作(拖放、通过鼠标隐藏和调整列大小,以及通过上下文菜单配置)
- 大型集合支持
- 排序。分组
- 可序列化(
PListInfo
类易于保存,并包含列表的所有信息) - 编辑打印
- 保存配置,并引用项目类型
通过实现IPColumn
的两个类实现列映射:PListColumn
和PListColumnMap
。列列表从列表项类型信息生成。
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
的对象 - 左、右、上、下和中心内容对齐
- 映射项目布局
基于
的简单实现,其作用类似于DockBox
Panel
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
, PropertyInfo
和ConstructorInfo
一起工作(在测试应用程序中,您可以比较延迟)。
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")
实现。下面的代码本地化string
CString
和类LocalizeItem
,用于存储Category
(例如Form
的名称)和原始Name
。LocalizeItemList
类通过为category
和name
生成索引来实现快速数据检索。
//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 序列化,使用Tools
和TypeService
。为了支持类加载器:使用特殊的类型命名:Type.Name
与Assembly.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.DataView
(ais.ctrl.PDataList
) PDataList
的测试- 2011.10.05
- 主要用于调试
- 样式(完全更改,允许用户保存和编辑,针对每个列)
- Windows 上的 ToolForm 行为(待办:在 mono 中进行调试)
CellEditorFont
和CellEditorColor
- 2012.08.30
- API 已更改
- 合并
PList
和FList
,添加新属性ListSource
和FieldSource
- 添加
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
- 新测试:“文件”