登录
注册
搜索
帮助
会员
界面
简洁版本
在线
SNS论坛
技术交流
MONO&.NET
SharpDevelop/MonoDevelop
写Form设计器尝试
帖子标题
新闻动态
SNS动态
SNS项目
SAF.NET
WOSIX
ezWinCE
技术交流
MONO&.NET
PostgreSQL数据库
操作系统
Linux/RHEL/CentOS
BSD/FreeBSD
Windows
软件开发
C&C++
Win32高级编程
Shell脚本编程
UNIX高级编程
GUI编程开发
综合交流区
布衣坊
软件下载
书籍与书评
Open Source
历史文化与故事
1
/ 1 页
1
跳转
页
查看:
988
写Form设计器尝试
enjoyo
admin
组别:
管理员
性别:
来自:
积分:
388
帖子:
374
注册:
2006-10-19
2008-05-26 10:34
|
只看楼主
树型
|
收藏
|
小
中
大
1
写Form设计器尝试
原文:
http://www.cnblogs.com/panjiwen/category/36995.html
Eddie Sheffield
作为微软之外的第一个发现启用
Form Designer
的人,确实是了不起
。
几年过后的今天,已经可以
找到一些关于Form Designer的资料,虽然不多也不全面,但毕竟还是有一些。我手上的资料是:
1
、《
Dissecting a C# Application Inside SharpDevelop
》中文版,第
16
章专门讲
Form
设计器;
2
、
windowsforms.net
上下载的一个小例子,显然也是从
SharpDevelop
得到的思路;
3
、
WinRes
这个工具,随
VS2005/.net FrameWork2.0
带的一个资源本地化的工具。可以反编译看看它的源码。
对比
1
、
2
与
3,
我发现两者虽然都实现了
Form Designer
,但设计思路迥然不同
,
显然
WinRes
用的办法要简明。原因是
.net 2.0
增强了设计器的功能并简化了设计器的调用方法。由于时间限制,短期内写一个功能完善的
Form
设计器可能难。我想做的是做一些测试,得出基于.net 2.0写一个
Form
设计器的基本方法。
我想先写一个没有任何功能但能看到设计器样式的小例子来测试。以后再慢慢增加设计的功能,比如控件的添加删除,属性的设置,保存文件等功能。
我们的步骤如下:
在
VS2005
中
(
我用的版本是
RC)
新建一个
C# WindowsForm
方案
,
在工程的引用中增加
System. Design,
在
Form1
的代码中先添加
using
System.ComponentModel.Design;
然后双击
Form, 在Form的Load事件中写以下代码:
DesignSurface surface
=
new
DesignSurface();
surface.BeginLoad(
typeof
(Form));
Control view
=
(Control)surface.View;
view.Dock
=
DockStyle.Fill;
this
.Controls.Add(view);
然后运行方案,这是最简单也没任何用处的窗体设计器了,但至少我们能调整那个被设计
Form
的大小
Sinoprise Network Studio
发送短消息
查看公共资料
查找该会员全部帖子
UID:
1
精华:
2
威望:
4
金钱:
84.45 元
状态:
离线
shg@sinoprise.com
20153462
enjoyo
admin
组别:
管理员
性别:
来自:
积分:
388
帖子:
374
注册:
2006-10-19
2008-05-26 10:34
|
只看楼主
树型
|
收藏
|
小
中
大
2
写Form设计器尝试(二) PropertyGrid
如果要修改这个设计时
Form
的其它属性该怎么办呢?接下来的更简单了。
切换到
Form1
的设计界面,往上面放一个
SplitContainer,
再在这个
SplitContainer
的右
panel
上放一个
PropertyGrid,
并将其
Dock
属性值设置成
Fill
。
切换到代码编辑界面,将上篇中输入的代码的最后一行改成:
this
.splitContainer1.Panel1.Controls.Add(view);
再加上一行:
this
.propertyGrid1.SelectedObject
=
surface.ComponentContainer.Components[
0
];
运行项目,现在就可以编辑
那个设计时
Form
的其它属性了。
除了一点点拖放操作之外,只写了六行代码,就实现这样的功能,有意思吧?
我的这个专题的目标是写一个能用的窗体设计器,有什么用呢?比如您的程序发布后,用户觉得某个控件的位置需要调整,某个控件的字体颜色需要修改,没关系,用户自行修改就是了。更复杂的一点的应用是用户希望在某个单据中增加一个字段,没关系,用户或者实施人员自已加就是了,不要改代码,不要重新编译。甚至用户想增加一些处理,也可以由实施人员现场在设计器中写代码,系统能将其编译好,并在运行时调用。
Sinoprise Network Studio
发送短消息
查看公共资料
查找该会员全部帖子
UID:
1
精华:
2
威望:
4
金钱:
84.45 元
状态:
离线
shg@sinoprise.com
20153462
enjoyo
admin
组别:
管理员
性别:
来自:
积分:
388
帖子:
374
注册:
2006-10-19
2008-05-26 10:35
|
只看楼主
树型
|
收藏
|
小
中
大
3
写Form设计器尝试(三) 在窗体上添加控件
在.net 2.0以前的版本中想实现在窗体设计器中添加控件的做法是定义一个实现IToolboxService接口的“服务”,然后添加到ServiceContainer中,具体方式可以参照
http://www.divil.co.uk/net/articles/designers/hosting.asp
。这个地址在
www.windowsforms.net
的Code hero中也可以找到。
.net 2.0简化了这个作业,它已经为我们提供了一个实现IToolboxService接口的抽象类ToolboxService,我们要做的就是写一个类继承自ToolboxService, 要简便很多。
具体操作是新建一个继承自ToolboxService的类,名为DemoToolboxService, 加上必要的using语句,在所继承的类名上按鼠标右键,点“Implement Abstract Class”,已经帮我们自动完成了DemoToolboxService的框架,由于我们需要在设计器窗体上显示一个工具箱,就象VS左侧的那个工具面板,不过我们现在做一个简单一点的,就用ListBox, 在DemoToolboxService中添加一个类型为ListBox的私有成员,并封装成属性。稍微改动一下,实现几个必要的方法,代码如下:
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.Drawing.Design;
using
System.Windows.Forms;
namespace
FormDesigner
{
class
DemoToolboxService
oolboxService
{
private
ListBox toolBox;
public
ListBox ToolBox
{
get
{
return
toolBox; }
set
{ toolBox
=
value; }
}
protected
override
CategoryNameCollection CategoryNames
{
get
{
return
null
;
}
}
//
实现带分类的工具列表,由于目前不分类,所以即为全部工具
protected
override
System.Collections.IList GetItemContainers(
string
categoryName)
{
ToolboxItem[] t
=
new
ToolboxItem[
this
.toolBox.Items.Count];
this
.toolBox.Items.CopyTo(t,
0
);
return
t;
}
//
实现工具列表
protected
override
System.Collections.IList GetItemContainers()
{
ToolboxItem[] t
=
new
ToolboxItem[
this
.toolBox.Items.Count];
this
.toolBox.Items.CopyTo(t,
0
);
return
t;
}
protected
override
void
Refresh()
{
}
protected
override
string
SelectedCategory
{
get
{
return
null
;
}
set
{
}
}
//
实现工具选择
protected
override
ToolboxItemContainer SelectedItemContainer
{
get
{
if
(toolBox.SelectedIndex
>
0
)
{
return
new
ToolboxItemContainer((ToolboxItem)toolBox.SelectedItem);
}
return
null
;
}
set
{
}
}
}
}
切换到主
Form的设计界面,在那个SplitContainer左侧再放上一个Panel, 做为将要完成的工具箱的容器,再切换到Form的代码编辑界面,添加一个类型为DemoToolService的私有成员toolBoxService, 修改Load事件代码,加上以下语句:
toolBoxService
=
new
DemoToolboxService();
toolBoxService.ToolBox
=
new
ListBox();
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(Button)));
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(TextBox)));
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(Label)));
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(TabControl)));
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(StatusBar)));
toolBoxService.ToolBox.Dock
=
DockStyle.Fill;
this
.panel1.Controls.Add(toolBoxService.ToolBox);
IServiceContainer container
=
surface.GetService(
typeof
(IServiceContainer))
as
IServiceContainer;
if
(container
!=
null
)
{
container.AddService(
typeof
(IToolboxService), toolBoxService);
}
运行方案,并试着在所设计的窗体上加上几个控件,界面如下:
我们画呀画,很有成就感了,但是左侧的工具箱也太丑了一点,右边那个PropertyGrid居然不因我们选择了不同的控件而改变,一点都不听话。
但无论怎样,到现在为止,我们写的代码也只有30行左右。
Sinoprise Network Studio
发送短消息
查看公共资料
查找该会员全部帖子
UID:
1
精华:
2
威望:
4
金钱:
84.45 元
状态:
离线
shg@sinoprise.com
20153462
enjoyo
admin
组别:
管理员
性别:
来自:
积分:
388
帖子:
374
注册:
2006-10-19
2008-05-26 10:36
|
只看楼主
树型
|
收藏
|
小
中
大
4
写Form设计器尝试(四) 修改窗体上的控件属性
在上一次的尝试中,我们已经可以进行控件的添加了。但后来我发现了一个
bug,
就是画好一个控件后,居然还可以接着画出这个控件,这不符合我们的习惯。一般情况下我们希望画好控件后,鼠标变回选择状态。这个功能在
.net 2.0
以前的做法是实现
IToolboxService
的
void
SelectedToolboxItemUsed()
方法,但是在
.net 2.0
中我们已经可以用更简单的办法,前面讨论过,在
.net 2.0
中我们是通过继承
ToolboxService
类而不是完全实现
IToolboxService
的方式来简化工具箱功能。分析
ToolboxService
的源代码,可以看到它已经实现了
SelectedToolboxItemUsed()
方法
,
其代码如下:
void
IToolboxService.SelectedToolboxItemUsed()
{
this
.SelectedItemContainerUsed();
}
protected
virtual
void
SelectedItemContainerUsed()
{
this
.SelectedItemContainer
=
null
;
}
很显然,它缺省的方式将
SelectedItemContainer
属性值赋值为null, 这时候我们可以想到一个简便的办法是并不需要重载SelectedItemContainerUsed(), 而是修改
SelectedItemContainer
属性的set方法,找到上次尝试的代码,在
SelectedItemContainer
属性的set方法上写上:
if
(value
==
null
)
{
toolBox.SelectedIndex
=
-
1
;
}
不过后来我觉得还是象
vs
的工具箱一样,提供一个
”Point”
的“虚”工具更方便。这个功能只要在我们前面的代码上对几个小地方稍作修改就可以了,就不帖代码了。
接下来的步骤是实现能在PropertyGrid中随意修改任何控件的属性。要做到这点也很简便,只要为DesignSurface的SelectionService实现一个SelectionChanged事件就行了。
切换到Form1的代码窗口,为窗体类添加一个私有成员:
private
ISelectionService selectionService;
然后在Load事件的最后加上:
selectionService
=
surface.GetService(
typeof
(ISelectionService))
as
ISelectionService;
selectionService.SelectionChanged
+=
new
EventHandler(selectionService_SelectionChanged);
当然也为窗体类加上以下事件方法:
void
selectionService_SelectionChanged(
object
sender, EventArgs e)
{
object
[] selection;
if
(selectionService.SelectionCount
==
0
)
propertyGrid1.SelectedObject
=
null
;
else
{
selection
=
new
object
[selectionService.SelectionCount];
selectionService.GetSelectedComponents().CopyTo(selection,
0
);
propertyGrid1.SelectedObjects
=
selection;
}
}
以下是运行时界面:
好象是大功告成了,不过控件只能添加,却怎么删除不了。 当然,还有一些必不可少的功能,如将设计内容序列化到资源文件或者生成代码文件等等。胡适先生说:“自古成功尝试始”,我想一定会在以后几节的尝试中成功实现这些功能的。
Sinoprise Network Studio
发送短消息
查看公共资料
查找该会员全部帖子
UID:
1
精华:
2
威望:
4
金钱:
84.45 元
状态:
离线
shg@sinoprise.com
20153462
enjoyo
admin
组别:
管理员
性别:
来自:
积分:
388
帖子:
374
注册:
2006-10-19
2008-05-26 10:37
|
只看楼主
树型
|
收藏
|
小
中
大
5
写Form设计器尝试(五) 让设计器使用自定义控件
在“写
Form
设计器尝试
(
三
)
在窗体上添加控件”的评论中,热心关注者
Leejee
提出了自定义控件的问题。我于是作了一个小测试,来实现设计器中使用自定义控件。
先要准备一个自定义控件。新建一个
Windows
控件方案,命名为
MyControl,
添加一个名为
ComboBoxField
的用户控件,在该用户控件上放一个
Label
和一个
ComboBox,
生成解决方案。将生成的
dll
文件复制到测试目录
D:\Dotnet
。
打开设计器方案,在工程中添加对
MyControl.Dll
的引用,在主窗体的代码中添加
Using MyControl;
然后在有形如
toolBoxService.ToolBox.Items.Add(
…
.);的最后加上一行代码:
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(MyControl.ComboBoxField)));
运行方案,确实可以象使用其它标准控件一样使用这个自定义控件。嘿嘿,没有一点意外,还是和以前的试验一样简单。
但是仔细想一想,出问题了,我们需要在工程中添加对控件所在文件的引用,需要在代码中写控件的类名。也就是说我们在写设计器时,就要知道我们要使用哪些自定义控件。而我们在
VS中添加自定义控件时,VS事先并不知道我们要加的是什么。要实现这个功能怎么办?理所当然地要用“反射”。
在工程中删除我们刚才添加的引用,并在主窗体代码中删除我们刚写的那两条语句。
在前面写第二条语句的地方写上:
Assembly a1
=
Assembly.LoadFrom(
@"
D:\Dotnet\MyControl.dll
"
);
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(a1.GetType(
"
MyControl.ComboBoxField
"
)));
运行方案,效果和前面的一样。好像这里也将动态库文件名以及类型名称硬编码到了代码中,但是可以很简单地做到让这两个字符串从配置文件中读出,这样就可以实现在设计器使用时随意使用自定义控件了
。
后记: 这篇是昨晚写的, 我后来在睡觉时觉得还有点不对, 就是我这个方法是在载入工具箱前就要使用反射,我猜测VS的做法是在需要使用此控件时才使用反射。这个问题的解决方法是修改ToolboxService。我会在以后的一个较为完善的版本中实现这个功能。2005/11/07
Sinoprise Network Studio
发送短消息
查看公共资料
查找该会员全部帖子
UID:
1
精华:
2
威望:
4
金钱:
84.45 元
状态:
离线
shg@sinoprise.com
20153462
enjoyo
admin
组别:
管理员
性别:
来自:
积分:
388
帖子:
374
注册:
2006-10-19
2008-05-26 10:37
|
只看楼主
树型
|
收藏
|
小
中
大
6
写Form设计器尝试(六) 实现菜单命令
在写这篇的正文之前,我要衷心感谢微软公司的Jeffrey Tan先生,他居然看懂了我的文理不通的英文提问,并且花费宝贵时间来钻研,帮我解决了一个星期来我百思不得其解的问题。我当时的问题是我写了一些语句来实现标准菜单命令,但是调试的时候怎么也得不到正确的结果,事实上我研究的两个例子用的方法和我的类似,它们却都可以正常执行。我一直没有找到原因,不得不求助支持。
在“
写Form设计器尝试(四) 修改窗体上的控件属性
”我提出了怎么删除控件的问题,我拙作的关注者山伟也提出过用什么方法实现控件对齐更简便。所有这些问题的答案是使用MenuCommandService, 在.net 1.0/1.1的办法是手工写一个实现IMenuCommandService接口的类,将其实例添加到服务容器中,而.net 2.0已经为我们提供了MenuCommandService。
打开主窗体代码界面,为Form1添加一个私有变量:
private
MenuCommandService menuCommandService;
修改Load事件代码,由于我们已经改了几次了,所以我这里全部帖出:
private
void
Form1_Load(
object
sender, EventArgs e)
{
DesignSurface surface
=
new
DesignSurface();
toolBoxService
=
new
DemoToolboxService();
toolBoxService.ToolBox
=
new
ListBox();
toolBoxService.ToolBox.Items.Add(
"
Point
"
);
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(Button)));
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(TextBox)));
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(Label)));
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(TabControl)));
toolBoxService.ToolBox.Items.Add(
new
ToolboxItem(
typeof
(StatusBar)));
//
Assembly a1=Assembly.LoadFrom(@"D:\Dotnet\MyControl.dll");
//
toolBoxService.ToolBox.Items.Add(new ToolboxItem(a1.GetType("MyControl.ComboBoxField")));
toolBoxService.ToolBox.Dock
=
DockStyle.Fill;
this
.panel1.Controls.Add(toolBoxService.ToolBox);
IServiceContainer container
=
surface.GetService(
typeof
(IServiceContainer))
as
IServiceContainer;
menuCommandService
=
new
MenuCommandService(surface);
if
(container
!=
null
)
{
container.AddService(
typeof
(IToolboxService), toolBoxService);
container.AddService(
typeof
(IMenuCommandService), menuCommandService);
}
surface.BeginLoad(
typeof
(Form));
Control view
=
(Control)surface.View;
view.Dock
=
DockStyle.Fill;
this
.splitContainer1.Panel1.Controls.Add(view);
this
.propertyGrid1.SelectedObject
=
surface.ComponentContainer.Components[
0
];
selectionService
=
surface.GetService(
typeof
(ISelectionService))
as
ISelectionService;
selectionService.SelectionChanged
+=
new
EventHandler(selectionService_SelectionChanged);
}
我们以删除功能来做测试,在主窗体的设计界面上添加一个MenuStrip,
在MenuStrip上添加Edit菜单项,在Edit菜单项下添加子菜单Delete, 设置其快捷键为Del, 为Delete菜单项写事件代码:
private
void
deleteToolStripMenuItem_Click(
object
sender, EventArgs e)
{
menuCommandService.GlobalInvoke(StandardCommands.Delete);
}
运行方案,在设计器上添加几个控件,然后在选中一个或几个控件,按下Del键或者点击菜单Edit->Delete, 所选的控件就会被删除。
其它的菜单命令如全选,如对齐等等,皆可如此实现。StandardCommands包含的命令实在太多了。
我们的设计器除了序列化资源、生成代码、事件处理这三项功能没有实现外,其它的都已经大功告成。
最近我会比较忙,剩下的内容要过段时间才会写出来,谢谢一直支持鼓励我的各位朋友们!
Sinoprise Network Studio
发送短消息
查看公共资料
查找该会员全部帖子
UID:
1
精华:
2
威望:
4
金钱:
84.45 元
状态:
离线
shg@sinoprise.com
20153462
enjoyo
admin
组别:
管理员
性别:
来自:
积分:
388
帖子:
374
注册:
2006-10-19
2008-05-26 10:38
|
只看楼主
树型
|
收藏
|
小
中
大
7
窗体设计器试验程序下载地址
目前的代码比较粗糙,仅包含了系列随笔1-6的内容。
我计划在未来的时间里将之设计成一个基本可用的提供代码生成、事件支持的设计器, 以此做为.net 2.0下Form Designer的一个参考例子。
http://www.cnblogs.com/Files/panjiwen/FormDesigner.zip
Sinoprise Network Studio
发送短消息
查看公共资料
查找该会员全部帖子
UID:
1
精华:
2
威望:
4
金钱:
84.45 元
状态:
离线
shg@sinoprise.com
20153462
<<
上一主题
|
下一主题
>>
1
/ 1 页
1
跳转
页
论坛跳转...
新闻动态
SNS动态
SNS项目
SAF.NET
WOSIX
ezWinCE
技术交流
MONO&.NET
安装使用
C#编程语言
MONO GUI
ASP.NET
Database Access(Ado.net)
WebService&Remoting
DotNET CF
CLR&Runtime
SharpDevelop/MonoDevelop
PostgreSQL数据库
PostgreSQL 中文化
PostgreSQL精华区
操作系统
Linux/RHEL/CentOS
BSD/FreeBSD
Windows
软件开发
C&C++
Win32高级编程
Shell脚本编程
UNIX高级编程
GUI编程开发
综合交流区
布衣坊
软件下载
书籍与书评
Open Source
历史文化与故事
我的主题
我的帖子
我的精华
帖子标题
空间日志
相册标题
作 者