2023年2月

Visio在VB6中的开发有很多现成的项目及代码,但在.NET领域相对比较滞后,但是从Visio SDK2007发布以来,给予Visio的C#开发逐渐多了起来,虽然和VB6的开发思路有一定的相似,不过平台不一样,做出来的东西差别还是有一定的距离,虽然总体慢一点,但是,DotNET框架的逐渐强大以及更多的DotNet程序和开发人员的加入,带给大家更高的整合价值以及更美好的发展未来。
本人将会在C#领域将Visio的二次开发进行下去,希望大家一起学习研究,碰撞出更美的火花

Visio Drawing Control控件使用
Visio开发必备
Visio SDK 2007
VisSDK.chm
Visio Code Samples Library.chm
Visio文档操作
查看ShapeSheet属性及帮助内容
宏的录制

Visio的几个重要对象


Application

Window (Application.ActiveWindow)

Document (Application.ActiveDocument)

Master、Shape

VisioObjects.jpg

Visio XML格式文件分析
Master格式
Visio_Master.jpg

Pages/Shapes格式
Visio_Page.jpg

图纸的XML文档中,Master后面的Shapes集合中只有一个Shape对象
图纸的Shapes集合有多个对象,每个对象的NameU和Name值可能不一样,一般使用NameU


Visio基本操作的实现


VisApplication
=

this
.ctrlDrawing.Document.Application;

VisWindow

=
VisApplication.ActiveWindow;

VisDocument

=
VisApplication.ActiveDocument;


//
Settings


VisApplication.Settings.ShowShapeSearchPane
=

false
;
//
显示搜索形状窗体


VisApplication.Settings.EnableAutoConnect
=

false
;
//
自动链接(2007默认)


VisApplication.Settings.StencilBackgroundColor
=

10070188
;
//
vbGrayText


//
文档各种显示


VisApplication.DoCmd((
short
)VisUICmds.visCmdViewRulers);

VisApplication.DoCmd((

short
)VisUICmds.visCmdViewGrid);

VisApplication.DoCmd((

short
)VisUICmds.ShowGuides);

VisApplication.DoCmd((

short
)VisUICmds.ShowConnectPoints);

VisApplication.DoCmd((

short
)VisUICmds.ShowPageBreaks);


//
各种窗口


VisApplication.DoCmd((
short
)VisUICmds.visCmdShapesWindow);

VisApplication.DoCmd((

short
)VisUICmds.visCmdPanZoom);

VisApplication.DoCmd((

short
)VisUICmds.visCmdCustProp);

VisApplication.DoCmd((

short
)VisUICmds.visCmdSizePos);



SendKeys.Send(

"
^(x)
"
);

//
VisApplication.DoCmd((short)VisUICmds.visCmdUFEditCut);


SendKeys.Send(
"
{DELETE}
"
);

//
VisApplication.DoCmd((short)VisUICmds.visCmdUFEditClear);


SendKeys.Send(
"
^+(p)
"
);

//
VisApplication.DoCmd(VisUICmds.visCmdFormatPainter);


SendKeys.Send(
"
^(z)
"
);

//
VisApplication.DoCmd(VisUICmds.visCmdEditUndo);


调用工具条对象、菜单对象的方法
Application.CommandBars
Microsoft.Office.Core.CommandBars共享Office对象模型
使用CommandBar代替UIObject
CommandBar对象中,菜单及工具条是同一个东西
CommandBar、CommandBarButton、 CommandBarComboBox、CommandBarControl、 和 CommandBarPopup

示例:执行视图中的工具条的所有按钮事件。


Microsoft.Office.Core.CommandBars commandBars;

commandBars

=
(Microsoft.Office.Core.CommandBars)VisApplication.CommandBars;


foreach
(Microsoft.Office.Core.CommandBarControl control
in
commandBars[
"
View
"
].Controls)



{

Microsoft.Office.Core.CommandBarButton button

=
control
as
Microsoft.Office.Core.CommandBarButton;

if
(button
!=

null
)



{

button.Execute();

}



}




StringBuilder sb

=

new
StringBuilder();

foreach
(Microsoft.Office.Core.CommandBar bar
in
commandBars)



{

sb.Append(

string
.Format(
"
CommandBar Name:{0}\r\n
"
, bar.Name));

foreach
(Microsoft.Office.Core.CommandBarControl control
in
bar.Controls)



{

Microsoft.Office.Core.CommandBarButton button

=
control
as
Microsoft.Office.Core.CommandBarButton;

if
(button
!=

null
)



{

sb.Append(

string
.Format(
"
Button Name:{0} \r\n
"
, button.Caption));

}



}



}



Form2 frm

=

new
Form2();

frm.txtContent.Text

=
sb.ToString();

frm.Show();



short
flags
=
(
short
)VisOpenSaveArgs.visOpenDocked
|
(
short
)VisOpenSaveArgs.visOpenRO;

StencilOpenEx(wndVisio.Application, flags);




///

<summary>


///
打开模具的公共方法

///

</summary>


///

<param name="visApp">
按引用调用的VisioApplication对象
</param>


///

<param name="flags">
打开的模式
</param>


private

void
StencilOpenEx(Application visApp,
short
flags)



{

List

<
string
>
stencilList
=
GetStencils();

string
stencilFileName;


foreach
(
string
stencil
in
stencilList)



{

stencilFileName

=
GetStencilsFileName(stencil);

if
(
!
string
.IsNullOrEmpty(stencilFileName))



{

visApp.Documents.OpenEx(Portal.gc.gStencileFileBasePath

+
stencilFileName, flags);

}



}



}




//
关闭模具文件


visApp.Documents[
"
Switch.vss
"
].Close();

visApp.Documents[

"
Span.vss
"
].Close();

visApp.Documents[

"
Line.vss
"
].Close();

visApp.Documents[

"
Label.vss
"
].Close();

visApp.Documents[

"
Construct.vss
"
].Close();

visApp.Documents[

"
Monitor.vss
"
].Close();



Visio Shape的属性操作
StringToFormulaForString、FormulaStringToString函数
访问属性
设置属性
添加属性


//
列出模具组


this
.cmbStencilGroup.Items.Clear();

List

<
string
>
stencilGroups
=
stencil.GetStencils();

foreach
(
string
group
in
stencilGroups)



{

this
.cmbStencilGroup.Items.Add(group);

}





//
根据模具组列出模具


string
stencilName
=
stencil.GetStencilsFileName(
this
.cmbStencilGroup.Text);

this
.cmbStencil.Items.Clear();

string
tempName;

foreach
(Master master
in
visApp.Documents[stencilName].Masters)



{

tempName

=
master.Name;

if
(
!
stencil.IsExcludeItem(tempName))



{

this
.cmbStencil.Items.Add(tempName);

}



}





//
根据模具,获取对应的属性集合,遍历属性集合,列出属性名称


string
stencilName
=
stencil.GetStencilsFileName(
this
.cmbStencilGroup.Text);

string
masterName
=

this
.cmbStencil.Text;

Visio.Shape shape

=
visApp.Documents[stencilName].Masters[masterName].Shapes[
1
];

if
(shape
!=

null
)



{

List

<
StencilPropertyInfo
>
propInfos
=
property.GetPropertyCollection(shape);

foreach
(StencilPropertyInfo info
in
propInfos)



{

this
.cmbProperty.Items.Add(info.Name);

}



}





//
根据模具、模具属性,列出对应的属性信息


string
stencilName
=
stencil.GetStencilsFileName(
this
.cmbStencilGroup.Text);

string
masterName
=

this
.cmbStencil.Text;

Visio.Shape shape

=
visApp.Documents[stencilName].Masters[masterName].Shapes[
1
];

StencilPropertyInfo info

=
property.GetProperty(shape,
this
.cmbProperty.Text);

if
(info
!=

null
)



{

this
.txtName.Text
=
info.Name;
//
属性名称



this
.txtValue.Text
=
info.Value;
//
属性值



this
.txtFormat.Text
=
info.Format;
//
属性格式



this
.txtSortKey.Text
=
info.Sort;
//
属性的排序



this
.txtPrompt.Text
=
info.Prompt;
//
属性的提示信息


}





//
根据模具,获取属性对象集合


public
List
<
StencilPropertyInfo
>
GetPropertyCollection(Visio.Shape shape)



{

List

<
StencilPropertyInfo
>
list
=

new
List
<
StencilPropertyInfo
>
();

StencilPropertyInfo propertyInfo;

Visio.Cell shapeCell;

short
shortSectionProp
=
(
short
)VisSectionIndices.visSectionProp;


if
(shape
!=

null
)



{

for
(
short
i
=

0
; i
<
shape.get_RowCount(shortSectionProp)
-

1
; i
++
)



{

if
(shape.get_CellsSRCExists(shortSectionProp, i, (
short
)VisCellIndices.visCustPropsLabel,
0
)
!=

0
)



{

propertyInfo

=

new
StencilPropertyInfo();


shapeCell

=
shape.get_CellsSRC(shortSectionProp, i, (
short
)VisCellIndices.visCustPropsLabel);

propertyInfo.Name

=
VisioUtility.FormulaStringToString(shapeCell.RowNameU);


shapeCell

=
shape.get_CellsSRC(shortSectionProp, i, (
short
)VisCellIndices.visCustPropsPrompt);

propertyInfo.Prompt

=
VisioUtility.FormulaStringToString(shapeCell.FormulaU);


shapeCell

=
shape.get_CellsSRC(shortSectionProp, i, (
short
)VisCellIndices.visCustPropsFormat);

propertyInfo.Format

=
VisioUtility.FormulaStringToString(shapeCell.FormulaU);


shapeCell

=
shape.get_CellsSRC(shortSectionProp, i, (
short
)VisCellIndices.visCustPropsValue);

propertyInfo.Value

=
VisioUtility.FormulaStringToString(shapeCell.FormulaU);


shapeCell

=
shape.get_CellsSRC(shortSectionProp, i, (
short
)VisCellIndices.visCustPropsSortKey);

propertyInfo.Sort

=
VisioUtility.FormulaStringToString(shapeCell.FormulaU);


//
shapeCell = shape.get_CellsSRC(shortSectionProp, i, (short)VisCellIndices.visCustPropsType);

//
propertyInfo.PropType = VisioUtility.FormulaStringToString(shapeCell.FormulaU);


//
shapeCell = shape.get_CellsSRC(shortSectionProp, i, (short)VisCellIndices.visCustPropsInvis);

//
propertyInfo.InVisible = VisioUtility.FormulaStringToString(shapeCell.FormulaU);


//



..



list.Add(propertyInfo);

}



}



}




return
list;

}





//
根据模具和属性名称,获取属性对象信息


public
StencilPropertyInfo GetProperty(Visio.Shape shape,
string
propertyName)



{

List

<
StencilPropertyInfo
>
list
=
GetPropertyCollection(shape);

StencilPropertyInfo propertyInfo

=

null
;

foreach
(StencilPropertyInfo tempInfo
in
list)



{

if
(tempInfo.Name
==
propertyName)



{

propertyInfo

=
tempInfo;

break
;

}



}




return
propertyInfo;

}

控件代码及测试例子:
https://files.cnblogs.com/wuhuacong/CommonSearch.rar

使用场景:

在列表页面中,一般有好几个条件, 用户进行查询时候,需要根据这几个条件进行过滤查询.但在组装这些过滤条件的时候,代码比较烦琐臃肿,本组件代码为解决该问题而设计。

使用目的:
1.减少对参数非空的条件判断 2. 可以构造出参数化的DbCommand对象,简化操作. 3.适当修改后可以用于其他数据访问的参数化参数生成.4.构造Sql语句或者参数化条件更加易读


1. 生成SQL条件语句

如有几个字段,需要根据不同的字段进行过滤,想生成的SQL语句如下:
Where (1=1)  AND AA2  Like  '%AA2Value%' AND AA6  >=  'Value6' AND AA7  <=  'value7' AND AA3  =  'Value3' AND AA4  <  'Value4' AND AA5  >  'Value5' AND AA  <>  '1'

那么代码如下:


SearchCondition search
=

new
SearchCondition();

search.AddCondition(

"
AA
"
,
1
, SqlOperator.NotEqual)

.AddCondition(

"
AA2
"
,
"
AA2Value
"
, SqlOperator.Like)

.AddCondition(

"
AA3
"
,
"
Value3
"
, SqlOperator.Equal)

.AddCondition(

"
AA4
"
,
"
Value4
"
, SqlOperator.LessThan)

.AddCondition(

"
AA5
"
,
"
Value5
"
, SqlOperator.MoreThan)

.AddCondition(

"
AA6
"
,
"
Value6
"
, SqlOperator.MoreThanOrEqual)

.AddCondition(

"
AA7
"
,
"
value7
"
, SqlOperator.LessThanOrEqual);

string
conditionSql
=
search.BuildConditionSql();


2. 生成基于Enterprise Library的DbCommand对象


Database db
=
DatabaseFactory.CreateDatabase();

SearchCondition search

=

new
SearchCondition();

search.AddCondition(

"
Name
"
,
"
测试
"
, SqlOperator.Like)

.AddCondition(

"
ID
"
,
1
, SqlOperator.MoreThanOrEqual);

DbCommand dbComand

=
search.BuildDbCommand(db,
"
select Comments from Test
"
,
"
Order by Name
"
);

using
(IDataReader dr
=
db.ExecuteReader(dbComand))



{

while
(dr.Read())



{

this
.txtSql.Text
+=

"
\r\n
"

+
dr[
"
Comments
"
].ToString();

}



}


下面是该控件的类对象图解
Search.jpg

下面我们比较一下使用该控件和不使用在列表查询页面中的代码,可以看出使用了控件后的代码大大较少了,并且可读性也增强了

1. 使用该控件, 列表查询页面中的代码



private

string
GetCondition()



{

SearchCondition search

=

new
SearchCondition();

search.AddCondition(

"
GroupID
"
,
this
.ddlUserGroup.SelectedValue, SqlOperator.Equal,
true
)
//
班组ID



.AddCondition(
"
DealGroupName
"
,
this
.ddlDealGroup.SelectedValue, SqlOperator.Equal,
true
)

/*
消缺单位
*/



.AddCondition(

"
VisioStationID
"
,
this
.ddlStation.SelectedValue, SqlOperator.Like,
true
)
//
变电站



.AddCondition(
"
VisioImageID
"
,
this
.ddlLine.SelectedValue, SqlOperator.Like,
true
)

/*
馈线
*/




.AddCondition(

"
BugNo
"
,
this
.txtBugNo.Text.Trim(), SqlOperator.Like,
true
)

/*
编号
*/




.AddCondition(

"
Finder
"
,
this
.ddlFindUser.SelectedValue, SqlOperator.Like,
true
)

/*
发现人
*/



.AddCondition(

"
CheckUser
"
,
this
.ddlCheckUser.SelectedValue, SqlOperator.Like,
true
)
//
验收人


.AddCondition(
"
DeviceBug.BugType
"
,
this
.ddlBugType.SelectedValue, SqlOperator.Equal,
true
)
//
缺陷类别


.AddCondition(
"
CurrentState
"
,
this
.ddlCurrentState.SelectedValue, SqlOperator.Equal,
true
)
//
处理状态


.AddCondition(
"
FindDate
"
,
this
.txtFindBeginDate.Text.Trim(), SqlOperator.MoreThanOrEqual,
true
)
//
发现日期


.AddCondition(
"
FindDate
"
,
this
.txtFindEndDate.Text.Trim(), SqlOperator.LessThanOrEqual,
true
)
//
发现日期


.AddCondition(
"
EndDate
"
,
this
.txtEndBeginDate.Text.Trim(), SqlOperator.MoreThanOrEqual,
true
)
//
消缺日期


.AddCondition(
"
EndDate
"
,
this
.txtEndEndDate.Text.Trim(), SqlOperator.LessThanOrEqual,
true
);
//
消缺日期




return
search.BuildConditionSql();

}


2. 普通做法,不使用控件,列表查询页面中的代码



Code


以编程方式使用 Microsoft Office Visio 2003 ActiveX 控件

Visio 2003 开发入门

使用Visio 2003 Drawing Control开发应用

Visio 2003 ShapeStudio 形状开发入门

Drawing Visio Shapes in the Visio ActiveX Control using C# and .NET

Using Universal Names and Syntax to Localize Your Visio Solutions

Integrating Menus and Toolbars in Visio 2003 Drawing Control Applications

Using CommandBar Objects to Customize the Visio User Interface

还有最好的帮助就是VisioSDK 2007 中的帮助文档了,虽然是英文的,但是很容易懂
最后是一篇我前段时间写的文章:
如何使用C#进行Visio二次开发

希望您有更好的文章,一起收录学习

问题说明:
在获取数据库记录数据的时候, 通常返回的ArrayList集合, 没有了层次关系. 如果每次根据PID重新到数据库获取记录, 可以做到, 但有以下几个缺点:

1. 访问数据库记录次数随着记录的增多而增多

2. 由于需要多次访问数据库, 因此访问速度受影响

3. 需要数据库访问层的支持, 并对记录进行转化, 耦合性太强

4. 通用性不好, 每次需要一个新的类型列表, 就需要重新编写
解决方法:
我根据原有的树状结构算法代码, 编写一个通用的算法, 利用反射原理, 递归的对数据进行筛选.

这样只需要访问数据库一次, 然后就在内存中遍历, 而且适合于所有具有(PID, ID, Name)属性的实体类集合的排序.


如我需要生成设备类型实体类集合的树状结构时候, 代码如下:


ArrayList equipTypelist
=
equipmentType.GetAll();

equipTypelist

=
CollectionHelper.GetTreeItems(equipTypelist);

this
.ddlEquipmentTypes.DataSource
=
equipTypelist;

this
.ddlEquipmentTypes.DataTextField
=

"
Name
"
;

this
.ddlEquipmentTypes.DataValueField
=

"
ID
"
;

this
.ddlEquipmentTypes.DataBind();


this
.ddlEquipmentType.Items.Insert(
0
,
new
ListItem(
"
(全部)
"
,
"
0
"
));




public

class
CollectionHelper



{

private

static
ArrayList Fill(
int
pID,
int
level, ArrayList list)



{

ArrayList returnList

=

new
ArrayList();

foreach
(
object
obj
in
list)



{

int
typePID
=
(
int
)ReflectionUtil.GetProperty(obj,
"
PID
"
);

int
typeID
=
(
int
)ReflectionUtil.GetProperty(obj,
"
ID
"
);

string
typeName
=
ReflectionUtil.GetProperty(obj,
"
Name
"
)
as

string
;


if
(pID
==
typePID)



{

string
newName
=

new

string
(
'
-
'
, level
*

4
)
+
typeName;

ReflectionUtil.SetProperty(obj,

"
Name
"
, newName);

returnList.Add(obj);


returnList.AddRange(Fill(typeID, level

+
1
, list));

}



}



return
returnList;

}






///

<summary>


///
生成有层次结构的列表

///

</summary>


///

<param name="list">
具有Name,ID,PID成员的任何集合
</param>


///

<returns></returns>



public

static
ArrayList GetTreeItems(ArrayList list)



{

return
Fill(
-
1
,
0
, list);

}



}





public

sealed

class
ReflectionUtil



{

private
ReflectionUtil()



{ }



public

static
BindingFlags bf
=
BindingFlags.DeclaredOnly
|
BindingFlags.Public
|


BindingFlags.NonPublic

|
BindingFlags.Instance
|
BindingFlags.Static;


public

static

void
SetProperty(
object
obj,
string
name,
object
value)



{

PropertyInfo fi

=
obj.GetType().GetProperty(name, bf);

fi.SetValue(obj, value,

null
);

}




public

static

object
GetProperty(
object
obj,
string
name)



{

PropertyInfo fi

=
obj.GetType().GetProperty(name, bf);

return
fi.GetValue(obj,
null
);

}



}


效果图如下
树状列表效果图.jpg

问题说明:
在ActiveRecord或者其他的ORM等代码中, 我们经常可以看到自定义特性(Attribute)的存在(如下面的代码所示)

[PrimaryKey(PrimaryKeyType.Native, "PostId")]
public int Id
{

......

}


看似非常神秘的东西, 我们在什么场合需要使用它, 我们该如何使用它?
自定义特性(Attribute)在一些需要声明特定信息的时候非常有用, 如标识实体类属于那个表, 那个字段是否为主键,主键的数据库字段名是什么这些信息的时候就会派上用场, 使用的时候也是非常的简单.
解决方法:
1. 定义一个以Attribute结尾的特性类, 特性类继承自System.Attribute, 如下所示.

[AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
public class PrimaryKeyAttribute : System.Attribute
{

.............



其中AttributeTargets是一个枚举的值, 可以是: Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate


2. 在需要使用的地方使用PrimaryKey自定义特性标签, 如下所示.

[PrimaryKey(Column = "CustomerID", IsIdentity=false)]
public int ID
{

.............


3. 为了获取自定义特性的信息, 需要反射的方式获取其数据, 首先我们定义一个类来存储这些信息, 如下所示

public class PrimaryKeyModel
{
private readonly PropertyInfo propertyInfo;//外键的属性字段信息
private readonly PrimaryKeyAttribute primaryKeyAtt;//外键的特性信息

................


public static PrimaryKeyModel GetPrimaryKey(Type type)
{
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo p in properties)
{
object[] keys = p.GetCustomAttributes(typeof(PrimaryKeyAttribute), true);
if (keys.Length == 1)
{
return new PrimaryKeyModel(p, keys[0] as PrimaryKeyAttribute);
}
}

return null;
}


4. 在Customer类中获取其特性的信息代码如下

string strReturn = string.Empty;
//Get PrimaryKey Name
PrimaryKeyModel attribute= PrimaryKeyModel.GetPrimaryKey(this.GetType());
if(attribute != null)
{
strReturn += string.Format("PrimaryKey Name:{0} IsIdentity:{1} Column:{2}\r\n",
attribute.Property.Name, attribute.PrimaryKeyAtt.IsIdentity, attribute.PrimaryKeyAtt.Column);
}
详细解释:
附件的实例中演示了如何获取类和属性的自定义特性, 类的特性包括获取实体类的表名称, 属性的特性包括获取外键的信息


附件代码下载:
https://files.cnblogs.com/wuhuacong/TestPrimaryKeyAttribute.rar