Revit二次开发-在Revit选项卡创建自己的Ribbon按钮

前言

本博客为笔者初入Revit二次开发第一篇博客,如有错误或疑问欢迎指正交流。后续笔者将持续输出与Revit二次开发的相关内容,对有新的实现方法也会进行更新,欢迎收藏关注。

一、应用场景

在Revit软件界面中有两种形式的按钮,一种是单命令按钮,一种是含下拉菜单的按钮。下面就让笔者分别介绍两种按钮的实现方法。

二、实现方法

创建界面按钮的(UIRibbon)的大体思路就是:1、编写好自己的命令文件(一般就是继承IExteralCommand接口);2、创建一个继承IExternalApplication接口的文件;3、编辑Addin文档。

新建自己编写好的命令文件这个大家自己准备就好了,确保自己测试能用就行。

下面就继承IExternalApplication接口的类的编辑

1.新建一个选项卡

1
2
application.CreateRibbonTab("NewTab");
RibbonPanel ribbonPanel = application.CreateRibbonPanel("NewTab","TabBar");

NewTab对应的选项卡的名称,TabBar对应的就是选项栏的名称。

2.在新建的选项卡中创建一个单命令按钮

1
2
PushButtonData p3 = new PushButtonData("命令名称3", "Text3", @"C:\Users\Jhon\Desktop\StudyRevit\HelloRevit\HelloRevit\bin\Debug\HelloRevit.dll", "HelloRevit.Class1");
PushButton pushButton3= ribbonPanel.AddItem(p3) as PushButton;

这里的“命令名称3”就程序内部的命令名称,用户看到的是“Text3”。@后面的是该按钮触发的命令文件路径(也就是第1步自己编写和测试好的命令文件);最后一个参数是该命令的全称。

3.1在新建的选项卡中创建一个可下拉按钮

1
2
SplitButtonData sbd1 = new SplitButtonData("Name", "Text");
SplitButton sb1 = ribbonPanel.AddItem(sbd1) as SplitButton;

3.2在可下拉按钮下创建两个单命令按钮

1
2
3
4
PushButtonData p1 = new PushButtonData("命令名称1", "Text1", @"C:\Users\Jhon\Desktop\StudyRevit\HelloRevit\HelloRevit\bin\Debug\HelloRevit.dll", "HelloRevit.Class1");
PushButton pushButton1= sb1.AddPushButton(p1);
PushButtonData p2 = new PushButtonData("命令名称2", "Text2", @"C:\Users\Jhon\Desktop\StudyRevit\HelloRevit\HelloRevit\bin\Debug\HelloRevit.dll", "HelloRevit.Class1");
PushButton pushButton2 = sb1.AddPushButton(p2);

4.为上述三个按钮添加图片

1
2
3
4
5
Uri uriImage = new Uri(@"C:\Users\Jhon\Desktop\StudyRevit\Ribbon\timg.jpg");
BitmapImage largeImage = new BitmapImage(uriImage);
pushButton1.LargeImage = largeImage;
pushButton2.LargeImage = largeImage;
pushButton3.LargeImage = largeImage;

这里要引用PresentationCore程序集,再引用system.windows.media.imaging

5.示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;

namespace Ribbon
{
public class Class1 : IExternalApplication
{
public Result OnShutdown(UIControlledApplication application)
{

return Result.Succeeded;
}

public Result OnStartup(UIControlledApplication application)
{
//新建一个选项卡,并在该选项卡总新建一个命令栏(命令栏可以放多个命令按钮)
application.CreateRibbonTab("NewTab");
RibbonPanel ribbonPanel = application.CreateRibbonPanel("NewTab","TabBar");

//1、建立一个可下拉的命令栏
//1.1、新建一个可下拉按钮
SplitButtonData sbd1 = new SplitButtonData("Name", "Text");
SplitButton sb1 = ribbonPanel.AddItem(sbd1) as SplitButton;
//1.2、在该按钮是添加两个命令按钮(软件中按钮的名称由命令名称文本定义)
PushButtonData p1 = new PushButtonData("命令名称1", "Text1", @"C:\Users\Jhon\Desktop\StudyRevit\HelloRevit\HelloRevit\bin\Debug\HelloRevit.dll", "HelloRevit.Class1");
PushButton pushButton1= sb1.AddPushButton(p1);
PushButtonData p2 = new PushButtonData("命令名称2", "Text2", @"C:\Users\Jhon\Desktop\StudyRevit\HelloRevit\HelloRevit\bin\Debug\HelloRevit.dll", "HelloRevit.Class1");
PushButton pushButton2 = sb1.AddPushButton(p2);

//2、在选项卡栏添加一个普通按钮
PushButtonData p3 = new PushButtonData("命令名称3", "Text3", @"C:\Users\Jhon\Desktop\StudyRevit\HelloRevit\HelloRevit\bin\Debug\HelloRevit.dll", "HelloRevit.Class1");
PushButton pushButton3= ribbonPanel.AddItem(p3) as PushButton;

//3、先准备一张图片,后面给按钮加图片。(这里要引用PresentationCore程序集,再引用system.windows.media.imaging)
Uri uriImage = new Uri(@"C:\Users\Jhon\Desktop\StudyRevit\Ribbon\timg.jpg");
BitmapImage largeImage = new BitmapImage(uriImage);
//3.1、将图片赋值给按钮。PushButton有两个属性,当按钮是堆叠时,显示的是Image;当按钮是下拉或单个的时候显示的是LargeImage。
pushButton1.LargeImage = largeImage;
pushButton2.LargeImage = largeImage;
pushButton3.LargeImage = largeImage;

return Result.Succeeded;
}
}
}

6.编辑Addin文件

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<RevitAddIns>
<AddIn Type="Application">
<Name>HelloRevit</Name>
<Assembly>C:\Users\Jhon\Desktop\StudyRevit\Ribbon\Ribbon\bin\Debug\Ribbon.dll</Assembly>
<AddInId>6cdba932-c058-4ec1-b038-33ed590c41d3</AddInId>
<FullClassName>Ribbon.Class1</FullClassName>
<VendorId>ADSK</VendorId>
</AddIn>
</RevitAddIns>

Addin文件编辑需要注意的点:1、路径名称一定要是第2步实现IExternalApplication接口的文件的路径名称。2、FullClassName就是该文件名称空间下的类(本文例子的名称空间为Ribbon,类名为Class1)

Addin文件的存放路径为C:\ProgramData\Autodesk\Revit\Addins\2018\(2018对应的是你安装的Revit版本,笔者安装的是2018版的Revit就放2018文件夹下)

三、最终效果

在这里插入图片描述
这里的NewTab和TabBar名称都可以在“1.新建一个选项卡”中就行修改
在这里插入图片描述
Text1、Text2、Text3就是PushButtonData里的参数。

Revit二次开发-创建IURibbon中使用相对地址

前言

  我们在进行二次开发的时候经常会把自己开发好的DLL文件连同Addin文件发给别人,让别人复制到电脑上的Addin文件夹进行插件的使用。
  可是当我们把自己的DLL文件发给别人的时候会发现经常出现由于代码中的地址发生改变使得插件无法使用。我们可以在编程中将外部命令、创建UIRibbon的外部应用和UIRibbon使用的图片封装在一个DLL文件中。这样我们只要发送一dll和一个addin文件给对方,一起放在Addin文件夹中即可使用。类似LookUp插件的使用效果。

一、创建DLL文件

  新建一个UIRibbon类库(名字随意,我只是举例),在该类库中添加两个类,一个是继承IExteranlCommand接口的命令类(插件命令),一个是继承IExternalApplication接口创建UIRibbon的功能按钮类。UIRibbon是一个库(.dll),库中可以有很多类,所有命令类和按钮类可以封装在一个库中。
在这里插入图片描述
  这里我编写的最简单的显示HelloRevit的命令进行示例。

二、按钮类编写(UIRibbon)

1.程序集的引用

  除了正常的RevitAPI及RevitAPIUI的引用外,程序里图片的使用还需要引用PresentationCore和WindowsBase两个程序集。引用如下:
在这里插入图片描述

2.代码编写

1、新建一个选项卡,并在该选项卡下创建一个面板

1
2
application.CreateRibbonTab("NewTab");
RibbonPanel ribbonPanel = application.CreateRibbonPanel("NewTab", "NewPanel");

2、获得UIRibbon.dll的相对地址,即不是写死了的地址,会根据dll所放的位置的改变而改变

1
string assemblyPash = Assembly.GetExecutingAssembly().Location;

3、编写PushButtonDate

1
2
string className = "UIRibbon.HelloRevit";
PushButtonData pushButtonData = new PushButtonData("ButtonName", "CommandName", assemblyPash, className);

4、将图片封装在dll,并对其进行引用。在类库项目右键,新建文件夹,将准备好的图面拖到文件里面。(当然放根目录也行)在这里插入图片描述
5、将图片的生成操作改成”嵌入的资源“,这样在生成dll的时候图片会一并封装在里面。在这里插入图片描述
6、嵌入图片的使用

1
2
3
Stream sm = Assembly.GetExecutingAssembly().GetManifestResourceStream("UIRibbon.Image.图标.png");
pushButtonData.LargeImage = BitmapFrame.Create(sm);
ribbonPanel.AddItem(pushButtonData);

这里的UIRibbon对应的是类库名称(也是名称空间),Image是文件夹名,后面的就是图片的文件名称。这里的点相当于文件夹的访问。
第一句是获取当前程序集的指定资源,返回值是一个Stream。第二句是将返回的stream创建一个imagesource。并进行使用。最后添加按钮。完成。

3.完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using Autodesk.Revit.UI;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;

namespace UIRibbon
{
class UIRibbon : IExternalApplication
{
public Result OnShutdown(UIControlledApplication application)
{
return Result.Succeeded;
}

public Result OnStartup(UIControlledApplication application)
{
application.CreateRibbonTab("NewTab");//创建选项卡
RibbonPanel ribbonPanel = application.CreateRibbonPanel("NewTab", "NewPanel");//创建面板
string assemblyPash = Assembly.GetExecutingAssembly().Location;//获取程序集的相对地址
string className = "UIRibbon.HelloRevit";//外部命令的FullName
PushButtonData pushButtonData = new PushButtonData("ButtonName", "CommandName", assemblyPash, className);
Stream sm = Assembly.GetExecutingAssembly().GetManifestResourceStream("UIRibbon.Image.图标.png");//获取嵌入的图片
pushButtonData.LargeImage = BitmapFrame.Create(sm);//创建图片并赋值给LargeImage
ribbonPanel.AddItem(pushButtonData);

return Result.Succeeded;
}
}
}

三、dll文件和addin文件的使用

1、打开Revit,点击附加模块的外部工具,选择右侧的LoadedApplications,选择下面的Load加载我们刚编译好的UIRibbon.dll点右下角的save,选择第二个(Save chacked items to local .addin file),Save前记得把Loaded Commads里面的勾全部去掉。在载入文件夹生成一个addin文件。这样在我们编译的文件夹就会生成一个我们需要的addin文件,不用去自己编写addin文件。在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
2、如果前面忘记把勾去掉,addin文件就是生成我们不需要加载的东西。记事本打开addin文件,将我们不需要加载的HelloRevit删掉,我们只需要加载UIRibbon这个类就行。HelloRevit在按钮中调用即可。
在这里插入图片描述
3、将dll和addin文件复制,粘贴到C:\ProgramData\Autodesk\Revit\Addins\2018文件夹下,打开Revit后就能正常使用。(2018是我安装的Revit版本,故放在此文件夹下)
在这里插入图片描述

四、最终效果

在这里插入图片描述
这样我们只需要把编译好的dll和addin文件发给别人,别人只需要放在上述的文件夹下就能使用。不会因为改变路径报错。
另外还有”黑夜de骑士”方法
将图片属性的“生成操作”改成“Resource”
在这里插入图片描述
代码变成

1
pushButtonData.LargeImage = new BitmapImage(new Uri("pack://application:,,,/UIRibbon;component/Image/图标.png"));

也能达到同样的效果。这里的UIRibbon名称空间,Image就是文件夹名,最后就是文件名,这三根据自己需求修改。其他就固定写法,照抄就行。
B站视频地址:https://www.bilibili.com/video/BV1MJ41167mb?t=2413

.Net平台开源可编辑Excel库ClosedXML,可代替Aspose.Cells

1. 库的引用

右键项目,点击管理NuGet程序包在浏览选项卡中输入ClosedXML,点击右侧箭头进行安装。
在这里插入图片描述
在这里插入图片描述

2. API的使用

2.1 创建工作簿

1
2
3
4
//新建工作簿
XLWorkbook workbook = new XLWorkbook();
//打开现有工作簿
XLWorkbook workbook2 = new XLWorkbook("C:\\Users\\Administrator\\Desktop\\ClosedXML_API_Test\\sampleWorkbook.xlsx");

2.2 创建工作表

1
2
//创建工作表
IXLWorksheet worksheet = workbook.AddWorksheet("Sample Sheet");

2.3 字体设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
IXLCell a1 = worksheet.Cell("A1");
a1.Value = "字体设置";
//1、更换字体
a1.Style.Font.FontName="宋体";
//2、调整字号
a1.Style.Font.FontSize=20;
//3、加粗
a1.Style.Font.Bold = true;
//4、斜体
a1.Style.Font.Italic = true;
//5、下划线
a1.Style.Font.Underline = XLFontUnderlineValues.Single;
//6、字体颜色
a1.Style.Font.FontColor = XLColor.Blue;

2.4 单元格设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//单元格设置
IXLCell a2 = worksheet.Cell("A2");
a2.Value = "单元格设置长长长长长长长";
//1、边框
a2.Style.Border.TopBorder = XLBorderStyleValues.Thin;
a2.Style.Border.BottomBorder = XLBorderStyleValues.Thin;
a2.Style.Border.LeftBorder = XLBorderStyleValues.Thin;
a2.Style.Border.RightBorder = XLBorderStyleValues.Thin;
//2、填充颜色
a2.Style.Fill.BackgroundColor = XLColor.Red;
//3、竖直对齐方式
a2.Style.Alignment.Vertical = XLAlignmentVerticalValues.Bottom;
//4、水平对其方式
a2.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Right;
//5、自动换行
a2.Style.Alignment.WrapText = true;
//6、单元格格式
IXLCell a3 = worksheet.Cell("A3");
a3.Value = 0.003125;
a3.Style.NumberFormat.NumberFormatId = (int)XLPredefinedFormat.Number.PercentPrecision2;

2.5 行高

1
2
3
4
//行高
IXLCell a4 = worksheet.Cell("A4");
a4.Value = "行高90";
worksheet.Row(4).Height = 90;

2.6 列宽

1
2
3
4
//列宽
IXLCell b1 = worksheet.Cell("B1");
b1.Value = "列宽60";
worksheet.Column(2).Width = 60;

2.7 合并单元格

1
2
3
4
5
//合并单元格
IXLCell c1 = worksheet.Cell("C1");
IXLCell d1 = worksheet.Cell("D1");
c1.Value = "合并单元格";
worksheet.Range(c1, d1).Merge();

2.8 插入公式

1
2
//插入公式
worksheet.Cell("C2").FormulaA1 = "MID(C1, 2, 1)";

2.9 查找

1
2
3
//查找
string id = worksheet.Search("列宽").First().Address.ToString(XLReferenceStyle.A1);
worksheet.Cell("B4").Value = $"查找列宽的位置为:{id}";

2.10 富文本

1
2
3
4
5
//富文本
IXLCell c4 = worksheet.Cell("C4");
IXLRichText c4RichText = c4.CreateRichText();
c4RichText.AddText("富").SetBold(true).SetFontSize(30);
c4RichText.AddText("文本").SetFontColor(XLColor.Red);

2.11 保存工作簿

1
2
3
4
5
//保存工作簿
//1、保存
workbook.Save();
//2、另存为
workbook.SaveAs("C:\\Users\\Administrator\\Desktop\\ClosedXML_API_Test\\HelloWorld.xlsx");

3. 完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
using ClosedXML.Excel;
using System.Linq;

namespace ClosedXML_API_Test
{
internal class Program
{
static void Main(string[] args)
{
//创建工作簿
//新建工作簿
XLWorkbook workbook = new XLWorkbook();
//打开现有工作簿
//XLWorkbook workbook2 = new XLWorkbook("C:\\Users\\Administrator\\Desktop\\ClosedXML_API_Test\\sampleWorkbook.xlsx");
//创建工作表
IXLWorksheet worksheet = workbook.AddWorksheet("Sample Sheet");

IXLCell a1 = worksheet.Cell("A1");
a1.Value = "字体设置";
//1、更换字体
a1.Style.Font.FontName = "宋体";
//2、调整字号
a1.Style.Font.FontSize = 20;
//3、加粗
a1.Style.Font.Bold = true;
//4、斜体
a1.Style.Font.Italic = true;
//5、下划线
a1.Style.Font.Underline = XLFontUnderlineValues.Single;
//6、字体颜色
a1.Style.Font.FontColor = XLColor.Blue;

//单元格设置
IXLCell a2 = worksheet.Cell("A2");
a2.Value = "单元格设置长长长长长长长";
//1、边框
a2.Style.Border.TopBorder = XLBorderStyleValues.Thin;
a2.Style.Border.BottomBorder = XLBorderStyleValues.Thin;
a2.Style.Border.LeftBorder = XLBorderStyleValues.Thin;
a2.Style.Border.RightBorder = XLBorderStyleValues.Thin;
//2、填充颜色
a2.Style.Fill.BackgroundColor = XLColor.Red;
//3、竖直对齐方式
a2.Style.Alignment.Vertical = XLAlignmentVerticalValues.Bottom;
//4、水平对其方式
a2.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Right;
//5、自动换行
a2.Style.Alignment.WrapText = true;
//6、单元格格式
IXLCell a3 = worksheet.Cell("A3");
a3.Value = 0.003125;
a3.Style.NumberFormat.NumberFormatId = (int)XLPredefinedFormat.Number.PercentPrecision2;

//行高
IXLCell a4 = worksheet.Cell("A4");
a4.Value = "行高90";
worksheet.Row(4).Height = 90;

//列宽
IXLCell b1 = worksheet.Cell("B1");
b1.Value = "列宽60";
worksheet.Column(2).Width = 60;

//合并单元格
IXLCell c1 = worksheet.Cell("C1");
IXLCell d1 = worksheet.Cell("D1");
c1.Value = "合并单元格";
worksheet.Range(c1, d1).Merge();

//插入公式
worksheet.Cell("C2").FormulaA1 = "MID(C1, 2, 1)";

//查找
string id = worksheet.Search("列宽").First().Address.ToString(XLReferenceStyle.A1);
worksheet.Cell("B4").Value = $"查找列宽的位置为:{id}";

//富文本
IXLCell c4 = worksheet.Cell("C4");
IXLRichText c4RichText = c4.CreateRichText();
c4RichText.AddText("富").SetBold(true).SetFontSize(30);
c4RichText.AddText("文本").SetFontColor(XLColor.Red);

//保存工作簿
////1、保存
//workbook.Save();
//2、另存为
workbook.SaveAs("C:\\Users\\Administrator\\Desktop\\ClosedXML_API_Test\\HelloWorld.xlsx");
}
}
}

4. 效果展示

在这里插入图片描述

5. 解决无法读取xls格式文件问题

5.1 使用Aspose.Cells免费版库进行格式转换

NuGet包管理器中搜索并安装Aspose.Cells库
在这里插入图片描述
安装完成后,执行以下代码进行格式转换:

1
2
3
Workbook workbook = new Workbook("C:\\Users\\Administrator\\Desktop\\ClosedXML_API_Test\\convertFormat.xls");
workbook.Save("C:\\Users\\Administrator\\Desktop\\ClosedXML_API_Test\\convertFormatAspose.xlsx",SaveFormat.Xlsx);
workbook.Dispose();

转换后效果如下:
在这里插入图片描述
存在问题:免费版会多一个水印,不过不影响使用
在这里插入图片描述

5.2 使用NPOI库进行内容复制

NuGet包管理器中搜索并安装NPOI库
在这里插入图片描述
安装完成后,执行以下代码进行内容复制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
// 读取xls格式文件
FileStream fs = new FileStream("C:\\Users\\Administrator\\Desktop\\ClosedXML_API_Test\\convertFormat.xls", FileMode.Open, FileAccess.Read);
HSSFWorkbook workbook = new HSSFWorkbook(fs);

// 创建xlsx格式文件
XSSFWorkbook newWorkbook = new XSSFWorkbook();

// 遍历所有sheet
for (int i = 0; i < workbook.NumberOfSheets; i++)
{
ISheet sheet = workbook.GetSheetAt(i);
ISheet newSheet = newWorkbook.CreateSheet(sheet.SheetName);

//复制合并单元格
for (int k = 0; k < sheet.NumMergedRegions; k++)
{
CellRangeAddress cellRangeAddress = sheet.GetMergedRegion(k);
if (sheet.IsMergedRegion(cellRangeAddress))
{
newSheet.AddMergedRegion(cellRangeAddress);
}
}


// 遍历所有row
for (int j = 0; j <= sheet.LastRowNum; j++)
{
IRow row = sheet.GetRow(j);
IRow newRow = newSheet.CreateRow(j);
//复制行高
newRow.Height = row.Height;

// 遍历所有cell
if (row != null)
{
for (int k = row.FirstCellNum; k < row.LastCellNum; k++)
{
ICell cell = row.GetCell(k);
ICell newCell = newRow.CreateCell(k);

// 复制cell的值和类型
if (cell != null)
{
//设置列宽
newCell.Sheet.SetColumnWidth(cell.ColumnIndex, cell.Sheet.GetColumnWidth(cell.ColumnIndex));

//逐个复制样式
ICellStyle style = newWorkbook.CreateCellStyle();
style.Alignment = cell.CellStyle.Alignment;
style.BorderBottom = cell.CellStyle.BorderBottom;
style.BorderDiagonal = cell.CellStyle.BorderDiagonal;
style.BorderDiagonalColor = cell.CellStyle.BorderDiagonalColor;
style.BorderDiagonalLineStyle = cell.CellStyle.BorderDiagonalLineStyle;
style.BorderLeft = cell.CellStyle.BorderLeft;
style.BorderRight = cell.CellStyle.BorderRight;
style.BorderTop = cell.CellStyle.BorderTop;
style.BottomBorderColor = cell.CellStyle.BottomBorderColor;
style.DataFormat = cell.CellStyle.DataFormat;
style.FillBackgroundColor = cell.CellStyle.FillBackgroundColor;
style.FillForegroundColor = cell.CellStyle.FillForegroundColor;
style.FillPattern = cell.CellStyle.FillPattern;
style.Indention = cell.CellStyle.Indention;
style.IsHidden = cell.CellStyle.IsHidden;
style.IsLocked = cell.CellStyle.IsLocked;
style.LeftBorderColor = cell.CellStyle.LeftBorderColor;
style.RightBorderColor = cell.CellStyle.RightBorderColor;
style.Rotation = cell.CellStyle.Rotation;
style.ShrinkToFit = cell.CellStyle.ShrinkToFit;
style.TopBorderColor = cell.CellStyle.TopBorderColor;
style.VerticalAlignment = cell.CellStyle.VerticalAlignment;
style.WrapText = cell.CellStyle.WrapText;

//复制字体
var cellFont = cell.CellStyle.GetFont(workbook);
IFont font = newWorkbook.CreateFont();
font.Color = cellFont.Color;
font.FontHeight = cellFont.FontHeight;
font.FontHeightInPoints = cellFont.FontHeightInPoints;
font.FontName = cellFont.FontName;
font.IsBold = cellFont.IsBold;
font.IsItalic = cellFont.IsItalic;
font.IsStrikeout = cellFont.IsStrikeout;
font.TypeOffset = cellFont.TypeOffset;
font.Underline = cellFont.Underline;
style.SetFont(font);

newCell.CellStyle = style;

newCell.SetCellType(cell.CellType);
switch (cell.CellType)
{
case CellType.Blank:
newCell.SetCellValue(cell.StringCellValue);
break;
case CellType.Boolean:
newCell.SetCellValue(cell.BooleanCellValue);
break;
case CellType.Error:
newCell.SetCellValue(cell.ErrorCellValue);
break;
case CellType.Formula:
newCell.SetCellFormula(cell.CellFormula);
break;
case CellType.Numeric:
newCell.SetCellValue(cell.NumericCellValue);
break;
case CellType.String:
newCell.SetCellValue(cell.StringCellValue);
break;
case CellType.Unknown:
newCell.SetCellValue(cell.StringCellValue);
break;
}
}
}
}
}
}

// 写入xlsx格式文件
FileStream newFs = new FileStream("C:\\Users\\Administrator\\Desktop\\ClosedXML_API_Test\\convertFormatNPOI.xlsx", FileMode.Create, FileAccess.Write);
newWorkbook.Write(newFs);

// 关闭文件流
newFs.Close();
fs.Close();

复制效果如下:
在这里插入图片描述
存在问题
1、对于以下这种提示损坏的xls文件,NPOI和Sylvan.Data.Excel均无法打开,但Aspose.Cells可打开。
在这里插入图片描述
2、NPOI将xls转换为xlsx的方式并非进行格式转换,而是进行内容1:1复制,但富文本无法进行复制

5.3 使用Sylvan.Data.Excel库进行格式转换

NuGet包管理器中搜索并安装Sylvan.Data.Excel库
在这里插入图片描述
执行以下代码进行格式转换:

1
2
3
4
5
6
7
8
9
ExcelDataReader edr = ExcelDataReader.Create("C:\\Users\\Administrator\\Desktop\\ClosedXML_API_Test\\convertFormat.xls");
// 创建一个ExcelDataWriter对象,写入xlsx文件
ExcelDataWriter edw = ExcelDataWriter.Create("C:\\Users\\Administrator\\Desktop\\ClosedXML_API_Test\\convertFormatSylvan.xlsx");
// 将ExcelDataReader对象中的数据写入到ExcelDataWriter对象中
edw.Write(edr);
// 关闭ExcelDataReader对象
edr.Close();
// 关闭ExcelDataWriter对象
edw.Dispose();

转换效果如下:
在这里插入图片描述
存在问题:转换后无法保留单元格格式,仅可用于内容查询。

群晖docker使用nginx部署前端

1. 群晖文件目录创建放置前端文件的文件夹

①在群晖的File Station中,选择一个文件夹,在空白处右键,点击新建文件夹。
在这里插入图片描述
②在弹出的窗口中输入文件夹名称后,点击确定,完成文件夹创建。
在这里插入图片描述
③按照上述方法在新建的文件夹中再创建一个名为html的文件夹用于放置前端静态文件。
在这里插入图片描述

2. docker安装nginx

①打开群晖docker,选择注册表,右键nginx镜像,点击下载此映像。
在这里插入图片描述
②在弹出的选择标签窗口,点击应用。
在这里插入图片描述
③转到映像页面,右键下载的nginx镜像,点击运行。
在这里插入图片描述
④在docker容器的常规设置中填写容器名称后,点击下一步。
在这里插入图片描述
⑤在端口设置中,将docker中的80端口映射到本地的自定义端口,这边映射到2088端口。
在这里插入图片描述
⑥在存储空间设置中,将容器的/usr/share/nginx/html文件夹映射到第一步新建的html文件夹。用于将群晖中的文件放置到dokcer容器中。点击下一步,点击完成即可完成容器创建。
在这里插入图片描述
⑦跳转到容器页面,右键新建的nginx容器,点击详情。
在这里插入图片描述
⑧跳转到日志页面,当容器处于start woker process 状态时即容器启动成功。
在这里插入图片描述

3. 映射本地端口到公网

①将刚刚设置的本地2088端口映射到公网进行访问。在控制面板中点击外部访问,点击新增,勾选新建的docker容器,点击完成。
在这里插入图片描述
②完成后,点击应用,待测联机测试结果显示OK即完成端口映射操作。
在这里插入图片描述
③跳转到你的域名后2088端口,如显示以下画面则nginx已成功完成公网访问。
在这里插入图片描述

4. 编译前端项目

①选择一个vue前端项目,在控制台输入npm run build编译前端项目。
在这里插入图片描述
②显示如下结果即编译成功。编译文件放置在dist文件夹中
在这里插入图片描述
在这里插入图片描述

5. 放置前端项目文件

①打开群晖中在第一步创建的html文件夹,将dist中的文件全部复制到html文件夹中
在这里插入图片描述

6. 访问前端项目

①将前端静态文件放置到上述文件夹中后,刷新网页即可完成前端项目部署。
在这里插入图片描述

CAD二次开发-基础流程

1. 创建.NET Framework类库项目

①打开vs,点击创建新项目
在这里插入图片描述
②筛选windows-库,选择类库(.NET Framework)点击下一步
在这里插入图片描述
③填写项目名称,选择项目框架,点击创建。
在这里插入图片描述

2. 引用CAD dll 库文件

①在项目的引用处右键,点击管理NuGet程序包
在这里插入图片描述
②在浏览选项卡搜索CAD2018,选择AutoCAD2018NET库进行安装。(这里以CAD2018举例,其他版本可以引用CAD安装目录下对应的同名dll)
在这里插入图片描述

3. 编写模板代码

模板代码如下:

1
2
3
4
5
[CommandMethod("test")]
public void TestCommand()
{
//在此输入业务逻辑代码
}

以下示例业务代码为创建一条线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//在此输入业务逻辑代码
//创建两条线
Line line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0));
Line line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0));
//打开CAD块表记录
using (IDisposable db = HostApplicationServices.WorkingDatabase, trans = (db as Database).TransactionManager.StartTransaction(), blockTable = (trans as Transaction).GetObject((db as Database).BlockTableId, OpenMode.ForRead), blockTableRecord = (trans as Transaction).GetObject((blockTable as BlockTable)[BlockTableRecord.ModelSpace], OpenMode.ForWrite))
{
//添加实体
(blockTableRecord as BlockTableRecord).AppendEntity(line);
(blockTableRecord as BlockTableRecord).AppendEntity(line2);
(trans as Transaction).AddNewlyCreatedDBObject(line, true);
(trans as Transaction).AddNewlyCreatedDBObject(line2, true);
//提交实体
(trans as Transaction).Commit();
}

4. 生成dll载入CAD进行调用

①在项目右键,点击生成。即可生成dll库文件。
在这里插入图片描述
②打开CAD,新建一张空白CAD图纸。在控制台输入NETLOAD命令按回车进行dll程序加载。
在这里插入图片描述
②在弹出的选择窗口找到我们刚才生成的dll文件,点击打开进行加载。(dll文件在项目文件夹下的bin/Debug文件夹中)
在这里插入图片描述
③在弹出的安全提示窗口点击“始终加载”。
在这里插入图片描述

④在控制台输入“test”命令按回车即可执行我们自定义的程序命令,生成两条线。
在这里插入图片描述
⑤生成效果入下:
在这里插入图片描述

5. 完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System;

namespace CADDEMO
{
public class Class1
{
[CommandMethod("test")]
public void TestCommand()
{
//在此输入业务逻辑代码
//创建两条线
Line line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0));
Line line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0));
//打开CAD块表记录
using (IDisposable db = HostApplicationServices.WorkingDatabase, trans = (db as Database).TransactionManager.StartTransaction(), blockTable = (trans as Transaction).GetObject((db as Database).BlockTableId, OpenMode.ForRead), blockTableRecord = (trans as Transaction).GetObject((blockTable as BlockTable)[BlockTableRecord.ModelSpace], OpenMode.ForWrite))
{
//添加实体
(blockTableRecord as BlockTableRecord).AppendEntity(line);
(blockTableRecord as BlockTableRecord).AppendEntity(line2);
(trans as Transaction).AddNewlyCreatedDBObject(line, true);
(trans as Transaction).AddNewlyCreatedDBObject(line2, true);
//提交实体
(trans as Transaction).Commit();
}
}
}
}

CAD二次开发-在选项卡中创建功能按钮

1. 新建项目

①打开VS2022,点击创建新项目
在这里插入图片描述
②选择类库.NET Framework),点击下一步。
在这里插入图片描述
③输入项目名称后,点击创建。
在这里插入图片描述

2. 添加CAD类库引用

①右键引用,点击管理NuGet程序包
在这里插入图片描述
②在浏览选项卡中,输入CAD2018,找到ModPlus.AutoCAD.API.2018,点击右侧箭头进行安装。
在这里插入图片描述

3. 创建选项卡

在class1类下创建如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
////创建按钮命令,在控制台输入InitMyRibbon,可生成选项卡、面板和按钮
[CommandMethod("InitMyRibbon")]
public void Init()
{
//设置名称参数
string ribbonTabTitle = "DSS-定制插件";
string ribbonPanleTitle = "自定义面板";
string ribbonButtonTitle = "自定义按钮";
//创建选项卡
RibbonControl ribbonControl = ComponentManager.Ribbon;
RibbonTab ribbonTab = ribbonControl.FindTab(ribbonTabTitle);
//如果没有该选项卡则创建新选项卡
if (ribbonTab == null)
{
ribbonTab = new RibbonTab
{
Title = ribbonTabTitle,
Id = ribbonTabTitle
};
ribbonControl.Tabs.Add(ribbonTab);
}
}

CommandMethod是方法的属性标签,用于设置调用该方法的命令。

4. 创建创建面板

在创建选项卡下补充以下代码,用于创建面板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//创建面板
RibbonPanel ribbonPanel;
//如果选项卡中没有我们的面板,则新建面板
if (ribbonTab.Panels == null || ribbonTab.Panels.Where(x => x.Source.Title == ribbonPanleTitle).Count() == 0)
{
RibbonPanelSource ribbonPanelSource = new RibbonPanelSource
{
Title = ribbonPanleTitle
};
ribbonPanel = new RibbonPanel
{
Source = ribbonPanelSource
};
ribbonTab.Panels.Add(ribbonPanel);
}
else
{
ribbonPanel = ribbonTab.Panels.First(x => x.Source.Title == ribbonPanleTitle);
}

5. 创建按钮

在创建面板代码下补充以下代码,用于创建按钮

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//创建按钮
//如果面板中没有我们的按钮,则创建新按钮
if (ribbonPanel.Source == null || ribbonPanel.Source.Items == null || ribbonPanel.Source.Items.Where(x => x.Name == ribbonButtonTitle).Count()== 0)
{
RibbonButton ribbonButton = new RibbonButton
{
Orientation = System.Windows.Controls.Orientation.Vertical,
AllowInStatusBar = true,
Size = RibbonItemSize.Large,
Name = ribbonButtonTitle,
ShowText = true,
Text = ribbonButtonTitle,
Description = ribbonButtonTitle,
LargeImage = new BitmapImage(new Uri("pack://application:,,,/CADButtonDemo;component/image/按钮图标.png", UriKind.Absolute)),
//绑定命令
CommandHandler = new ButtonCommandHandler(),
CommandParameter ="test\n"
};
ribbonPanel.Source.Items.Add(ribbonButton);
}

上面LargeImage = new BitmapImage(new Uri("pack://application:,,,/CADButtonDemo;component/image/按钮图标.png", UriKind.Absolute)),为设置按钮图片。CADButtonDemo为项目名称,按钮图标.png为放置在项目image文件夹中的图片。其他照抄。也可以参考 “Revit二次开发-创建IURibbon中使用相对地址” 这篇文章的方法进行图片设置。
按钮图片属性设置如下:
在这里插入图片描述

6. 按钮绑定命令

创建按钮时,需要绑定按钮命令。代码为:

1
2
CommandHandler = new ButtonCommandHandler(),
CommandParameter ="test\n"

其中,ButtonCommandHandler为自定义类,继承ICommand接口,用于处理命令。
ButtonCommandHandler类的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ButtonCommandHandler : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}

public event EventHandler CanExecuteChanged;

public void Execute(object parameter)
{
//is from Ribbon Button
RibbonButton ribBtn = parameter as RibbonButton;
if (ribBtn != null)
{
//execute the command
Autodesk.AutoCAD.ApplicationServices.Application
.DocumentManager.MdiActiveDocument
.SendStringToExecute(
(string)ribBtn.CommandParameter, true, false, true);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
```c
[CommandMethod("test")]
public void TestCommand()
{
//在此输入业务逻辑代码
//创建两条线
Line line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0));
Line line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0));
//打开CAD块表记录
using (IDisposable db = HostApplicationServices.WorkingDatabase, trans = (db as Database).TransactionManager.StartTransaction()
blockTable = (trans as Transaction).GetObject((db as Database).BlockTableId, OpenMode.ForRead), blockTableRecord = (trans as Transaction).GetObject((blockTable as BlockTable)[BlockTableRecord.ModelSpace], OpenMode.ForWrite))
{
//添加实体
(blockTableRecord as BlockTableRecord).AppendEntity(line);
(blockTableRecord as BlockTableRecord).AppendEntity(line2);
(trans as Transaction).AddNewlyCreatedDBObject(line, true);
(trans as Transaction).AddNewlyCreatedDBObject(line2, true);
//提交实体
(trans as Transaction).Commit();
}
}

7. 开启CAD加载自定义面板

以上代码完成后,使用netload加载CADButtonDemo.dll文件后,在命令行输入,InitMyRibbon即可在选项卡生成自定义的按钮。
如需要开启CAD自动加载自定义选项卡需要做如下操作:
①创建配置文件,在项目中添加名为PackageContents.xml的文件
在这里插入图片描述
在文件中添加如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8" ?>
<ApplicationPackage SchemaVersion="1.0"
AutodeskProduct="插件名称"
ProductType="Application"
Name="插件名称">
<RuntimeRequirements SeriesMin="15.0"
SeriesMax="25.0"
Platform="AutoCAD|AutoCAD*"
OS="Win32|Win64" />
<Components>
<ComponentEntry AppName="插件名称"
Version="1.0"
ModuleName="./CADButtonDemo.dll"
AppDescription="DigitalStruct Studio定制化CAD插件(插件描述)"
LoadOnCommandInvocation="True" LoadOnRequest="True">
<Commands>
<Command Local="InitMyRibbon" Global="InitMyRibbon" StartupCommand="True" />
</Commands>
</ComponentEntry>
</Components>
</ApplicationPackage>

其中插件名称可以修改为自己的插件名称,ModuleName="./CADButtonDemo.dll"为需要加载的dll名称。Local="InitMyRibbon" Global="InitMyRibbon"为创建按钮命令。同[CommandMethod("InitMyRibbon")]
②创建插件文件夹,放入指定文件。
在这里插入图片描述
打开C:\Program Files\Autodesk\ApplicationPlugins文件夹。创建xxx.bundle文件。将生成的dll文件和上述的配置文件放入。
后。打开CAD即可完成按钮创建。效果如下:
在这里插入图片描述

8. 完整代码

class1文件代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
using Autodesk.AutoCAD.Runtime;
using System;
using System.Linq;
using Autodesk.Windows;
using System.Windows.Media.Imaging;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using System.Windows.Input;

namespace CADButtonDemo
{
public class Class1
{
//创建按钮命令,在控制台输入InitMyRibbon,可生成选项卡、面板和按钮
[CommandMethod("InitMyRibbon")]
public void Init()
{
//设置名称参数
string ribbonTabTitle = "DSS-定制插件";
string ribbonPanleTitle = "自定义面板";
string ribbonButtonTitle = "自定义按钮";
//创建选项卡
RibbonControl ribbonControl = ComponentManager.Ribbon;
RibbonTab ribbonTab = ribbonControl.FindTab(ribbonTabTitle);
//如果没有该选项卡则创建新选项卡
if (ribbonTab == null)
{
ribbonTab = new RibbonTab
{
Title = ribbonTabTitle,
Id = ribbonTabTitle
};
ribbonControl.Tabs.Add(ribbonTab);
}
//创建面板
RibbonPanel ribbonPanel;
//如果选项卡中没有我们的面板,则新建面板
if (ribbonTab.Panels == null || ribbonTab.Panels.Where(x => x.Source.Title == ribbonPanleTitle).Count() == 0)
{
RibbonPanelSource ribbonPanelSource = new RibbonPanelSource
{
Title = ribbonPanleTitle
};
ribbonPanel = new RibbonPanel
{
Source = ribbonPanelSource
};
ribbonTab.Panels.Add(ribbonPanel);
}
else
{
ribbonPanel = ribbonTab.Panels.First(x => x.Source.Title == ribbonPanleTitle);
}
//创建按钮
//如果面板中没有我们的按钮,则创建新按钮
if (ribbonPanel.Source == null || ribbonPanel.Source.Items == null || ribbonPanel.Source.Items.Where(x => x.Name == ribbonButtonTitle).Count()== 0)
{
RibbonButton ribbonButton = new RibbonButton
{
Orientation = System.Windows.Controls.Orientation.Vertical,
AllowInStatusBar = true,
Size = RibbonItemSize.Large,
Name = ribbonButtonTitle,
ShowText = true,
Text = ribbonButtonTitle,
Description = ribbonButtonTitle,
LargeImage = new BitmapImage(new Uri("pack://application:,,,/CADButtonDemo;component/image/按钮图标.png", UriKind.Absolute)),
//绑定命令
CommandHandler = new ButtonCommandHandler(),
CommandParameter ="test\n"
};
ribbonPanel.Source.Items.Add(ribbonButton);
}
}
[CommandMethod("test")]
public void TestCommand()
{
//在此输入业务逻辑代码
//创建两条线
Line line = new Line(new Point3d(0, 0, 0), new Point3d(1, 1, 0));
Line line2 = new Line(new Point3d(0, 0, 0), new Point3d(-1, 1, 0));
//打开CAD块表记录
using (IDisposable db = HostApplicationServices.WorkingDatabase, trans = (db as Database).TransactionManager.StartTransaction(), blockTable = (trans as Transaction).GetObject((db as Database).BlockTableId, OpenMode.ForRead), blockTableRecord = (trans as Transaction).GetObject((blockTable as BlockTable)[BlockTableRecord.ModelSpace], OpenMode.ForWrite))
{
//添加实体
(blockTableRecord as BlockTableRecord).AppendEntity(line);
(blockTableRecord as BlockTableRecord).AppendEntity(line2);
(trans as Transaction).AddNewlyCreatedDBObject(line, true);
(trans as Transaction).AddNewlyCreatedDBObject(line2, true);
//提交实体
(trans as Transaction).Commit();
}
}
}

public class ButtonCommandHandler : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}

public event EventHandler CanExecuteChanged;

public void Execute(object parameter)
{
//is from Ribbon Button
RibbonButton ribBtn = parameter as RibbonButton;
if (ribBtn != null)
{
//execute the command
Autodesk.AutoCAD.ApplicationServices.Application
.DocumentManager.MdiActiveDocument
.SendStringToExecute(
(string)ribBtn.CommandParameter, true, false, true);
}
}
}
}

完整项目文件如下:
在这里插入图片描述

WixSharp打包教程

一、安装WixSharp项目模板

点击VisualStudio-扩展-管理扩展,搜索“WixSaharp”,安装“WixSharp Project Templates”。如下图所示:

image

二、添加WixSharp打包项目

1、右键解决方案,点击添加-新建项目

image

2、在弹出的添加新项目模板选择里面,搜索“wix”,找到“WixSharp Managed Setup”项目模板,点击下一步。

image

3、点击右下角“创建”创建项目

image

4、点击“Program.cs”进入打包程序文件。

image

5、解决方案构成如下:

image

三、安装包配置

1、安装路径及打包配置

1.1、单文件打包

1
2
//打包单个EXE文件
var project = new ManagedProject("软件名称",new Dir($@"%ProgramFiles%\制造商名称\软件名称",new File(@"..\WpfApp1\bin\Debug\WpfApp1.exe")));

ManagedProject后面填写软件的名称,Dir后面填写安装路径,File后面填写需要打包的文件。其中“..\”的意思为返回上一级目录。

1.2、多文件打包

1
2
//打包Debug目录下的所有文件
var project = new ManagedProject("软件名称",new Dir($@"%ProgramFiles%\制造商名称\软件名称",new Files(@"..\WpfApp1\bin\Debug\*.*")));

打包多个文件,由File变为Files,字符串格式以”*.*“结尾,意为匹配所有文件。

1.3、创建桌面图标

1
2
//打包多个文件并创建桌面快捷方式
var project = new ManagedProject("软件名称", new Dir($@"%ProgramFiles%\制造商名称\软件名称", new Files(@"..\WpfApp1\bin\Debug\*.*", f => !f.EndsWith("WpfApp1.exe")), new File(@"..\WpfApp1\bin\Debug\WpfApp1.exe", new FileShortcut("软件名称", @"%Desktop%"))));

先创建Files实例,将除了需要创建桌面快捷方式的文件进行打包,再创建单个File实例,打包EXE文件,并创建快捷方式。其中”f => !f.EndsWith(“WpfApp1.exe”)“是用于过滤出指定EXE文件。

File中传入”new FileShortcut(“软件名称”, @”%Desktop%”)“进行桌面快捷方式创建,”@”%Desktop%”“为相对的桌面地址。

1.4、根据不同功能安装不同文件到不同位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 //依据功能选项安装不同文件
//创建不同功能选项
var revit2018 = new Feature("Revit2018", "Revit2018的功能描述") { IsEnabled = false };
var revit2019 = new Feature("Revit2019", "Revit2019的功能描述") { IsEnabled = false };
var revit2020 = new Feature("Revit2020", "Revit2020的功能描述") { IsEnabled = false };
//安装路径及文件配置
var project = new ManagedProject("软件名称", new WixEntity[]
{
new Dir(@"C:\ProgramData\Autodesk\Revit\Addins\2018", new WixEntity[] { new Files(revit2018, @"..\WpfApp1\bin\Debug\*.*"), new File(revit2018, @"..\WpfApp1\Ribbon2018.addin") }),
new Dir(@"C:\ProgramData\Autodesk\Revit\Addins\2019", new WixEntity[] { new Files(revit2019, @"..\WpfApp1\bin\Debug\*.*"), new File(revit2019, @"..\WpfApp1\Ribbon2019.addin") }),
new Dir(@"C:\Users\Administrator\Desktop\新建文件夹", new WixEntity[] { new Files(revit2020, @"..\WpfApp1\bin\Debug\*.*"), new File(revit2020, @"..\WpfApp1\Ribbon2020.addin") })
});
//设置默认功能
project.DefaultFeature = revit2018;

以上代码设置了三个功能选项,分别为”Revit2018”,”Revit2019”,”Revit2020“,IsEnabled属性设置为false后,在功能选项下,其默认为不勾选状态。如下图所示:

Dir后面的”2018“参数指的是当勾选,Revit2018功能后,软件会安装在”C:\ProgramData\Autodesk\Revit\Addins\2018“目录下。Files及File后面的”revit2018“是表示该文件归属revit2018功能。

project.DefaultFeature = revit2018,必须设置,如不进行默认功能设置,在功能选项卡则会生成Complete选项。

image

2、安装包名称设置

1
2
//MSI包名称
roject.OutFileName = "软件名称" + $"{DateTime.Now.Year}{DateTime.Now.Month:00}{DateTime.Now.Day:00}{DateTime.Now.Hour:00}{DateTime.Now.Minute:00}";

生成的包名效果如下:

image

3、安装包输出路径设置

1
2
//MSI文件输出文件夹
project.OutDir = @"..\";

表示生成在Pregram.cs文件的上级目录即”WixSharpSample“文件夹下

image

4、安装包UI元素设置

1
2
3
4
//设置自定义图片
project.ValidateBackgroundImage = false;//关闭背景图片大小验证
project.BackgroundImage = "BackgroundImage-CAD.png";//建议宽度156,高度312
project.BannerImage = "BannerImage-CAD.png";

关闭背景图片大小验证后,当设置非156*312的图片时,会自动调整布局。否则非156*312图片设置会报错。

BackgroundImage对应下图:

image

BannerImage对应下图:

image

5、控制面板信息设置

1
2
3
4
//设置控制面板制造商名称
project.ControlPanelInfo.Manufacturer = "DigitalStruct Studio";
//设置控制面板图标
project.ControlPanelInfo.ProductIcon = "ShellIcon-CAD.ico";

控制面板信息会在软件安装后在控制面板显示。效果如下:

image

6、设置可覆盖安装

1
2
3
4
5
6
7
8
9
//安装新版本时自动卸载旧版本
project.UpgradeCode = new Guid("511EED44-E344-4821-BF25-B42175CE41AC");
project.MajorUpgrade = new MajorUpgrade
{
AllowSameVersionUpgrades = true,
DowngradeErrorMessage = "当前安装的版本低于已安装的版,无法再次安装。",
AllowDowngrades = false,
chedule = UpgradeSchedule.afterInstallValidate
};

通过设置以上代码实现软件安装时自动覆盖安装,无需卸载。

7、设置安装包语言

1
2
3
//加载和设置中文配置文件
project.Language = "zh-CN";
project.LocalizationFile = "WixUI_zh-CN.wxl";

需要手动载入”WixUI_zh-CN.wxl”中文配置文件,不然配置project.Language = “zh-CN”不会生效。

8、设置用户协议

1
2
//设置用户协议文件
project.LicenceFile = "用户协议-CAD.rtf";

用户协议必须为.rtf文件格式。

9、对话框配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//设置对话框
project.ManagedUI = new ManagedUI();
project.ManagedUI.Icon = "ShellIcon-CAD.ico";
project.ManagedUI.InstallDialogs.Add(Dialogs.Welcome)
.Add(Dialogs.Licence)
.Add(Dialogs.SetupType)
.Add(Dialogs.Features)
.Add(Dialogs.InstallDir)
.Add(Dialogs.Progress)
.Add(Dialogs.Exit);

project.ManagedUI.ModifyDialogs.Add(Dialogs.MaintenanceType)
.Add(Dialogs.Features)
.Add(Dialogs.Progress)
.Add(Dialogs.Exit);

project.ManagedUI.Icon = “ShellIcon-CAD.ico”为设置左上角图标。

image

9.1安装对话框

Welcome对话框

image

Licence对话框

image

SetupType对话框

image

当选择典型时,会根据project.DefaultFeature = revit2018设置的默认功能进行安装。自定义则进行自定义安装。完整则全部安装。

Features对话框

image

InstallDir对话框

image

Progress对话框

image

Exit对话框

image

9.2卸载对话框

MaintenanceType对话框

image

Features对话框

image

Progress对话框

image

Exit对话框

10、完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
using System;
using System.Windows.Forms;
using WixSharp;
using WixSharp.Forms;

namespace WixSharp_Setup1
{
internal class Program
{
static void Main()
{
//打包单个EXE文件
//var project = new ManagedProject("软件名称",
// new Dir($@"%ProgramFiles%\制造商名称\软件名称",
// new File(@"..\WpfApp1\bin\Debug\WpfApp1.exe")));


//打包Debug目录下的所有文件
//var project = new ManagedProject("软件名称",
// new Dir($@"%ProgramFiles%\制造商名称\软件名称",
// new Files(@"..\WpfApp1\bin\Debug\*.*")));


//打包多个文件并创建桌面快捷方式
//var project = new ManagedProject("软件名称",
// new Dir($@"%ProgramFiles%\制造商名称\软件名称",
// new Files(@"..\WpfApp1\bin\Debug\*.*", f => !f.EndsWith("WpfApp1.exe")),
// new File(@"..\WpfApp1\bin\Debug\WpfApp1.exe", new FileShortcut("软件名称", @"%Desktop%"))));


////依据功能选项安装不同文件
////创建不同功能选项
//var revit2018 = new Feature("Revit2018", "Revit2018的功能描述") { IsEnabled = false };
//var revit2019 = new Feature("Revit2019", "Revit2019的功能描述") { IsEnabled = false };
//var revit2020 = new Feature("Revit2020", "Revit2020的功能描述") { IsEnabled = false };
////安装路径及文件配置
//var project = new ManagedProject("软件名称",
// new Dir(@"C:\ProgramData\Autodesk\Revit\Addins",
// new Dir("2018", new WixEntity[] { new Files(revit2018, @"..\WpfApp1\bin\Debug\*.*"), new File(revit2018, @"..\WpfApp1\Ribbon2018.addin") }),
// new Dir("2019", new WixEntity[] { new Files(revit2019, @"..\WpfApp1\bin\Debug\*.*"), new File(revit2019, @"..\WpfApp1\Ribbon2019.addin") }),
// new Dir("2020", new WixEntity[] { new Files(revit2020, @"..\WpfApp1\bin\Debug\*.*"), new File(revit2020, @"..\WpfApp1\Ribbon2020.addin") })));
////设置默认功能
//project.DefaultFeature = revit2018;

//依据功能选项安装不同文件
//创建不同功能选项
var revit2018 = new Feature("Revit2018", "Revit2018的功能描述") { IsEnabled = false };
var revit2019 = new Feature("Revit2019", "Revit2019的功能描述") { IsEnabled = false };
var revit2020 = new Feature("Revit2020", "Revit2020的功能描述") { IsEnabled = false };
//安装路径及文件配置
var project = new ManagedProject("软件名称", new WixEntity[]
{
new Dir(@"C:\ProgramData\Autodesk\Revit\Addins\2018", new WixEntity[] { new Files(revit2018, @"..\WpfApp1\bin\Debug\*.*"), new File(revit2018, @"..\WpfApp1\Ribbon2018.addin") }),
new Dir(@"C:\ProgramData\Autodesk\Revit\Addins\2019", new WixEntity[] { new Files(revit2019, @"..\WpfApp1\bin\Debug\*.*"), new File(revit2019, @"..\WpfApp1\Ribbon2019.addin") }),
new Dir(@"C:\Users\Administrator\Desktop\新建文件夹", new WixEntity[] { new Files(revit2020, @"..\WpfApp1\bin\Debug\*.*"), new File(revit2020, @"..\WpfApp1\Ribbon2020.addin") })
});
//设置默认功能
project.DefaultFeature = revit2018;

//MSI包名称
project.OutFileName = "软件名称" + $"{DateTime.Now.Year}{DateTime.Now.Month:00}{DateTime.Now.Day:00}{DateTime.Now.Hour:00}{DateTime.Now.Minute:00}";

//MSI文件输出文件夹
project.OutDir = @"..\";

//设置自定义图片
project.ValidateBackgroundImage = false;//关闭背景图片大小验证
project.BackgroundImage = "BackgroundImage-CAD.png";//建议宽度156,高度312
project.BannerImage = "BannerImage-CAD.png";

//设置控制面板制造商名称
project.ControlPanelInfo.Manufacturer = "DigitalStruct Studio";
//设置控制面板图标
project.ControlPanelInfo.ProductIcon = "ShellIcon-CAD.ico";

//安装新版本时自动卸载旧版本
project.UpgradeCode = new Guid("511EED44-E344-4821-BF25-B42175CE41AC");
project.MajorUpgrade = new MajorUpgrade
{
AllowSameVersionUpgrades = true,
DowngradeErrorMessage = "当前安装的版本低于已安装的版,无法再次安装。",
AllowDowngrades = false,
Schedule = UpgradeSchedule.afterInstallValidate
};

//加载和设置中文配置文件
project.Language = "zh-CN";
project.LocalizationFile = "WixUI_zh-CN.wxl";

//设置用户协议文件
project.LicenceFile = "用户协议-CAD.rtf";

//设置对话框
project.ManagedUI = new ManagedUI();
project.ManagedUI.Icon = "ShellIcon-CAD.ico";
project.ManagedUI.InstallDialogs.Add(Dialogs.Welcome)
.Add(Dialogs.Licence)
.Add(Dialogs.SetupType)
.Add(Dialogs.Features)
.Add(Dialogs.InstallDir)
.Add(Dialogs.Progress)
.Add(Dialogs.Exit);

project.ManagedUI.ModifyDialogs.Add(Dialogs.MaintenanceType)
.Add(Dialogs.Features)
.Add(Dialogs.Progress)
.Add(Dialogs.Exit);

project.Load += Msi_Load;
project.BeforeInstall += Msi_BeforeInstall;
project.AfterInstall += Msi_AfterInstall;

project.BuildMsi();
}

static void Msi_Load(SetupEventArgs e)
{
if (!e.IsUISupressed && !e.IsUninstalling)
MessageBox.Show(e.ToString(), "Load");
}

static void Msi_BeforeInstall(SetupEventArgs e)
{
if (!e.IsUISupressed && !e.IsUninstalling)
MessageBox.Show(e.ToString(), "BeforeInstall");
}

static void Msi_AfterInstall(SetupEventArgs e)
{
if (!e.IsUISupressed && !e.IsUninstalling)
MessageBox.Show(e.ToString(), "AfterExecute");
}
}
}

Revit二次开发-使用DataStorage进行数据存储

1. 简要介绍

通常情况下,在Revit中存储数据一般会使用项目参数等可见的参数进行数据存储与使用,这些参数是可以被用户查和修改的,但在某些情况一些程序敏感数据不希望用户查看或修改时,可以使用Revit的DataStorage类进行数据的创建,写入和读取。

DataStorage的数据存储主要分为字段创建、数据写入、数据读取。

  • 字段创建:可出创建三种字段类型,分别为单字段、列表字段和字典字段。不同类型的字段对应的不同的字段添加方法。创建字典字段时需要定义两个字段字段的值类型。单字段和列表字段则只需要定义一个。字段的GUID不可重复。字段的单位类型需要和存入的数据相匹配。如存入数字,需要设置字段单位类型UnitType.UT_Length或其他。
  • 数据写入:数据写入时需要匹配写入数据的单位。如本案例中写入的是1200.2单位为mm。无单位时候需要设置DisplayUnitType.DUT_UNDEFINED
  • 数据读取:数据读取同理,需要设置读取的单位。如本案例读取的单位为m,读取的值则为1.2002。无单位时则需设置DisplayUnitType.DUT_UNDEFINED

2. 实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.ExtensibleStorage;
using Autodesk.Revit.UI;
using System;
using System.Collections.Generic;
using System.Linq;

namespace dataStorage
{
[Transaction(TransactionMode.Manual)]
public class Class1 : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
Document doc = commandData.Application.ActiveUIDocument.Document;
Transaction transaction = new Transaction(doc);
//创建文档数据库(可替换成构件)
List<Element> eles = new FilteredElementCollector(doc).OfClass(typeof(DataStorage)).Where(x => x.Name == "文档数据库").ToList();
Element dataStorage;
if (eles.Count != 0)
{
dataStorage = eles.First();
}
else
{
transaction.Start("创建文档数据库");
dataStorage = DataStorage.Create(doc);
dataStorage.Name = "文档数据库";
transaction.Commit();
}


//创建字段
bool isSimpleBuild = false;
bool isArrayBuild = false;
bool isMapBuild = false;
transaction.Start("创建文档参数");
//单字段
isSimpleBuild = BuildField(dataStorage, new Guid("34B6239D-42FF-7076-D211-BEC5E64CD172"), "文档单属性", AccessLevel.Public, AccessLevel.Public, false, typeof(double), UnitType.UT_Length);
//列表字段
isArrayBuild = BuildField(dataStorage, new Guid("BBF63B09-907B-E7C6-A598-8EC9B4D28A88"), "文档列表属性", AccessLevel.Public, AccessLevel.Public, true, typeof(double), UnitType.UT_Length);
//字段字段
isMapBuild = BuildField(dataStorage, new Guid("9F2398BC-B703-8362-6137-27435FE49AB4"), "文档字典属性", AccessLevel.Public, AccessLevel.Public, typeof(string), typeof(double), UnitType.UT_Length);
transaction.Commit();


//填写数据
transaction.Start("填写文档参数");
bool isSimpleSet = false;
bool isArraySet = false;
bool isMapSet = false;
//单字段
isSimpleSet = SetField(dataStorage, "文档单属性", 1200.2, DisplayUnitType.DUT_MILLIMETERS);
//列表字段
IList<double> listString = new List<double>() { 1200.2, 1500.5 };
isArraySet = SetField(dataStorage, "文档列表属性", listString, DisplayUnitType.DUT_MILLIMETERS);
//字典字段
IDictionary<string, double> myDictionary = new Dictionary<string, double>();
myDictionary.Add("1", 1200.2);
isMapSet = SetField(dataStorage, "文档字典属性", myDictionary, DisplayUnitType.DUT_MILLIMETERS);
transaction.Commit();


//读取数据
double simpleValue = GetField<double>(dataStorage, "文档单属性", DisplayUnitType.DUT_METERS);
IList<double> arrayValue = GetField<IList<double>>(dataStorage, "文档列表属性", DisplayUnitType.DUT_METERS);
IDictionary<string, double> mapValue = GetField<IDictionary<string, double>>(dataStorage, "文档字典属性", DisplayUnitType.DUT_METERS);


//输出结果
TaskDialog.Show("Revit", $"单属性创建:{isSimpleBuild}\n列表属性创建:{isArrayBuild}\n字典属性创建:{isMapBuild}\n" +
$"单属性填写:{isSimpleSet}\n列表属性填写:{isArraySet}\n字典属性填写:{isMapSet}\n"
+ $"单属性值:{simpleValue}\n列表属性值:{arrayValue.First()}\n字典属性值:{mapValue.First()}");
return Result.Succeeded;
}


/// <summary>
/// 创建字段
/// </summary>
/// <param name="dataStorage">存储元素,可以是项目文档,也可以是构件</param>
/// <param name="fieldGuid">字段GUID</param>
/// <param name="fieldName">字段名称</param>
/// <param name="readAccessLevel">字段读取权限</param>
/// <param name="writeAccessLevel">字段写入权限</param>
/// <param name="isArrayType">字段是否为列表</param>
/// <param name="keyType">字典关键字类型</param>
/// <param name="fieldType">字段类型</param>
/// <param name="unitType">字段单位类型</param>
/// <returns>如果字段创建成功则返回True,否则返回false</returns>
bool BuildField(Element dataStorage, Guid fieldGuid, string fieldName, AccessLevel readAccessLevel, AccessLevel writeAccessLevel, bool isArrayType, Type keyType, Type fieldType, UnitType unitType)
{
//文档属性是否创建成功
bool isBuildSucceed = false;
//获取指定属性
Schema specifiedSchema = Schema.Lookup(fieldGuid);
if (specifiedSchema == null)
{
bool hasSameName = false;
foreach (Schema schema in Schema.ListSchemas())
{
if (schema.SchemaName == fieldName)
{
hasSameName = true;
break;
}
}
if (!hasSameName)
{
SchemaBuilder schemaBuilder = new SchemaBuilder(fieldGuid);
schemaBuilder.SetSchemaName(fieldName);
schemaBuilder.SetReadAccessLevel(readAccessLevel);
schemaBuilder.SetWriteAccessLevel(writeAccessLevel);
FieldBuilder fieldBuilder;
if (keyType == null)
{
if (isArrayType)
{
fieldBuilder = schemaBuilder.AddArrayField(fieldName, fieldType);
}
else
{
fieldBuilder = schemaBuilder.AddSimpleField(fieldName, fieldType);
}
}
else
{
fieldBuilder = schemaBuilder.AddMapField(fieldName, keyType, fieldType);
}
if (unitType != UnitType.UT_Undefined)
{
fieldBuilder.SetUnitType(unitType);
}
schemaBuilder.Finish();
isBuildSucceed =true;
}
else
{
throw new ArgumentException($"“{fieldName}”参数名称已存在无法创建,请修改参数名称后继续", "fieldGuid");
}
}
else
{
if (specifiedSchema.SchemaName == fieldName)
{
Entity entity = dataStorage.GetEntity(specifiedSchema);
if (entity.Schema == null)
{
entity = new Entity(specifiedSchema);
dataStorage.SetEntity(entity);
isBuildSucceed = true;
}
else
{
if (entity.Schema.SchemaName == specifiedSchema.SchemaName)
{
dataStorage.SetEntity(entity);
isBuildSucceed = true;
}
else
{
throw new ArgumentException("参数创建失败,请试试更换参数名称或GUID后继续");
}
}
}
else
{
throw new ArgumentException($"GUID“{fieldGuid.ToString()}”对应的参数名称为{specifiedSchema.SchemaName},请修改参数名称后继续", fieldName);
}
}
return isBuildSucceed;
}
/// <summary>
/// 创建单字段或列表字段
/// </summary>
/// <param name="dataStorage">存储元素,可以是项目文档,也可以是构件</param>
/// <param name="fieldGuid">字段GUID</param>
/// <param name="fieldName">字段名称</param>
/// <param name="readAccessLevel">字段读取权限</param>
/// <param name="writeAccessLevel">字段写入权限</param>
/// <param name="isArrayType">字段是否为列表字段</param>
/// <param name="fieldType">字段类型</param>
/// <param name="unitType">字段单位类型</param>
/// <returns>如果字段创建成功则返回True,否则返回false</returns>
/// <inheritdoc cref="ExtensibleStorage" path="remarks|example" />
bool BuildField(Element dataStorage, Guid fieldGuid, string fieldName, AccessLevel readAccessLevel, AccessLevel writeAccessLevel, bool isArrayType, Type fieldType, UnitType unitType) => BuildField(dataStorage, fieldGuid, fieldName, readAccessLevel, writeAccessLevel, isArrayType, null, fieldType, unitType);
/// <summary>
/// 创建字典字段
/// </summary>
/// <param name="dataStorage">存储元素,可以是项目文档,也可以是构件</param>
/// <param name="fieldGuid">字段GUID</param>
/// <param name="fieldName">字段名称</param>
/// <param name="readAccessLevel">字段读取权限</param>
/// <param name="writeAccessLevel">字段写入权限</param>
/// <param name="keyType">字典关键字类型</param>
/// <param name="fieldType">字典数值类型</param>
/// <param name="unitType">字典数值单位</param>
/// <returns>如果字段创建成功则返回True,否则返回false</returns>
/// <inheritdoc cref="ExtensibleStorage" path="remarks|example" />
bool BuildField(Element dataStorage, Guid fieldGuid, string fieldName, AccessLevel readAccessLevel, AccessLevel writeAccessLevel, Type keyType, Type fieldType, UnitType unitType) => BuildField(dataStorage, fieldGuid, fieldName, readAccessLevel, writeAccessLevel, false, keyType, fieldType, unitType);
/// <summary>
/// 在指定字段存储数据
/// </summary>
/// <typeparam name="T">存储数据类型</typeparam>
/// <param name="dataStorage">存储元素,可以是项目文档,也可以是构件</param>
/// <param name="fieldName">字段名称</param>
/// <param name="fieldValue">存储数据值</param>
/// <param name="displayUnitType">存储数据单位</param>
/// <returns>如果数据储存成功则返回True,否则返回False</returns>
/// <inheritdoc cref="ExtensibleStorage" path="remarks|example" />
bool SetField<T>(Element dataStorage, string fieldName, T fieldValue, DisplayUnitType displayUnitType)
{
bool isSetSucceed = false;
//获取指定属性
Schema specifiedSchema = null;
foreach (Schema schema in Schema.ListSchemas())
{
if (schema.SchemaName == fieldName)
{
specifiedSchema = schema;
break;
}
}
if (specifiedSchema != null)
{
Field field = specifiedSchema.GetField(fieldName);
if (field != null)
{
Entity entity = dataStorage.GetEntity(specifiedSchema);
if (entity.Schema == null || entity.Schema.GUID != specifiedSchema.GUID)
{
entity = new Entity(specifiedSchema);
}
if (displayUnitType == DisplayUnitType.DUT_UNDEFINED)
{
entity.Set(field, fieldValue);
}
else
{
entity.Set(field, fieldValue, displayUnitType);
}
dataStorage.SetEntity(entity);
isSetSucceed = true;
}
}
return isSetSucceed;
}
/// <summary>
/// 从指定字段读取数据
/// </summary>
/// <typeparam name="T">读取的数据类型</typeparam>
/// <param name="dataStorage">存储元素,可以是项目文档,也可以是构件</param>
/// <param name="fieldName">字段名称</param>
/// <param name="displayUnitType">读取的数据单位</param>
/// <returns>如果数据读取成功则返回True,否则返回False</returns>
/// <inheritdoc cref="ExtensibleStorage" path="remarks|example" />
T GetField<T>(Element dataStorage, string fieldName, DisplayUnitType displayUnitType)
{
T value = default(T);
//获取指定属性
Schema specifiedSchema = null;
foreach (Schema schema in Schema.ListSchemas())
{
if (schema.SchemaName == fieldName)
{
specifiedSchema = schema;
break;
}
}
if (specifiedSchema != null)
{
Field field = specifiedSchema.GetField(fieldName);
if (field != null)
{
Entity entity = dataStorage.GetEntity(specifiedSchema);
if (entity.Schema != null && entity.Schema.GUID == specifiedSchema.GUID)
{
if (displayUnitType == DisplayUnitType.DUT_UNDEFINED)
{
value = entity.Get<T>(field);
}
else
{
value = entity.Get<T>(field, displayUnitType);
}
}
else
{
value = default(T);
}
}
}
return value;
}
}
}

3. 实现效果

在这里插入图片描述

4. 数据查看

可使用RevitLookup插件查看数据存储情况。
在这里插入图片描述
在这里插入图片描述

Revit二次开发-基础流程

1 开发前准备

1.1 安装Revit AddIn-Manager

Revit AddIn-Manager是由Autodesk官方提供的一个插件加载及调用工具,利用该工具,用户可以通过即插即用的方式加载自己开发的插件工具。Revit AddIn-Manager是我们开发过程中常用的客户端调试工具。

安装步骤:

第一步:点击Revit SKD下载链接 https://pan.baidu.com/s/1TBo0ocyR0cNSkRD1SEbGpA 密码:ew51,下载“REVIT_2018_SDK_1.msi”。

第二步:下载完成后双击“REVIT_2018_SDK_1.msi”点击“Next”

image

第三步:勾选“I accept the terms in the License Agreement”,点击“Next”。

image

第四步:点击“Change…”可选择安装路径,这里按默认。选择好路径后点”Next“进行下一步

image

第五步:点击”Install“进行安装

image

第六步:等待安装完成后点击”Finish“完成安装。

image

第七步:打开看安装文件夹下的”Revit 2018 SDK”,看到如下界面

image

第八步:打开“Add-In Manager”文件夹,复制“AddInManager.dll”及“Autodesk.AddInManager.addin”到”C:\ProgramData\Autodesk\Revit\Addins\2018”目录下

image

image

第九步:打开“Revit 2018”,在“附加模块”选项栏,“外部工具”中即可看到“AddInManager”插件。image

1.2 安装Revit Lookup

Revit Lookup是由Autodesk Developer Network (ADN)技术专家Jeremy Tammik开发并开放源码的Revit数据库查看工具。利用Revit Lookup,开发者可以轻易地查看当前Revit文件中所有对象的大部分属性信息,以及对象间的关系,从而快速定位开发过程中遇到的问题。

安装步骤:

第一步:点击“Revit Lookup”项目的GitHub地址:https://github.com/jeremytammik/RevitLookup

image

第二步:下滑找到“Versions”,点击“2018.0.0.8 for Revit 2018”版本,进入下载页面

image

第三步:点击“Source code(zip)”下载源代码压缩包

image

第四步:解压后得到,如下文件。接着进入“CS”文件夹,找到“RevitLookup.addin”复制到”C:\ProgramData\Autodesk\Revit\Addins\2018”目录下。

image

image

第五步:依次打开“bin“文件夹,打开”Debug”文件夹,找到“RevitLookup.dll”文件,复制到”C:\ProgramData\Autodesk\Revit\Addins\2018”目录下。至此“Revit Lookup”插件安装完成。

image

第六步:打开“Revit 2018”,在“附加模块”选项栏,在最右侧即可看到“Revit Lookup”插件。

image

1.3 使用Revit AddIn-Manager

AddInMananger有三个模式:1)菜单模式;2)无界面模式;3)只读模式;

image

我们主要使用的是第一种模式,第二种类似于重复上一次命令,第三种使用情况较少

打开窗口以后点击“Load”按钮来加载程序文件

image

支持.dll和.exe格式的程序文件,我们加载NaiveMepMini.dll的文件

注意:新建一个单独的文件夹来放置程序文件,否则很可能报错

image

载入程序以后,选中对应的命令,点击“Run”按钮就可以运行了

image

当我们不需要使用插件时,点击“Remove”就可以卸载相关的程序集了

1.4 使用Revit Lookup

RevitLookup是Autodesk平台开发的一款不用写代码就可以直观地看到API对象的插件。

下面我们打开“Revit 2018”,在项目中新建一道墙,通过“Revit LookUp”查看这道墙体的属性来了解”Revit LookUp”的基本用法。

首先我们鼠标选中“墙体”,点击“Revit LookUp”工具,选择“”Snoop Current Selection…“

image

之后我们就可以得到当前“墙体”的API属性表。

image

表中的“属性“和”方法“是和API中的属性和方法是一一对应的。总的来说,Revit LookUp”工具就是用来查看”Revit“中”构件“的API属性值的插件。下面做简要解读。

image

image

image

image

image

image

1.5 Revit API文档的查询与使用

在安装了Revit SDK之后,文件夹中有个“RevitAPI.chm”文件就是Revit API文档。

image

双击打开该文档后得到如下界面:

image

下面简要介绍Revit API文档的使用方法:

image

image

image

image

image

2 HelloWorld入门

2.1 新建项目

Visual Studio编写C#程序的第一步是选择一种项目类型并创建新的类库。

(1)从“文件”菜单选择新建项目…

(2)从已安装的样板框架中,单击Visual C#

(3)在右边框架内,单击“类库(.NET Framework)”。

(4)在“名称“一栏输入”Hello World”作为项目名称,勾选”为解决方案创建目录”。

(5)单击确定

image

2.2 添加引用

  • 如果解决方案资源管理器窗口未打开,则从“视图”菜单中选择解决方案资源管理器。

  • 在解决方案资源管理器中,右键单击引用以显示上下文菜单。

  • 从上下文菜单中,单击添加引用。出现“添加引用”对话框。

  • 在添加引用对话框中,单击浏览选项卡。找到 Revit 安装的文件夹,然后单击RevitAPI.dll。例如,安装文件夹的位置通常是C:\Program Files\Autodesk\Revit 2018\RevitAPI.dll。Revit APIUI.dll的引用也在该文件夹中。

image

  • 勾选导入的.dll文件,单击确定以选择.dll并关闭对话框。RevitAPI会出现在解决方案资源管理器引用树中。

  • 请注意,对于新项目,RevitAPI的“复制本地”属性应始终设置为false。这会节省硬盘空间,并避免Visual Studio 调试器不知道使用哪个DLL副本。右键单击RevitAPI和RevitAPIUI,选择属性,并将**“复制本地”设置从true改为false。**

image image

2.3 编写代码

添加下列代码以创建插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.Attributes;

namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
TaskDialog.Show("Revit""Hello World");
return Result.Succeeded;
}
}
}

提示:(1)Visual Studio 智能感知功能可帮助创建实现接口的方法。在上例中Class1之后,添加“IExternalCommand”,使用**“alt”+“回车”**快捷键,呼出智能选项。再回车选择“实现接口”。

image

(2)每个Revit插件应用程序都必须有一个进入点类来实现IExternalCommand接口,且必须实现Execute()方法。Execute()方法是插件程序的进入点,类似于其他程序中的Main()方法。插件程序进入点类的定义包含在一个程序集内。

2.4 使用插件

(1)调出“输出”窗口。在菜单栏点击视图输出。

(2)代码编写完成后,在菜单栏点击生成生成解决方案,即可在输出窗口看到如下信息:

image

其中,红框部分即为输出的dll文件位置。复制此文件位置,打开Revit软件,新建一个项目。在附加模块→外部工具点击“Add-In Manager”,选择“Load”,将复制的地址粘贴到地址栏,点击打开。

image

选择我们载入的插件“HelloWorld.Class1”,点击Run,运行。即可得到如下结果:

image

image

2.5 调试代码

在调试模式下运行程序可使用断点暂停程序,以便检查变量和对象的状态。如果出现错误,则可以检查程序运行的变量,来推断为什么不是预期的值。

具体代码调试步骤如下:
(1)确保后台有个在运行的Revit项目
(2)点击菜单栏调试附加到进程,在右侧筛选进程里面输入”revit”关键字,点击“Revit.exe”,点击附加

image

此时程序已经进入调试状态。image

(3)鼠标点击红点位置,将该行代码打上断点。打开”Revit“软件,按上面步骤运行插件。之后系统自动调整到断点代码处。如下所示:

image

(4)此时按”F11“让程序逐语句进行代码运行,按”F10逐过程进行代码运行,当断点代码在循环语句中,可以按”Shift+F11跳出循环。

这里我们按F11让程序逐语句进行代码运行。效果如下:

image

此时代码已经台运行到” return Result.Succeeded;“这行代码中。再按”F11“则程序运行结束,自动退出调试状态。

当我们要反复调试程序时候,只需点调试重新附加到进程,Visual Studio 会自动将代码附加到我们上一次附加的进程上。此时我们只需要进行下一步调试即可。

程序调试时,我们可以将我们需要时刻关注的变量下拉(或者复制,或者手动输入)到监视窗口,以观察该变量的变化。image

3 图元基本交互

3.1 过滤

Revit API提供一种机制,用于过滤和迭代Revit 文件中的图元。这是用于获取一组相关图元的最好方式,如文件中所有的墙或门。过滤器也可以用来寻找出一组很具体的图元,如某一特定尺寸的所有的梁。
通过指定过滤器获取图元的基本步骤如下:

(1)新建一个FilteredElementCollector。

(2)对它运用一个或多个过滤器。

(3)获取滤过的图元或图元ID(使用几种方法之一)。

下列代码示例了过滤和迭代文件中图元的基本步骤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//应用过滤器,获得所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).WhereElementIsNotElementType().ToList();
//获得所有墙体的ID
string wallIds = "本项目墙体ID如下:\n"
foreach (Element wall in walls)
{
wallIds = wallIds + wall.Id.ToString() + "\n"
}
TaskDialog.Show("Revit", wallIds);
return Result.Succeeded;
}
}
}

运行效果如下:

image

3.1.1 创建图元过滤集

用于图元的迭代和过滤的主类称作FilteredElementCollector。可以用以下三种方式之一来建立:

(1)文档。搜索并过滤文档中的所有图元。

1
new FilteredElementCollector(Document document)

(2)文档和视图。搜索并过滤指定视图中可见的所有图元。

1
new FilteredElementCollector(Document document, ElementId viewId)

(3)文档和图元ID集。搜索并过滤文档中指定的图元。

1
new FilteredElementCollector(Document document, ICollection<ElementId> elementIds)

3.1.2 应用过滤器

可以用 ElementFilter 对 FilteredElementCollector 使用过滤器。ElementFilter 是一个类,用于检查图元是否满足某种条件。ElementFilter 类有三个派生类,将图元过滤器划分为三类。

image

  • ElementQucikFilter。快速过滤器仅对 EIementRecord 进行操作,是一个低内存占用的类,以一个有限接口来读取图元属性。被快速过滤器丢弃的图元不会展开到内存中。

    image

快速过滤器 过滤结果 快捷方法
BoundingBoxContainsPointFilter 图元的边界框内包含给定点的图元
BoundingBoxIntersectsFilter 图元的边界框与给定轮框相交的图元
BoundingBoxIsInsideFilter 图元边界框包含给定轮廓的图元
ElementCategoryFilter 与给定类别相同的图元 OfCategory OfCategoryId
ElementClassFilter 与给定类相同的图元 OfClass
ElementDesignOptionFilter 符合一个特定设计选项的图元 ContainedInDesignOption
ElementIsCurveDrivenFilter 由线驱动的图元 WhereElementIsCurveDriven
ElementIsElementTypeFilter 是一个“图元类型”的图元 WhereElementIsElementType WhereElementIsNotElementType
ElementMulticategoryFilter 与任何一组给定的类别匹配的图元
ElementMulticlassFilter 与给定一组类(或派生类)匹配的图元
ElementOwnerViewFilter 某个视图专有的图元 OwnedByView WhereElementIsViewIndependent
ElementStructuralTypeFilter 与给定结构类型相匹配的图元
ElementWorksetFilter 在指定工作集中的图元
ExclusionFilter 除指定图元以外的所有图元 Excluding
ExtensibleStorageFilter 基于特定架构id的可扩展存储数据的图元
FamilySymbolFilter 特定族符号的图元
  • ElementSlowFilter。慢速过滤器首先需要获取图元并展开到内存中。因此,更为可取的方法是,将慢速过滤器与至少一个快速过滤器结合使用,尽量减少展开到内存的图元数量,以对照此过滤器设置的标准进行评价。慢速过滤器均无快捷方法。

image

慢速过滤器 过滤结果
RoomFilter 是房间的图元
RoomTagFilter 是房间标记的图元
AreaFilter 是面积的图元
AreaTagFilter 是面积标记的图元
CurveElementFilter 是给定曲线类型的图元
ElementIntersectsFilter 与给定图元几何实体相交的图元
ElementLevelFilter 与给定标高ID相关的图元
ElementParameterFilter 符合一个或多个过滤规则的图元
ElementPhaseStatusFilter 对应给定阶段由给定阶段的图元
FamilyInstanceFilter 给定族实例类型的图元
SpaceFilter 是空间的图元
SpaceTagFilter 是空间标记的图元
PrimaryDesignOptionMemberFilter 属于任何初步设计选项的图元
FamilyStructuralMaterialTypeFilter 具有给定结构材料类型的族图元
StructuralInstanceUsageFilter 与给定结构用法相同的族实例
StructuralMaterialTypeFilter 与给定结构材料类型相同的族实例
StructuralWallUsageFilter 与给定结构墙用法相同的墙
SelectableInViewFilter 在指定视图中所有可选择的图元
  • ElementLogicalFilter。逻辑过滤器逻辑组合两个或更多过滤器。Revit 以使过滤器执行最快为优先评估条件,可能会将合成过滤器重新排序。

image

逻辑过滤器 过滤结果 快捷方法
LogicalAndFilter 通过多组过滤器的图元 WherePass IntersectWith
LogicalOrFilter 多组过滤器中,至少通过一组过滤器的图元 UnionWith

3.1.3 过滤实例

下面用快速过滤,慢速过滤器和逻辑过滤器找出项目中属于标高2的墙体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//创建属于“墙类”的快速过滤器
ElementClassFilter wallClassFilter = new ElementClassFilter(typeof(Wall));
//创建与标高2相关的的慢速过滤器,2607为标高2的ID
ElementLevelFilter elementLevelFilter = new ElementLevelFilter(new ElementId(2607));
//创建逻辑与过滤器,连接两个过滤器
LogicalAndFilter logicalAndFilter = new LogicalAndFilter(wallClassFilter, elementLevelFilter);
//应用逻辑与过滤找属于标高2的墙体
List<Element> walls = collector.WherePasses(logicalAndFilter).ToList();
//获得所有墙体的ID
string wallIds = "本项目标高墙体ID如下:\n"
foreach (Element wall in walls)
{
wallIds = wallIds + wall.Id.ToString() + "\n"
}
TaskDialog.Show("Revit", wallIds);
return Result.Succeeded;
}
}
}

运行结果如下:

image

3.2 参数

3.2.1 参数类

Revit提供了一个通用机制,给每个图元一组可以编辑的参数。在Revit用户界面中,图元属性对话框中的参数是可见的。本节介绍如何利用Revit平台API来获取和使用内置参数。

本节以”墙体“为例子,了解参数的基本内容。首先我们选中一面墙体,点击”Revit LookUp“。

image

即可查看该墙体的一个图元属性。在墙体图元中”Paremeters“属性包含了该墙体所有参数。

image

点击”Parameters“可得到该墙体的所有属性。

image

这里可以看到,参数是属于”Parameter“类,其中有具有值的属性和方法能够通过”Revit LookUp“查看,所有属性和方法则需要自行通过Revit API文档查找。”Parameter“类文档界面如下:

image

3.2.2 Revit单位

在介绍”Parameter”成员之前,有必要先介绍下Revit单位系统中的基本单位。

基本单位 Revit 单位 单位系统
长度 英尺(ft) 英制
角度 弧度 公制
质量 千克(kg) 公制
时间 秒(s) 公制
电流 安培(A) 公制
温度 开氏度(K) 公制
照度 坎德拉(cd) 公制

如何理解此表格,以墙体的”无连接高度“参数为例。

image image

在上图,我们可以发现,在”无连接高度“参数的方法中,AsDouble() 是将”无连接高度“的参数值转化为Double值,AsValueString() 是将”无连接高度“的参数值转化为字符串值。但为何double值为26.2467而不是8000?原因在于此double值的单位为英尺,而AsValueString()的单位为项目单位。下图可以看到本项目的长度单位为mm。

image

简而言之,在程序中获得的长度单位为英尺,角度为弧度。英尺转化为毫米一般乘以304.8来转换。

3.2.3 参数类成员

名称 描述
方法 AsDouble 若参数的值是数值类型的参数,则返回该数值,单位为Revit单位,否则返回默认值“0”。例:无连接高度
AsElementId 若参数的值是ElementId类型的参数,则返回该ElelmentId,否则返回默认值“null”。例:底部约束
AsInteger 若参数的值是bool类型的参数,则值为”true”时,返回值为“1”;值为“false”时,返回值为“0”。当值不为bool类型时,返回默认值“0”。例:房间边界
AssociateWithGlobalParameter 将此参数与同一文档中的全局参数关联。
AsString 若参数的值是字符串类型,则返回该字符串,否则返回”空白“值。例:备注
AsValueString 将值转换为字符串。double转化单位为项目单位的数值字符串;ElementId转化为Id字符串;bool类型转化为”是“或”否“。
CanBeAssociatedWithGlobalParameter 测试此参数是否可以与给定的全局参数关联。
CanBeAssociatedWithGlobalParameters 测试此参数是否可以与任何全局参数关联。
Dispose 使对象立即释放它可能正在使用的任何资源。
DissociateFromGlobalParameter 将此参数与全局参数解关联。
Equals 确定指定对象是否等于当前对象。
GetAssociatedGlobalParameter 返回当前与此参数关联的全局参数(如果有)。
GetHashCode 返回该参数的哈希值
GetType 获取当前实例的类型。
Set Set方法有4个重载,可分别为参数设double值,int32值,字符串,ElementId值
SetValueString 根据输入字符串设置参数值。该方法仅适用于值类型的参数。
ToString 返回表示当前对象的字符串。
属性 Definition 返回定义对象,该对象描述参数的数据类型、名称和其他详细信息。每个参数都由一个Definition定义,该Definition中包含了该参数的名称,参数组,参数类型,单位类型
DisplayUnitType 获取参数对象的项目单位类型。
Element 此参数所属的元素。
GUID 共享参数的Guid。若为该参数非共享参数,则会引发异常。
HasValue 标识参数是否有已分配的值。
Id 参数id。
IsReadOnly 获取参数的只读属性,即该参数在API内的只读属性。对Revit程序而言。
IsShared 标识参数是否为共享参数。
StorageType 描述参数内部用于存储其值的类型。
UserModifiable 用户是否可以在Revit界面修改该参数的值。对Revit用户而言。

3.2.4 参数实例

通过墙体的无连接高度参数,批量修改墙体高度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//获取墙体修改前无连接高度
string before = "修改前墙体无连接高度为:\n"+walls.First().LookupParameter("无连接高度").AsValueString()+"\n"
//修改高度(修改模型必须在事务中进行,后续介绍)
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//事务开始
transaction.Start();
//修改高度
foreach (Element wall in walls)
{
wall.LookupParameter("无连接高度").Set(4000 / 304.8);
}
//事务介绍
transaction.Commit();
//获取墙体修改后无连接高度
string after = "修改后墙体无连接高度为:\n" + walls.First().LookupParameter("无连接高度").AsValueString();
TaskDialog.Show("Revit", before+after);
return Result.Succeeded;
}
}
}

修改前

image

修改后

image

3.3 编辑图元

通过Revit API 平台可以对某个图元进行移动、复制、旋转、对齐、镜像、成组、阵列、删除和锁定等操作。API中的编辑功能类似于Revit用户界面中的命令。

3.3.1 移动图元

Revit中移动图元的方法有两种,一种是通过ElementTransformUtils 类的方法进行移动,另一种是通过修改图元的Location属性进行移动。

(1)用ElementTransformUtils 类进行移动

ElementTransformUtils 类提供了两个静态方法来将一个或多个图元从某处移动到别处,如下所示:

1
public static void MoveElement(Document document, ElementId elementToMove, XYZ translation)

根据指定向量移动文件种的一个图元。

1
public static void MoveElements(Document document, ICollection<ElementId> elementsToMove, XYZ translation)

根据指定向量移动文件中的一组图元。

注意:

  • 当使用MoveElement()或MoveElements()方法时,这些方法不能将基于标高的图元移动到该标高的上方或下方。当图元基于某标高时,不能更改其Z坐标值,但可以将图元放置在同一标高的任何位置上。某些基于标高的图元有一个偏移实例参数可以用来在Z方向移动它们。例如,如果在标高1原点位置(0,0,0)新建一根柱,然后将其移动到新位置(10,20,30),则柱被放置在位置(10,20,0),而不是位置(10,20,30)。

  • 当移动一个或多个图元时,与其相关联的图元也跟着移动。例如,如果有一个窗户的墙移动了,则窗户也会跟着移动。

  • 被固定的图元不能被移动。

代码示例:使用MoveElements()方法移动所有墙体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//遍历所有墙体,收集墙体ID
List<ElementId> wallIds = new List<ElementId>();
foreach (Element wall in walls)
{
wallIds.Add(wall.Id);
}
//移动墙体,向上移动200mm
transaction.Start();
ElementTransformUtils.MoveElements(document, wallIds, new XYZ(0200 / 304.80));
transaction.Commit();
return Result.Succeeded;
}
}
}

移动前:

image

移动后:

image

(2)修改图元Location属性进行移动

移动Revit 图元的另一种方法是使用Location及其派生对象。在Revit 平台API中,Location对象提供了图元平移和旋转的能力。使用它的派生对象,如LocationPoint或LocationCurve,可以获得更多的位置信息和控制。如果Location图元向下转换为LoactionCurve对象或LocationPoint对象,则可以直接将曲线或点移动到新的位置上。

当移动图元时,请注意向量(300/304.8,400/304.8,0)不是目标位置而是偏移值。表示向左偏移300mm,向上偏移400mm

代码示例:修改所有墙体Location属性,将墙体左偏移300mm,向上偏移400mm。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//遍历所有墙体,修改Location属性,使墙体向左偏移300mm,向上偏移400mm
transaction.Start();
foreach (Element wall in walls)
{
(wall.Location as LocationCurve).Move(new XYZ(300 / 304.8400 / 304.80));
}
transaction.Commit();
return Result.Succeeded;
}
}
}

移动前:

image

移动后:

image

3.3.2 复制图元

ElementTransformUtils 类提供了几个静态方法,从某处复制一个或多个图元到别处,见表2-8,可以复制到同一个文件或视图内,也可以复制到其他文件或视图内。

1
CopyElement(Document document, ElementId elementToCopy, XYZ translation)

复制一个图元,并将其做指定偏移。

1
CopyElements(Document document, ICollection<ElementId> elementsToCopy, XYZ translation)

复制一组图元,并将其做指定偏移。

1
CopyElements(Document sourceDocument, ICollection<ElementId> elementsToCopy, Document destinationDocument, Transform transform, CopyPasteOptions options);

将一组图元从指定文档复制到目标文档,并做指定偏移(变换)。

1
CopyElements(View sourceView, ICollection<ElementId> elementsToCopy, View destinationView, Transform additionalTransform, CopyPasteOptions options)

将一组图元从指定视图复制到目标视图,并做指定偏移(变换)。

代码示例:复制所有墙体,并将其向上偏移2000mm。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//遍历所有墙体,获取所有墙体ID
List<ElementId> wallIds = new List<ElementId>();
foreach (Element wall in walls)
{
wallIds.Add(wall.Id);
}
//复制墙体,并向上偏移2000mm
transaction.Start();
ElementTransformUtils.CopyElements(document, wallIds, new XYZ(02000 / 304.80));
transaction.Commit();
return Result.Succeeded;
}
}
}

复制前:

image

复制后:

image

3.3.3 旋转图元

ElementTransformUtils 类提供了两种静态方法,用于旋转项目中的一个或多个图元。

1
RotateElement(Document document, ElementId elementToRotate, Line axis, double angle);

将一个图元绕指定旋转轴旋转指定的弧度。

1
RotateElements(Document document, ICollection<ElementId> elementsToRotate, Line axis, double angle);

将一组图元绕指定的旋转轴旋转指定的弧度。

在这些方法中,旋转角度是以弧度表示的。正的弧度表示绕指定轴逆时针旋转,负的弧度表示顺时针旋转。

代码示例:将所有墙体绕其自身中点逆时针旋转30°。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//遍历所有墙体,将墙体绕其自身中点逆时针旋转30°
transaction.Start();
foreach (Element wall in walls)
{
//获取墙体的定位线
Line wallLocaionLine = (wall.Location as LocationCurve).Curve as Line;
//获取墙体中点
XYZ midPoint = wallLocaionLine.GetEndPoint(0) + wallLocaionLine.Direction * wallLocaionLine.Length / 2
//创建旋转轴
Line axis = Line.CreateBound(midPoint, new XYZ(midPoint.X, midPoint.Y, midPoint.Z + 10));
//旋转墙体
ElementTransformUtils.RotateElement(document, wall.Id, axis, Math.PI / 6);
}
transaction.Commit();
return Result.Succeeded;
}
}
}

旋转前:

image

旋转后:

image

3.3.4 对齐图元

ItemFactoryBace.NewAlignment() 方法可以在两个参照之间新建一个锁定对齐。ItemFactoryBace属于Autodesk.Revit.Creation名称空间可在Document.Create.NewAlignment() 下调用。

1
ItemFactoryBace.NewAlignment(View view, Reference reference1, Reference reference2)

这两个参照必须为下列组和之一:

  • 两个平表面

  • 两条线

  • 直线和点

  • 直线和参照平面

  • 2条弧

  • 2个圆柱面

这些参照必须已经是几何对齐的,因为该函数不会强制他们变得对齐。如果对齐可被创建,则返回一个表示锁定对齐的新Dimension对象;否则将引发异常。

NewAlignment()方法还需要一个视图,用来确定对齐的方向

3.3.5 镜像图元

ElementTransformUtils 类提供了两静态类来镜像项目中的一个或多个图元。

1
MirrorElement(Document document, ElementId elementToMirror, Plane plane)

将一个图元以一个平面为对称轴进行镜像。

1
MirrorElements(Document document, ICollection<ElementId> elementsToMirror, Plane plane, bool mirrorCopies)

将一组图元以一个平面为对称轴进行镜像,可设置是否保留原图元。

在执行镜像操作之后,即可从Selection.ElementSet 访问新图元。

ElementTransformUtils.CanMirrorElement()和ElementTransformUtils.CanMirrorElements()可用于在尝试镜像一个或一组图元前确定是否可以镜像。

每个族实例都具有Mirrored属性,它显示该族实例是否已被镜像。

代码示例:在平面视图下,选择一条线,将所有墙体以该线为对称轴进行镜像。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获取项目UI文档
UIDocument uIDocument = commandData.Application.ActiveUIDocument;
//获得本项目文档
Document document = uIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//遍历所有墙体,获取所有墙体的ID
List<ElementId> wallIds = new List<ElementId>();
foreach (Element wall in walls)
{
wallIds.Add(wall.Id);
}
//选择一条线
Reference lineRe = uIDocument.Selection.PickObject(Autodesk.Revit.UI.Selection.ObjectType.Element,"请选择镜像轴线");
DetailLine detailLine = document.GetElement(lineRe) as DetailLine;
//获得轴线的定位线
Line locationLine = (detailLine.Location as LocationCurve).Curve as Line;
//获得定位线的起点和终点
XYZ startPoint = locationLine.GetEndPoint(0);
XYZ endPoint = locationLine.GetEndPoint(1);
//创建轴线上方的点
XYZ topPoint = new XYZ(startPoint.X, startPoint.Y, startPoint.Z + 10);
//以三点创建一个平面
Plane plane = Plane.CreateByThreePoints(startPoint, endPoint, topPoint);
//镜像墙体并保留原图元
transaction.Start();
ElementTransformUtils.MirrorElements(document, wallIds, plane, true);
transaction.Commit();
return Result.Succeeded;
}
}
}

镜像前:

image

镜像后:

image

3.3.6 成组图元

Revit平台API用 Creation.Document.NewGroup()方法选择一个图元或多个图元与组,然后将它们合并,对于某个组的每个实例,它们之同是有关联的。例如,项目中创建包含一张床、墙壁和窗户的多个实例,如果修改该组中的一面墙,那么该组中的所有实例该类型墙都会改变。由于一次操作就可更改某个组的多个实例,因此大大简化了建筑模型的修改。

1
NewGroup(ICollection<DB.ElementId> elementIds)

Revit 有三种类型的组:模型组、详图组和附属详图组。所有这些组都使用NewGroup()方法来创建。所创建组的类型取决于组内图元的类型。

  • 若无任何详图图元,则创建的是个模型组

  • 如果所有的图元都是详图图元,那么创建的是个详图组。

  • 如果这两种类型的图元都包括在内,则创建一个包含附属详图组的模型组并返回。注意:图元成组后,可将图元从项目中删除。

  • 模型组中某个模型图元被删除时,即使应用程序成功返回到用户界面,但若单击该组或鼠标悬停于其上,则它仍然可见。事实上,该模型图元已被删除,并且无法选择或访问该图元。

  • 当项目中一组实例的最后一个成员被删除、剔除或移除时,模型组实例也就被删除了

图元成组后,就不能移动或旋转。如果在已成组的图元上执行这些操作,尽管Move()或Rotate()方法返回true,但实际上图元没有发生任何变化。

如果参照的图元没有成组,则无法将其尺寸和标记成组。如果这样做则API调用会失败。

开发人员可以对参照某个模型组中模型图元的那些尺寸和标记进行分组。这些尺寸和标记被添加到一个附属详图组。你无法单独移动、复制、旋转、阵列或镜像附属详图组,而不对其参照模型组执行相同操作。

代码示例:将所有墙体成组,并将组名称该为”墙组1“

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//遍历所有墙体,获取所有墙体ID
List<ElementId> wallIds = new List<ElementId>();
foreach (Element wall in walls)
{
wallIds.Add(wall.Id);
}
transaction.Start();
//成组墙体
Group wallGroup = document.Create.NewGroup(wallIds);
//修改组名
wallGroup.GroupType.Name = "墙组1"
transaction.Commit();
return Result.Succeeded;
}
}
}

成组前:

image

成组后:

image

3.3.7 创建图元阵列

Revit 平台 API提供了两个类,即LinearArray和 RadialArray来阵列项目中的一个或多个图元。这些类提供静态方法来创建一个或多个选定构件的线性或径向阵列。线性阵列表示从一个点顺着一条线创建的阵列,而径向阵列表示沿弧线创建的阵列。

可以选择同一面墙上的一扇门和一扇窗,然后通过阵列创建门、墙、窗结构布局的多个实例。

LinearArray 和RadialArray 还提供了一些方法,来阵列未被分组和关联的一个或多个图元。虽然类似于阵列图元的 Create()方法,但每个目标图元都独立市其他图元,可对其探作而不影响其他图元。

LinearArray 方法
成员 说明
Create(Document aDoc, View dBView, ElementId id, int count, XYZ translationToAnchorMember, ArrayAnchorMember anchorMember); 根据指定数目阵列项目中的单个图元
Create(Document aDoc, View dBView, ICollection ids, int count, XYZ translationToAnchorMember, ArrayAnchorMember anchorMember); 根据指定数目阵列项目中的一组图元
ArrayElementsWithoutAssociation(Document aDoc, View dBView, ICollection ids, int count, XYZ translationToAnchorMember, ArrayAnchorMember anchorMember); 根据指定数目阵列项目中的单个图元,目标图元与线形阵列不关联。
ArrayElementWithoutAssociation(Document aDoc, View dBView, ElementId id, int count, XYZ translationToAnchorMember, ArrayAnchorMember anchorMember); 根据指定数目阵列项目中的一组图元,目标图元与线形阵列不关联。
RadiaArray 方法
成员 说明
Create(Document aDoc, View dBView, ElementId id, int count, Line axis, double angle, ArrayAnchorMember anchorMember); 根据输入的旋转轴,阵列项目中的单个图元
Create(Document aDoc, View dBView, ICollection ids, int count, Line axis, double angle, ArrayAnchorMember anchorMember); 根据输入的旋转轴,阵列项目中的一组图元
ArrayElementsWithoutAssociation(Document aDoc, View dBView, ICollection ids, int count, Line axis, double angle, ArrayAnchorMember anchorMember); 根据输入的旋转轴,阵列项目中的单个图元,目标图元与径向阵列不关联
ArrayElementWithoutAssociation(Document aDoc, View dBView, ElementId id, int count, Line axis, double angle, ArrayAnchorMember anchorMember); 根据输入的旋转轴,阵列项目中的一组图元,目标图元与径向阵列不关联

如果需要创建一个构件的多个实例开间时操控它们,则可使用阵列图元的方法。中的每个实例都可以是组的成员。

注意:使用阵列图元方法,适用以下规则:

(1)在执行线性和径向阵列操作时,依赖于这些阵列图元的图元也会被阵列。

(2)不能成组的那些图元无法阵列。更多信息请参阅 Revit 用户指南中关于组和阵充的限制条件的内容。

(3)大部分注释符号不支持阵列。

代码示例:在三维视图下,阵列向上出6组墙体,间距500mm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目的UI文档
UIDocument uIDocument = commandData.Application.ActiveUIDocument;
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//遍历所有墙体,获取所有墙体ID
List<ElementId> wallIds = new List<ElementId>();
foreach (Element wall in walls)
{
wallIds.Add(wall.Id);
}
//阵列墙体,间距500mm,个数6
transaction.Start();
LinearArray.Create(document, uIDocument.ActiveView, wallIds, 6, new XYZ(0500 / 304.80), ArrayAnchorMember.Second);
transaction.Commit();
return Result.Succeeded;
}
}
}

阵列前:

image

阵列后:

image

3.3.8 删除图元

Revit 平台API提供了Document.Delete()方法来删除项目中的一个或多个图元。

删除成员
成员 说明
Delete(ElementId elementId) 使用图元ID删除项目中的单个图元
Delete(ICollection elementIds) 使用图元IDs删除项目中的多个图元

注意:

  • 删除图元时,与其关联的子图元也会被删除

  • 删除图元后,删除图元的参照会失效,如果访问他们会引发异常

代码示例:删除项目中所有墙体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
amespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目的UI文档
UIDocument uIDocument = commandData.Application.ActiveUIDocument;
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//遍历所有墙体,获取所有墙体ID
List<ElementId> wallIds = new List<ElementId>();
foreach (Element wall in walls)
{
wallIds.Add(wall.Id);
}
//删除所有墙体
transaction.Start();
document.Delete(wallIds);
transaction.Commit();
return Result.Succeeded;
}
}
}

删除前:

image

删除后:

image

3.3.9 锁定图元

图元可以锁定,以防其移动。Element.Pinned 属性可用于检查某个图元是否已锁定或未锁定与另一个图元。

当Element.Pinned 设置为true,则不能移动或旋转该图元。

4 几何图元

4.1 族实例

4.1.1 创建单个族实例

通常,创建族实例对象使用Autodesk.Revit.Creation.Document类的NewFamilyInstance()方法,该方法具有12个重载方法。选择使用哪个重载不仅取决于实例的类别,而且与其它位置特征也有关,如该实例对象是否嵌入主体、置于相关参照标高,或直接置于特定的面。

用NewFamilyInstance()创建实例的选项

NewFamilyInstance()参数 说明
XYZ,FamilySymbol,StructuralType 在无参照标高或主体图元的任意位置创建实例
XYZ,Famliysymbol,Element,StructuralType 如果实例对象嵌与墙、楼板或天花板上
XYZ,FamilySymbol,XYZ,Element,StructuralType 如果对象嵌于墙、楼板或天花板上,并需要非默认的定方向
XYZ,FamilySymbol,Element,Level,StructuralType 如果对象嵌与墙,楼板或天花板上,并与某个参照标高关联
XYZ,FamilySymbol,Level,StructuralType 如果对象与某个参照关联
Face,XYZ,XYZ,FamilySymbol 如果是基于面的对象,并需要非默认的定方向
Reference,Line,FamilySymbol 如果是个基于面的线性对象,但接受“面参照”而非某个面
XYZ,FamilySymbol,Level,StructuralTpye 创建基面位于参照标高上的柱。该柱将延伸到模型中临近的可用标高,或以默认柱高延伸(若参照标高上没有合适的标高)
XYZ,FamilySymbol,Element,StructuralType 门窗必须嵌于墙上,若它们可被置于默认方向,则使用此方法
XYZ,FamilySymbol,XYZ,Element,StructuralType 如果所创实例需要一个非默认的定位方向
XYZ,FamilySymbol,Element,Level,StructuralType 如果实例需要一个参照标高关联
Curve,FamilySymbol,Level,StructuralType 根据给定曲线创建基于标高的支撑或梁。这是创建支撑或梁的推荐方法
XYZ,FamilySymbol,StructuralType 在任意位置创建实例
XYZ,FamilySymbol,Element,Level,StructuralType 如果实例嵌于某个图元(楼板等)并与参照标高关联
XYZ,FamilySymbol,Level,StructuralType 若实例与某个参照标高关联
XYZ,FamilySymbol,Element,StructuralType 如果实例嵌于某个图元(楼板等)
Line,FamilySymbol,View 仅适用于二维族基于线的详图符号
XYZ,FamlySymbol,View 仅适用于二维族符号

在NewFamilyInstance()方法需要设定StructuralType参数,以指定创建族实例的类型。StructuralType值对应族实例类型如下:

StructuralType值 族实例类型
NonStructural 门、桌子等非结构类型
Beam
Brace 支撑
Column
Footing 基础

代码示例:在所有墙体的两端创建结构柱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目的UI文档
UIDocument uIDocument = commandData.Application.ActiveUIDocument;
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//获取结构柱类型
FamilySymbol columnSymbol = new FilteredElementCollector(document).WhereElementIsElementType().OfClass(typeof(FamilySymbol)).Where(x => x.Name == "UC305x305x97").First() as FamilySymbol;
//获取标高1
Level level1 = new FilteredElementCollector(document).WhereElementIsNotElementType().OfClass(typeof(Level)).Where(x => x.Name == "标高 1").First() as Level;
//遍历所有墙体,创建结构柱
transaction.Start();
foreach (Element wall in walls)
{
//获取墙体的定位线
Line wallLine = (wall.Location as LocationCurve).Curve as Line;
//创建结构柱
document.Create.NewFamilyInstance(wallLine.GetEndPoint(0), columnSymbol, level1, StructuralType.Column);
document.Create.NewFamilyInstance(wallLine.GetEndPoint(1), columnSymbol, level1, StructuralType.Column);
}
transaction.Commit();
return Result.Succeeded;
}
}
}

创建前:

image

创建后:

image

4.1.2 批量创建族实例

一些FamilyInstance 对象需要创建多个位置,在这种情况下,使用该对象所提供的详细的创建方法更为适合。如果实例未创建,则引发异常。调用该方法之前,必须先将类型/符号加载到项目中。

使用Document.NewFamilyInstances2(),通过一次创建多个族实例,可以精简代码并提高性能。此方法有个单独的参数,即描述所创建族实例的FamilyInstanceCreatetionDate 对象列表。

代码示例:沿X轴,批量创建结构柱

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目的UI文档
UIDocument uIDocument = commandData.Application.ActiveUIDocument;
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document, "创建事务");
//获取结构柱类型
FamilySymbol columnSymbol = new FilteredElementCollector(document).WhereElementIsElementType().OfClass(typeof(FamilySymbol)).Where(x => x.Name == "UC305x305x97").First() as FamilySymbol;
//获取标高1
Level level1 = new FilteredElementCollector(document).WhereElementIsNotElementType().OfClass(typeof(Level)).Where(x => x.Name == "标高 1").First() as Level;
//构造10个柱子信息
List<Autodesk.Revit.Creation.FamilyInstanceCreationData> familyInstanceCreationDatas = new List<Autodesk.Revit.Creation.FamilyInstanceCreationData>();
for (int i = 1; i <= 10; i++)
{
Autodesk.Revit.Creation.FamilyInstanceCreationData familyInstanceCreationData = new Autodesk.Revit.Creation.FamilyInstanceCreationData(new XYZ(i * 1000 / 304.800), columnSymbol, level1, StructuralType.Column);
familyInstanceCreationDatas.Add(familyInstanceCreationData);
}
//创建结构柱
transaction.Start();
document.Create.NewFamilyInstances2(familyInstanceCreationDatas);
transaction.Commit();
return Result.Succeeded;
}
}
}

创建前:

image

创建后:

image

4.2 注释图元

4.2.1 尺寸标注

尺寸标注主要有三种类型:临时性尺寸标注(Temporary Dimensions)、永久性尺寸标注(Permanent Dimensions)和高程点标注(Spot Dimensions)。当创建或选择几何图形时,Revit会在构件周围显示临时尺寸标注,这有利于在适当的位置放置构件。

永久性尺寸标注是特意放置的尺寸标注,通过Revit API只能访问和创建永久性尺寸标注和高程点标注。

(1)创建尺寸标注

类型 方法 参数描述
线性尺寸标注 Creation.Document.NewDimension(View view,Line line,ReferenceArray references) 参数view是尺寸标注所要创建在的视图,line表示线性尺寸的直线,references是绑定的几何参照。
Creation.Document.NewDimension(View view,Line line,ReferenceArray references,DimensionType dimensionType) 参数view是尺寸标注所要创建在的视图,line 表示线性尺寸的直线,references是绑定的几何参照,dimensionType是尺寸标注的族类型。
Creation.FamilyltemFactory.NewLinearDimension( View view,Line line,Reference Array references) 参数view是尺寸标注所要创建在的视图,line表示线性尺寸的直线,references是绑定的几何参照
Creation.FamilyltemFactory.NewLinear Dimension(View view,Line line,ReferenceArray references,DimensionType dimension Type) 参数 view是尺寸标注所要创建在的视图,line表示线性尺寸的直线,referenes是绑定的几何参照,dimensionType是尺寸标注的族类型
对齐尺寸标注 Document.NewAlignment(View view,Reference reference1,Reference reference2) 参数view 是尺寸标注所要创建在的视图,reference1和 reference2是绑定的几何参照。
角度尺寸标注 Creation.FamilyltemFactory.NewAngularDimension(View view,Arc arc,Reference firstRef,Reference secondRef) 参数view 是尺寸标注所要创建在的视图,arc 表示所要标注的圆弧,firstRef 和aecomdRef是尺寸标注的两个参照,它们必须和圆弧垂直
Creation.FamilyltemFactory.NewAngularDimension(View view,Arc arc,Reference firstRef,Reference secondRef,DimensionTypedimensionType) 参数view 是尺寸标注所要创建在的视图,arc表示所要标注的圆弧,firstRef 和secondRef 是尺寸标注的两个参照,它们必须和圆弧垂直,dimensionType是尺寸标注的族类型
AngularDimension.Create(Document document, View dbView, Arc arc, IList references, DimensionType dimensionStyle) 参数document 是创建标注的文档,view 是尺寸标注所要创建在的视图,arc表示所要标注的圆弧,references是绑定的几何参照,它们必须和圆弧垂直,dimensionType是尺寸标注的族类型
弧长度尺寸标注· Creation.FamilyItemFactory.NewArcLengthDimension(View view,Arc arc,Reference arcRef,Reference firstRef,Reference secondRef) 参数view 是尺寸标注所要创建在的视图,arc表示所要标注的圆弧,arcRef 表示所要标注的圆弧的几何参照,firstRef 和 secondRef是尺寸标注的两个参照,它们必须和圆弧相交
Creation.FamilyItemFactory.NewArcLengthDimension(View view,Arc arc,Reference arcRef,Reference firstRef,Reference secondRef,DimensionType dimensionType) 参数view是尺寸标注所要创建在的视图,arc表示所要标注的圆弧,arcRef 表示所要标注的圆弧的几何参照,firstRef 和 secondRef是尺寸标注的两个参照,它们必须和圆弧相交。dimensionType是尺寸标注的族类型
直径尺寸标注 Creation.FamilyltemFactory.NewDiameterDimension(View view,Reference arcRef,XYZ origin) 参数 view是尺寸标注所要创建在的视图,arcRef表示所要标注的圆弧的几何参照,origin 和圆心连线表示尺寸标注所在的线
径向尺寸标注 Creation.FamilyltemFactory.NewRadialDimension(View view,Reference arcRef,XYZ origin) 参数view是尺寸标注所要创建在的视图,arcRef 表示所要标注的圆弧的几何参照,origin和圆心连线表示尺寸标注所在的线
Creation.FamilyItemFactory.NewRadialDimension(View view,Reference arcRef,XYZ origin,DimensionType dimensionType) 参数view是尺寸标注所要创建在的视图,arcRef 表示所要标注的圆弧的几何参照,origin 和圆心连线表示尺寸标注所在的线,dimensionType是尺寸标注的族类型
高程点标注 Creation.Document.NewSpotElevation(View view,Reference reference,XYZ origin,XYZ bend,XYZend,XYZ refPt,bool hasLeader) 参数view是高程点标注所要创建在的视图,reference 是其几何参照,origin是标注的起点,bend是弯曲点,end 是标注的终点,refPt是标注所要测量的点,hasLeader表示是否有箭头。
高程点坐标标注 Creation.Document.NewSpotCoordinate(View view,Reference reference,XYZ origin,XYZ bend,XYZ end,XYZ refPt,bool hasLeader) 参数view是高程点标注所要创建在的视图,reference是其几何参照,origin是标注的起点,bend是弯曲点,end是标注的终点,refPt是标注所要测量的点,hasLeader表示是否有箭头

(2)尺寸标注文字修改

在Revit中,可以在如下图所示的对话框中自定义尺寸标注上所显示的文字。(通过双击尺寸标注的文字来调出该对话框)

image

尺寸标注文字属性框

可以使用实际的尺寸值再加上附加的文字字段来表示尺寸标注的文字,也可以以随意的文字来替换实际尺寸值。

image

有附加文字的尺寸标注

Revit API 也提供了相应的接口来读取过修改这些值。

属性 描述 访问
Dimension.Above 文字字段中的“高于” 可读写
Dimension.Below 文字字段中的“低于” 可读写
Dimension.Prefix 文字字段中的“前缀” 可读写
Dimension.Suffix 文字字段中的“后缀” 可读写
Dimension.VauleString 文字字段中的“值” 只读
Dimension.ValueOverride 替换实际尺寸值的任意文字 可读写

代码示例:创建墙体的长度尺寸标注,并修改标注尺寸值为”墙体长度为xxx毫米“

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目的UI文档
UIDocument uIDocument = commandData.Application.ActiveUIDocument;
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document,"创建事务");
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//获取标高1视图
View level1View = new FilteredElementCollector(document).WhereElementIsNotElementType().OfClass(typeof(View)).Where(x => x.Name == "标高 1").First() as View;
//遍历所有墙体,创建尺寸标注
transaction.Start();
foreach (Element wall in walls)
{
//获取墙体的定位线
Line wallLine = (wall.Location as LocationCurve).Curve as Line;
//创建标注的定位线,墙体定位线向上偏移1500mm
Line dimensionLine = Line.CreateUnbound(wallLine.GetEndPoint(0) + level1View.UpDirection * 1500 / 304.8, level1View.RightDirection);
//创建参照集
ReferenceArray referenceArray = new ReferenceArray();
//定位线
Line line1 = Line.CreateBound(wallLine.GetEndPoint(0), wallLine.GetEndPoint(0) + level1View.UpDirection * 1 / 304.8);
Line line2 = Line.CreateBound(wallLine.GetEndPoint(1), wallLine.GetEndPoint(1) + level1View.UpDirection * 1 / 304.8);
//定位详图线
DetailLine detailLine1 = document.Create.NewDetailCurve(level1View, line1) as DetailLine;
DetailLine detailLine2 = document.Create.NewDetailCurve(level1View, line2) as DetailLine;
//详图线转参照
referenceArray.Append(new Reference(detailLine1));
referenceArray.Append(new Reference(detailLine2));
//创建尺寸标注
Dimension wallDimension = document.Create.NewDimension(level1View, dimensionLine, referenceArray);
//修改尺寸标注值
wallDimension.ValueOverride = $"墙体长度为{wallDimension.ValueString}毫米"
}
transaction.Commit();
return Result.Succeeded;
}
}
}

标注前:

image

标注后:

image

4.2.2 详图曲线

详图线和其他注释一样,也是基于视图的。详图线是在视图的草图平面中绘制的。

详图线相对比较简单,类Autodesk.Revit.BD.DetailLine 代表详图线。创建方法如下

1
Autodesk.Revit.Creation.ItemFactoryBase.NewDetailCurve(View view, Curve geometryCurve)

其中,参数view是所要创建的详图线所在的视图,GeometryCurve是其对应的几何线。详图线有两个重要的属性

  • DetailLine.GeometryCurve:详图线对应的几何线,可读写。

  • DetailLine.LineStyle:线样式,可读写。

代码示例见尺寸标注代码示例。

4.2.3 文字

在Revit API中,类TextNote 代表文字注释元素。

(1)文字的创建

可通过以下方法创建文字注释:

方法 参数说明
TextNote.Create(Document document, ElementId viewId, XYZ position, string text, ElementId typeId) * document:项目文档 * viewId:文字放置视图的ID * position:文字放置点 * width:文字宽度 * typeId:文字字体类型ID
TextNote.Create(Document document, ElementId viewId, XYZ position, string text, TextNoteOptions options) * document:项目文档 * viewId:文字放置视图的ID * position:文字放置点 * text:文字内容 * options:控制文本注释的行为和外观的选项
TextNote.Create(Document document, ElementId viewId, XYZ position, double width, string text, ElementId typeId) * document:项目文档 * viewId:文字放置视图的ID * position:文字放置点 * width:文字宽度 * text:文字内容 * typeId:文字字体类型ID
TextNote.Create(Document document, ElementId viewId, XYZ position, double width, string text, TextNoteOptions options) * document:项目文档 * viewId:文字放置视图的ID * position:文字放置点 * width:文字宽度 * text:文字内容 * options:控制文本注释的行为和外观的选项

(2)文字的属性修改

可以通过以下属性对文字进行修改,也可通过参数(Paramenter)来访问和修改所需要内容。

属性 描述 访问
Height 文字高度 只读
Width 文字宽度 可读写
Text 文字内容 可读写
BaseDirection 文字的水平方向 只读
UpDirection 文字的垂直方向 只读
Symbol 文字的族类型 只读
Coord 文字的放置点坐标 可读写
TextNoteType 文字字体类型 可读写

4.2.4 标记

对于标记,通过Autodesk.Revit.Creation.Document.NewTag() 创建更为合适。需要参数如下:

NewTag(View,Element,Boolean,TagMode,TagOrientation,XYZ)

  • View:该标记所要放置的平面视图

  • Element:该标记的主体图元

  • Boolean:是否显示标记引线

  • TagMode:当所要创建的是材料标记时,TagMode应设置TM_ADDBY_MATERIAL;当所要创建的是多类别标记时,TagMode应设置TM_ADDBY_MUTICATEGORY;其它类别标记TagMode则设置TM_ADDBY_CATEGORY

  • TagOrientation:设置标记的方向(水平或竖直)

  • XYZ:标记的放置点坐标

4.4 几何

在Autodesk.Revit.DB命名空间里包含了一些几何图形相关的类型,它们在API中用于几何图形的表示和处理,从基类继承的情况分,API 提供了三大种几何类型来描述和存储几何信息:

  • 几何基元类:包括所有从GeometryObject派生出来的子类。

  • 几何辅助类:包括一些从APIObject派生出来的几何相关的子类和一些值类型。

  • 几何集合类:包括一些实现了 IEnumerable 或者 IEnumerator接口的几何相关的类型。

本章将介绍如何使用各种图形相关的类型,如何从元素中获取几何数据,如何让一个元素进行图形变换等。

用”Revit LookUp”图元的Geometry属性可以查看图元的几何,如下为墙体的几何属性界面:

image

4.4.1 几何基元类

几何基元类在API中描述图形表示,由基类GeometryObject派生,主要有如下的类型:

①轮廓(Profile),轮廓是可用来生成形状的单条线、一串连接起来的线或闭合的环。可以通过操纵轮廓来修改形状的几何图形。

②面(Face):三维空间的实体面。其子类型有:平面(PlanarFace),规则曲面(RuledFace),旋转面(RevolvedFace),圆锥面(ConicalFace),圆柱面(CylindricalFace),Hermite曲面(HermiteFace)。

③边(Edge):三维空间实体的边。

④线(Curve),参数曲线。其子类有:直线(Line),圆弧线(Arc),椭圆线(Ellipse),架旋线(CylindricalHelix),Hemite样条曲线(HermiteSpline),NURBS样条曲线(NurbSpline)。

⑤点(Point):三维空间的点。

⑥几何元素(GeometryElement):一个元素的几何表示,包含了所有的几何信息。

⑦几何实例(Geometrylnstance):一个类型图元的实例,可以取得与该实例相关的类型图元的几何信息。

⑧网格(Mesh):三角化网格用于描述三维面的形状。

⑨实体(Solid):三维实体。

(1)几何实例(GeometryInstance)

几何实例表示了储存在 Revit中默认配置的几何组合,通过各种变换到适当的位置成为一个元素的属性。最普遍的情况是在族实例中应用儿何实例。Revit使用几何实例来存储一份特定族元素类型的几何拷贝,然后在多个族实例中重复使用它。但是并不是所有的族实例都会有几何实例。当由于局部相连,相交,以及其他种种因素影响到实例位置时,Revit只需要唯一一份特定族元素类型的几何表示,这种情况就不会有几何实例,而是用实体来进行几何表示。

几何实例中有SymbolGeometry属性,该属性是生成这个几何实例的类型的几何表示,它使用的是局部坐标系。几何实例也提供了一个Transform属性,表示了从类型的局部坐标系到实例的世界坐标系的坐标变换。

几何实例也提供了GetInstanceGeometry()和GetSymbolGeometry()方法来分别获取其族实例的几何元素和族类型的几何元素。一般来说,从几何实例中可以得到三种类型的数据:

  • 线(Curve):基类线或者子类的线。

  • 实体(Solid):包含了面和边。

  • 几何实例(Instance):另一个几何实例,用来构造本几何实例。

用户有时候需要使用 Transform属性对取到的几何数据进行坐标变换。

代码示例:获取桌子几何实例的体积

打开Revit,在建筑样板下,点击,建筑→构件→放置构件,放置一个桌子实例。首先从族实例中获取到它的几何元素,然后从几何元素中找到其几何实例,再用GetInstanceGeometry() 从几何实例获取其族实例的几何元素,最后遍历几何元素中的所有实体,获取实体的体积。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取一个桌子的实例
FamilyInstance desk = collector.OfClass(typeof(FamilyInstance)).Where(x=>(x as FamilyInstance).Symbol.FamilyName=="桌").First() as FamilyInstance;
//声明体积
double deskVolume = 0
//获取桌子的几何元素
GeometryElement deskGeo = desk.get_Geometry(new Options());
//遍历桌子的几何元素,获取几何实例
foreach (GeometryObject geometryObject in deskGeo)
{
GeometryInstance geometryInstance = geometryObject as GeometryInstance;
if (geometryInstance != null)
{
//获取几何实例中的几何元素
GeometryElement instGeoEle = geometryInstance.GetInstanceGeometry();
//遍历实例中的几何元素,获得所有实体
foreach (GeometryObject instGeoObj in instGeoEle)
{
Solid solid = instGeoObj as Solid;
if(solid != null)
{
//获取所有实体体积
deskVolume += solid.Volume;
}
}
}
}
//对体积进行单位转换(由立方英尺转换为立方米)
deskVolume = Math.Round(deskVolume * 0.3048 * 0.3048 * 0.30482);
//打印信息
TaskDialog.Show("Revit", $"该桌子的体积为{deskVolume}m³");
return Result.Succeeded;
}
}
}

运行后:

image

(2)实体(Solid)

实体类型定义了一个包含了面和边的三维实体,如立方体和长方体,同时可以从它的属性中获取对应的表面积和体积。实体的属性如下:

  • Edges:实体的边

  • Faces:实体的面

  • SurfaceArea:实体的表面积

  • Volume:实体的体积

有时API可以取到没有任何边和面的空实体,使用前请先检查对应的属性来确保获取想要的边和面。例如添加 **if(solid != null && solid.Face.Size != 0)**语句判断后再使用。

image

空实体属性

代码示例:获取墙体的周长

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取一个墙体的实例
Wall wall = collector.OfClass(typeof(Wall)).WhereElementIsNotElementType().First() as Wall;
//声明周长
double wallPerimeter = 0
//获取墙体的几何元素
GeometryElement deskGeo = wall.get_Geometry(new Options());
//遍历墙体的几何元素,获取实体
foreach (GeometryObject geometryObject in deskGeo)
{
Solid solid = geometryObject as Solid;
if (solid != null && solid.Faces.Size != 0)
{
//遍历实体的所有边,获取周长
foreach (Edge edge in solid.Edges)
{
wallPerimeter += edge.ApproximateLength;
}
}
}
//对体积进行单位转换(由英尺转换为米)
wallPerimeter = Math.Round(wallPerimeter * 0.30482);
//打印信息
TaskDialog.Show("Revit", $"该墙体的周长为{wallPerimeter}m");
return Result.Succeeded;
}
}
}

运行后:

image

4.4.2 几何集合类

API提供了下面一些集合类用来者遍历几何数据,所有这些类的方法和属哲类似的。

  • 线:CurveArray,CurveArraylterator

  • 边:EdgeArray,EdgeArraylterator,EdgeArrayArray,EdgeArrayArraylterator

  • 面:FaceArray,FaceArraylterator

  • 几何对象:GeometryObjectArray,GeometryObjectArraylterator

  • 实例:InstanceArray,InstanceArraylterator

  • 网格:MeshArray,MeshArraylterator

  • 引用:ReferenceArray,ReferenceArrayIterator

  • 实体:SolidArray,SolidArrayIterator

  • Double值:DoubleArray,DoubleArraylterator

5 事务

5.1 事务概述

事务是个类似上下文的对象,它封装了Revit模型中的任何更改。对文件的任何更改只能在该文件的活动事务打开之际进行。试图在事务之外更改文件将引发异常。所作更改不会成为模型的一部分,直到活动事务提交完成。因此,在事务中所作的所有更改都可(通过析构函数)显式或隐式回滚。在任何时候,每个文件只能打开一个事务。一个事务可以包括多个操作。Revit API 中与事务相关的主类有三个:

  • Transaction(事务)

  • SubTransaction(子事务)

  • TransactionGroup(事务组)

本节主要介绍Transaction类,对文件进行更改只需要 Transaction类。而其他的类用于更好地组织更改。要注意的是,在调用该命令时,应用于IExteralCommand 定义的 TransactionMode 属性,会影响 Revit 预期处理事务的方式。回顾事务属性可获得更多信息。

注:若从线程以外或非模态对话框以外启动事务,则将引发异常。事务只能从受支持的API工作流中启动,如部分外部命令、直件、更新器或者回调。

5.2 事务类通用方法

所有三个事务相关的类有相同的方法,如下表所示:

方法 描述
Start 启动事务
Commit 结束事务,并提交所有修改到文档中
RollBack 结束事务,并撤销对文档所做的所有修改
GetStatus 返回当前事务状态

除了GetStatus函数会返回当前事务状态外,Start,Commit和RollBack函数也会返回TransactionStatus表示当前操作是否成功。TransactionStatus值包含了如下值:

描述
Uninitialized 事务对象实例化后的初始值,但上下文尚未启动
Started 事务对象已经成功启动(Start方法已调用)
Committed 事务对象成功提交完成(Commit方法已调用)
RolledBack 事务对象成功回滚(Rollback方法已调用)
Pending 试图提交或回滚事务对象,但因故障而进程尚无法完成,正等待最终用户的响应(在非模态对话框中)。一旦故障处理完成,状态将自动更新(到Committd或RolledBack状态)

5.3 事务

事务是便于Revit模型作任意更改所需的上下文,++一次只能打开一个事务++;不允许嵌套使用。**每个事务必须都有一个名称。**名称可以在创建事务对象的时候赋值,也可以在事务启动的时候赋值。这个“事务名称”在成功提交后会显示在Undo菜单中。

image

Revit Undo菜单

创建事务的两种方法:

(1)直接声明事务对象

1
2
3
4
5
6
7
8
//创建事务对象
Transaction transaction = new Transaction(document, "事务名称");
//启动事务
transaction.Start();
//模型修改内容……

//提交事务
transaction.Commit();

事务名称也可以在事务启动时候赋值(推荐),代码如下:

1
2
3
4
5
6
7
8
//创建事务对象
Transaction transaction = new Transaction(document);
//启动事务
transaction.Start("事务名称");
//模型修改内容……

//提交事务
transaction.Commit();

(2)用using创建事务

1
2
3
4
5
6
7
8
9
10
//创建事务对象
using(Transaction transaction = new Transaction(document, "事务名称"))
{
//启动事务
transaction.Start();
//模型修改内容……

//提交事务
transaction.Commit();
}

6 外部事件

6.1 外部事件简介

Revit API提供了外部事件框架可以用于非模态对话框。它是特别针对异步处理所开放的事件接口。它和闲置事件(IdlingEvenl)处理类似,但可以由用户自己触发。

要使用外部事件来实现非模态对话框可以通过下面儿步来实现:

  • 继承并实现实现外部事件处理接口(IExternalEventHandler);

  • 用静态方法ExternalEvent.Create()方法来创建一个外部事件(ExternalEvent);

  • 当非模态对话框中某个时候需要调用Revi@方法时,调用ExternalEvent.Raise();

  • Revit@会在下个闲置时间(idling time cyele)到来时调用 IExternalEventHandler.Execute()方法的实现。

6.2 外部事件的创建

调用外部事件类ExternalEvent.Create()方法来创建一个外部事件。该事件所创建的一个实例会被返回给创建者,创建者使用该实例来向 Revit发信号触发该事件。Revit会不时地检查是否有外部事件发信号,如果有就会调用外部事件相应的Execute()函数。

6.3 外部事件的触发

调用外部事件类ExternalEvent.Raise()方法来触发一个外部事件。当它被调用后,Revit会等到下一个闲置时间(idling time)来调用注册好的IExternalEventHandler.Execute()方法。

6.4 外部事件接口

IExternalEventHandler接口这是外部事件需要实现的接口。实现这个接口的实例会被注册到 Revit中,当每次应的外部事件触发时,该接口的Execute()方法会被调用。IExternalEventHandiler 只有两个接口需要实现:Execute()方法和 GetName()方法。后者要返回事件的名字。下列代码片段展示了一个完整外部事件应用的例子。首先是实现IExternalEventHandler 接口,下面代码当事件被调用时显示一个任务对话框。

1
2
3
4
5
6
7
8
9
10
11
public class ExternalEventExamp : IExternalEventHandler
{
public void Execute(UIApplication app)
{
TaskDialog.Show("Revit","示例窗口");
}
public string GetName()
{
return "外部事件接口示例代码";
}
}

代码示例:利用外部事件创建墙体

打开VisualStudio2017 点击“文件”→“新建”→“项目”,选择WPF应用(.NET Framework)。

image

第一步:在名称空间处右键,选择属性。在输出类型,将“Windows 应用程序” 改为“类库”

image

选择属性

image

将输出类型改为类库

第二步:将App.config 和 App.xaml删除。

image

删除后资源管理器界面

第三步:创建主程序类(继承IExternalCommand接口),创建外部命令类(继承IEventHandler)。在名称空间右键,选择“添加”→“新建项”→”类“。

image

image

第四步:添加”Revit API“和”Revit APIUI“引用,把复制本地改为”False“。完善主程序类代码,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace WpfApp2
{
[Transaction(TransactionMode.Manual)]
class CreatWallMainProgram : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//创建窗口对象
MainWindow mainWindow = new MainWindow();
//显示主窗口
mainWindow.Show();
return Result.Succeeded;
}
}

第五步:完善创建墙体外部事件类代码,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
namespace WpfApp2
{
class CreatWallEvent : IExternalEventHandler
{
//墙体位置属性
public XYZ WallLocation { get; set; }
//墙体高度属性
public double WallHight { get; set; }
//墙体长度属性
public double WallLength { get; set; }
public void Execute(UIApplication app)
{
//获取用户文档;
Document document = app.ActiveUIDocument.Document;
//声明事务
Transaction transaction = new Transaction(document);
//墙体放置点1
XYZ point1 = WallLocation;
//墙体高度点2
XYZ point2 = point1 + XYZ.BasisZ * WallHight / 304.8;
//墙体长度点3
XYZ point3 = point1 + XYZ.BasisX * WallLength / 304.8;
//墙体点4
XYZ point4 = point3 + XYZ.BasisZ * WallHight / 304.8;
//创建墙体轮廓
Line line1 = Line.CreateBound(point1, point2);
Line line2 = Line.CreateBound(point2, point4);
Line line3 = Line.CreateBound(point4, point3);
Line line4 = Line.CreateBound(point3, point1);
List<Curve> curves = new List<Curve>() { line1, line2, line3, line4 };
//开启事务
transaction.Start("创建墙体");
//创建墙体
Wall.Create(document, curves, true);
//提交事务
transaction.Commit();
}

public string GetName()
{
return "创建墙体";
}
}
}

第六步:制作Windows窗口,窗口xaml代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="153.426" Width="192.628" >
<Grid>
<Button Content="生成墙体" HorizontalAlignment="Left" Margin="56,82,0,0" VerticalAlignment="Top" Width="74" Click="Button_Click"/>
<TextBox x:Name="wallHight" HorizontalAlignment="Left" Height="23" Margin="47,18,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBlock HorizontalAlignment="Left" Margin="11,22,0,0" TextWrapping="Wrap" Text="高度:" VerticalAlignment="Top"/>
<TextBox x:Name="wallLength" HorizontalAlignment="Left" Height="23" Margin="47,46,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBlock HorizontalAlignment="Left" Margin="10,51,0,0" TextWrapping="Wrap" Text="长度:" VerticalAlignment="Top"/>
</Grid>
</Window>

窗体效果如下:

image

第七步:完成MainWindow.cs 代码,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
namespace WpfApp2
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
//注册外部事件
CreatWallEvent creatWallEvent;
ExternalEvent externalEvent;
public MainWindow()
{
InitializeComponent();
//初始化外部事件
creatWallEvent = new CreatWallEvent();
externalEvent = ExternalEvent.Create(creatWallEvent);
}

private void Button_Click(object sender, RoutedEventArgs e)
{
//参数传递
creatWallEvent.WallHight = Convert.ToDouble(wallHight.Text);
creatWallEvent.WallLength = Convert.ToDouble(wallLength.Text);
//执行外部命令
externalEvent.Raise();
}
}
}

第八步:生成方案,执行插件image

执行效果如下:

image

7 部件和视图

7.1 Revit部件

在Revit软件中,部件的创建可以通过选择要成为部件的图元,在修改选项卡中,点击创建部件实现。

image

创建部件

部件创建完成后,可点击“创建视图”按钮,选择要创建的部件视图,完成部件视图创建。

image

创建部件视图

7.2 部件

AssemblyInstance类的静态方法Creat()用于在项目中创建一个新的部件实例。Creat()方法比须在事务内创建,并且必须在行新创建的部件实例上执行任何操作之前提交事务。在事务完成中之后,部件类型才被指定。

创建部件实例的另一个方法是使用现有部要使用现有部件类型来创建部件实例,请使用静态方法Assemblylnstancc.Placelnstance()并指定要用到的部件类型的图元ID,以及用来放置部件的位置。

7.3 视图

使用 AsscmblyViewUtils类,可以创建部件实例的各种部件视图,包括正交三维部件视图、详图剖面部件视图,材料提取多类别明细表部件视图、零件清单多类别明细表部件视图以及图纸部件视图。在使用任何这些新创建的部件视图之前,文件必须重新生成。通过上面的示例可发现,创建两个新部件视图后,事务被提交,Commit()方法会自动重新生成文件。

代码示例:将项目所有墙体创建部件,并修改部件类型名称,创建部件视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
namespace HelloWord
{
[Transaction(TransactionMode.Manual)]
public class Class1 : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
//获得本项目文档
Document document = commandData.Application.ActiveUIDocument.Document;
//创建事务
Transaction transaction = new Transaction(document);
//创建图元过滤集,收集整个文档所有构件
FilteredElementCollector collector = new FilteredElementCollector(document);
//获取本项目所有墙体
List<Element> walls = collector.OfClass(typeof(Wall)).ToList();
//遍历所有墙体,创建部件
foreach (Element wall in walls)
{
//开启事务
transaction.Start("创建部件");
//创建部件
AssemblyInstance wallAssembly = AssemblyInstance.Create(document, new List<ElementId>() { wall.Id }, wall.Category.Id);
//提交事务(必须先提交事务,否则找不到部件进行下一步名称修改)
transaction.Commit();
//开启事务
transaction.Start("修改名称,创建视图");
//修改部件名称为墙体长度*墙体高度
document.GetElement(wallAssembly.GetTypeId()).Name = wall.LookupParameter("长度").AsValueString()+"*"+ wall.LookupParameter("无连接高度").AsValueString();
//创建墙体三视图
ViewSection frontView = AssemblyViewUtils.CreateDetailSection(document, wallAssembly.Id, AssemblyDetailViewOrientation.ElevationFront);
ViewSection rightView = AssemblyViewUtils.CreateDetailSection(document, wallAssembly.Id, AssemblyDetailViewOrientation.ElevationRight);
ViewSection topView = AssemblyViewUtils.CreateDetailSection(document, wallAssembly.Id, AssemblyDetailViewOrientation.ElevationTop);
//提交事务
transaction.Commit();
}
//提交事务
return Result.Succeeded;
}
}
}

创建前:

image

创建后:image

事务名称显示在Undo菜单中:
image