工作共享

工作共享是一种设计方法,它允许多个团队成员同时处理同一个项目模型。启用工作共享后,可以将Revit文档细分为工作集,这些工作集是项目中图元的集合。

本节中的页面

  • 工作共享概述
  • 工作集
  • 工作集中的图元
  • 编辑工作集中的图元
  • 打开工作共享文档
  • 可见性和显示
  • 工作共享文件管理

工作共享概述

为Revit创建附加模块时,了解文档在工作共享环境中的功能非常重要。文件是本地文件、中心文件还是由Revit服务器管理的文件会影响对模型所做的更改对其他用户的影响,也会影响模型是否可能过期或工作集是否已被其他用户锁定。

工作流

这是从高层角度来看的工作共享工作流程。当用户打开中心模型时,他们将获得模型的本地副本。编辑图元时,图元将从中心模型中检出,这样其他人就无法编辑它们。仅当执行“带中心”的修改时,局部更改才会提交到中心模型。提交后,其他用户可以通过执行“最新更新”来获取更改。

工作集

图元放置在工作集中。并且可以检出整个工作集,以便用户对工作集中的所有图元具有独占编辑权限。如果添加了新图元,它们将放置在本地模型的活动工作集中。

特定的工作集可以与模型一起打开。只有打开的工作集可见,但模型中的所有图元都可用。工作共享模型也可以是开放的“分离”模型,在这种情况下,不可能更新中心模型。在这种情况下,不需要工作集管理。

工作共享类型

工作共享有两种类型:

  • File-based- 可通过网络访问磁盘上的中心模型
  • Server-based- Revit服务器管理中心模型和可能本地可用的加速器

工作集

工作集是将Revit文档中的一组图元划分为工作共享子集的一种方法。文档中可能有一个或多个工作集。

在文档中添加工作集

该文档包含一个WorksetTable,该表包含对该文档中包含的所有工作集的引用。每个文档都有一个WorksetTable。即使文档中未启用工作共享,表中也至少有一个默认工作集。IsWorkshared属性可用于确定文档中是否已启用工作共享。WorksetTable类可用于获取活动工作集(如下例所示),并通过调用SetActiveWorksetId()来设置活动工作集

代码区域:获取活动工作集

1
2
3
4
5
6
7
8
9
10
public Workset GetActiveWorkset(Document doc)
{
// Get the workset table from the document
WorksetTable worksetTable = doc.GetWorksetTable();
// Get the Id of the active workset
WorksetId activeId = worksetTable.GetActiveWorksetId();
// Find the workset with that Id
Workset workset = worksetTable.GetWorkset(activeId);
return workset;
}

过滤工作集

由于Workset类不是从Element派生的,因此可以使用FilteredWorksetCollector在一组工作集中进行搜索、筛选和重命名。可以指定条件来过滤返回的工作集。如果未应用任何条件,则此过滤器将访问文档中的所有工作集。WorksetKind枚举器对于筛选工作集非常有用,如下例所示。WorksetKind标识工作集的细分:

  • User- 用户管理的三维实例图元工作集
  • Family- 保存族符号和族的位置
  • Standard- 项目标准所在的位置,包括系统族类型
  • Other- 通常不应被应用程序考虑的内部使用的工作集
  • View- 包含视图和特定于视图的元素

代码区域:过滤工作集

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
public void GetWorksetsInfo(Document doc)
{
String message = String.Empty;
// Enumerating worksets in a document and getting basic information for each
FilteredWorksetCollector collector = new FilteredWorksetCollector(doc);

// find all user worksets
collector.OfKind(WorksetKind.UserWorkset);
IList worksets = collector.ToWorksets();

// get information for each workset
int count = 3; // show info for 3 worksets only
foreach (Workset workset in worksets)
{
message += "Workset : " + workset.Name;
message += "\nUnique Id : " + workset.UniqueId;
message += "\nOwner : " + workset.Owner;
message += "\nKind : " + workset.Kind;
message += "\nIs default : " + workset.IsDefaultWorkset;
message += "\nIs editable : " + workset.IsEditable;
message += "\nIs open : " + workset.IsOpen;
message += "\nIs visible by default : " + workset.IsVisibleByDefault;

TaskDialog.Show("GetWorksetsInfo", message);

if (0 == --count)
break;
}
}

工作集属性

Workset类表示Revit文档中的工作集。如上面的过滤工作集示例所示,Workset类提供了许多属性来获取有关给定工作集的信息,例如所有者以及工作集是否可编辑。这些属性是只读的。若要更改现有工作集的名称,请使用静态方法WorksetTable.RenameWorkset()。

创建工作集

静态Workset.Create()方法可用于在给定文档中创建具有指定名称的新工作集。只能在启用了工作共享的文档中创建工作集,并且名称必须唯一。静态方法WorksetTable.IsWorksetNameUnique()将确认给定的名称在文档中是否唯一。下面的示例演示如何创建新工作集。

代码区域:创建新工作集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public Workset CreateWorkset(Document document)
{
Workset newWorkset = null;
// Worksets can only be created in a document with worksharing enabled
if (document.IsWorkshared)
{
string worksetName = "New Workset";
// Workset name must not be in use by another workset
if (WorksetTable.IsWorksetNameUnique(document, worksetName))
{
using (Transaction worksetTransaction = new Transaction(document, "Set preview view id"))
{
worksetTransaction.Start();
newWorkset = Workset.Create(document, worksetName);
worksetTransaction.Commit();
}
}
}

return newWorkset;
}

工作集中的图元

文档中的每个图元必须属于且只能属于一个工作集。每个元素都有一个WorksetId,用于标识该元素所属的唯一工作集。此外,给定WorksetId,可以使用ElementWorksetFilter获取文档中属于该Workset的所有元素,如下所示。

代码区域:ElementWorksetFilter

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
public void WorksetElements(Document doc, Workset workset)
{
// filter all elements that belong to the given workset
FilteredElementCollector elementCollector = new FilteredElementCollector(doc);
ElementWorksetFilter elementWorksetFilter = new ElementWorksetFilter(workset.Id, false);
ICollection worksetElemsfounds = elementCollector.WherePasses(elementWorksetFilter).ToElements();

// how many elements were found?
int elementsCount = worksetElemsfounds.Count;
String message = "Element count : " + elementsCount;

// Get name and/or Id of the elements that pass the given filter and show a few of them
int count = 5; // show info for 5 elements only
foreach (Element ele in worksetElemsfounds)
{
if (null != ele)
{
message += "\nElementId : " + ele.Id;
message += ", Element Name : " + ele.Name;

if (0 == --count)
break;
}
}

Autodesk.Revit.UI.TaskDialog.Show("ElementsOfWorkset", message);
}

新元素将自动放置在用户的模型本地副本中的活动工作集中。由于元素的WorksetId是只读属性,因此请使用参数ELEM_PARTITION_PARAM。下面的示例演示如何创建已更改为属于其他工作集的图元。

代码区域:更改新元素的工作集

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
Document doc = commandData.View.Document;
String targetWorksetName = "Target workset";

//Find target workset
FilteredWorksetCollector worksetCollector = new FilteredWorksetCollector(doc);
worksetCollector.OfKind(WorksetKind.UserWorkset);
Workset workset = worksetCollector.FirstOrDefault(ws => ws.Name == targetWorksetName);

// Workset not found, abort
if (workset == null)
{
TaskDialog dialog = new TaskDialog("Error");
dialog.MainInstruction = String.Format("There is no workset named {0} in the document. Aborting this operation.", targetWorksetName);
dialog.MainIcon = TaskDialogIcon.TaskDialogIconWarning;
dialog.Show();
return Result.Cancelled;
}

// Find "Level 1" for the new wall
FilteredElementCollector collector = new FilteredElementCollector(doc);
collector.OfClass(typeof(Level));
Level level = collector.Cast().First(lvl => lvl.Name == "Level 1");

using (Transaction t = new Transaction(doc, "Add elements by API"))
{
t.Start();

// Create the wall
Wall wall = Wall.Create(doc, Line.CreateBound(new XYZ(25, 0, 0), new XYZ(25, 15, 0)), level.Id, false);

// Get the parameter that stores the workset id
Parameter p = wall.get_Parameter(BuiltInParameter.ELEM_PARTITION_PARAM);

// This parameter storage type is Integer
p.Set(workset.Id.IntegerValue);

t.Commit();
}

可以使用WorksharingUtils类获取元素的当前所有者和签出状态等工作共享信息。它是一个静态类,包含与工作共享相关的实用程序函数。

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
public void GetElementWorksharingInfo(Document doc, ElementId elemId)
{
String message = String.Empty;
message += "Element Id: " + elemId;

Element elem = doc.GetElement(elemId);
if(null == elem)
{
message += "Element does not exist";
return;
}

// The workset the element belongs to
WorksetId worksetId = elem.WorksetId;
message += ("\nWorkset Id : " + worksetId.ToString());

// Model Updates Status of the element
ModelUpdatesStatus updateStatus = WorksharingUtils.GetModelUpdatesStatus(doc, elemId);
message += ("\nUpdate status : " + updateStatus.ToString());

// Checkout Status of the element
CheckoutStatus checkoutStatus = WorksharingUtils.GetCheckoutStatus(doc, elemId);
message += ("\nCheckout status : " + checkoutStatus.ToString());

// Getting WorksharingTooltipInfo of a given element Id
WorksharingTooltipInfo tooltipInfo = WorksharingUtils.GetWorksharingTooltipInfo(doc, elemId);
message += ("\nCreator : " + tooltipInfo.Creator);
message += ("\nCurrent Owner : " + tooltipInfo.Owner);
message += ("\nLast Changed by : " + tooltipInfo.LastChangedBy);

Autodesk.Revit.UI.TaskDialog.Show("GetElementWorksharingInfo", message);
}

编辑工作集中的图元

Overview

在团队中工作的用户可能会遇到Revit API附加模块的可用性问题,而不是单个用户可能遇到的问题。特别是,外接程序的设计方式可以防止或创建编辑冲突。例如,如果加载项尝试编辑数千个元素,则所有这些元素都需要由本地用户检出,并且在执行与中心的同步之前,其他用户将无法使用这些元素。或者,某些元素可能已被其他用户检出,无法进行编辑。在通过API对工作共享模型进行更改时,请务必记住这一点。

基本的模型编辑工作流程如下所示:

Action 行动 Example 例如 Why this is important 为什么这很重要
用户更改模型中的某些元素 用户拖动墙 这些变化是“用户变化”。用户必须借用这些元素来进行更改。
Revit会根据需要重新生成模型中的其他数据 连接墙移动,地板更新,屋顶更新,房间更新,房间标记检查他们是否仍在房间中 这些变化就是“系统变化”。即使它们被更改,它们仍然对其他用户可用。

大多数API更改都是“用户更改”,并被视为本地用户手动进行的更改。无论是从外部命令、宏还是事件调用,都是如此。唯一的例外是,从更新程序进行的更改被视为系统更改。要解决在尝试编辑工作共享文档中的元素时可能出现的工作共享问题,一种方法是为用于编辑元素的事务设置FailureHandlingOptions。这允许自动捕获和抑制编辑错误并回滚更改,如下所示:

代码区域:取消工作共享错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void TryToEditGeometricElement(Element elem, bool useFailureHandlingOpts)
{
Document doc = elem.Document;
using (Transaction t = MakeTransaction(doc, "Move element", useFailureHandlingOpts))
{
t.Start();
ElementTransformUtils.MoveElement(doc, elem.Id, new XYZ(1, 1, 0));
t.Commit();
}
}

private static Transaction MakeTransaction(Document doc, string name, bool useFailureHandlingOpts)
{
Transaction t = new Transaction(doc, name);
if (useFailureHandlingOpts)
{
FailureHandlingOptions opts = t.GetFailureHandlingOptions();
opts.SetClearAfterRollback(true);
opts.SetFailuresPreprocessor(new WorksharingErrorsPreprocessor());
t.SetFailureHandlingOptions(opts);
}

return t;
}

WorksharingUtils类可用于修改图元和工作集的所有权。CheckoutElements()方法为当前用户获取尽可能多的指定图元的所有权,而CheckoutWorksets()方法对工作集执行相同的操作。这些方法对于尝试在执行编辑之前检出图元非常有用。RelinquishOwnership()方法根据指定的RelinquishOptions放弃当前用户拥有的图元和工作集。 为获得最佳性能,请在一次大调用中检出所有图元或工作集并放弃项目,而不是在许多次小调用中。

注意:检出图元时,Revit可能会检出使请求的图元可编辑所需的其他图元。例如,如果某个图元位于组中,则Revit将检出整个组。 下面的示例尝试在编辑之前签出给定的元素,并在出现问题时向用户发出消息。

代码区域:签出元素

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
public static bool AttemptToCheckoutInAdvance(Element element)
{
Document doc = element.Document;
String categoryName = element.Category.Name;

// Checkout attempt
ICollection checkedOutIds = WorksharingUtils.CheckoutElements(doc, new ElementId[] { element.Id });

// Confirm checkout
bool checkedOutSuccessfully = checkedOutIds.Contains(element.Id);

if (!checkedOutSuccessfully)
{
TaskDialog.Show("Element is not checked out", "Cannot edit the " + categoryName + " element - " +
"it was not checked out successfully and may be checked out to another.");
return false;
}

// If element is updated in central or deleted in central, it is not editable
ModelUpdatesStatus updatesStatus = WorksharingUtils.GetModelUpdatesStatus(doc, element.Id);
if (updatesStatus == ModelUpdatesStatus.DeletedInCentral || updatesStatus == ModelUpdatesStatus.UpdatedInCentral)
{
TaskDialog.Show("Element is not up to date", "Cannot edit the " + categoryName + " element - " +
"it is not up to date with central, but it is checked out.");
return false;
}

return true;
}

下一个示例演示如何检出所有视图工作集。

1
2
3
4
5
6
7
8
9
10
void CheckoutAllViewWorksets(Document doc)
{
FilteredWorksetCollector collector = new FilteredWorksetCollector(doc);

// find all view worksets
collector.OfKind(WorksetKind.ViewWorkset);
ICollection viewworksets = collector.ToWorksetIds();
ICollection checkoutworksets = WorksharingUtils.CheckoutWorksets(doc, viewworksets);
TaskDialog.Show("Checked out worksets", "Number of worksets checked out: " + checkoutworksets.Count);
}

打开工作共享文档

Application.OpenDocumentFile(ModelPath,OpenOptions)方法可用于设置与打开工作共享文档相关的选项。除了从中心文档拆离或允许本地文件由其所有者以外的用户以只读方式打开的选项外,还可以设置与工作集相关的选项。打开工作共享文档时,将自动打开所有系统工作集,但是可以指定打开或关闭用户创建的工作集。可以展开和显示打开的工作集中的图元。但是,关闭的工作集中的图元不会显示,以避免展开它们。通过仅打开当前任务中所需的工作集,可以减少Revit的内存占用量,从而有助于提高性能。

在下面的示例中,打开了一个文档,其中指定了两个要打开的工作集。请注意,WorksharingUtils.GetUserWorksetInfo()方法可用于访问关闭的Revit文档中的工作集信息。

代码区域:打开工作共享文档

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
Document OpenDocumentWithWorksets(Application app, ModelPath projectPath)
{
Document doc = null;
try
{
// Get info on all the user worksets in the project prior to opening
IList worksets = WorksharingUtils.GetUserWorksetInfo(projectPath);
IList worksetIds = new List();
// Find two predetermined worksets
foreach (WorksetPreview worksetPrev in worksets)
{
if (worksetPrev.Name.CompareTo("Workset1") == 0 ||
worksetPrev.Name.CompareTo("Workset2") == 0)
{
worksetIds.Add(worksetPrev.Id);
}
}

OpenOptions openOptions = new OpenOptions();
// Setup config to close all worksets by default
WorksetConfiguration openConfig = new WorksetConfiguration(WorksetConfigurationOption.CloseAllWorksets);
// Set list of worksets for opening
openConfig.Open(worksetIds);
openOptions.SetOpenWorksetsConfiguration(openConfig);
doc = app.OpenDocumentFile(projectPath, openOptions);
}
catch (Exception e)
{
TaskDialog.Show("Open File Failed", e.Message);
}

return doc;
}

另一个选项是在打开文档时仅打开上次查看的工作集。

代码区域:打开上次查看的工作集

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
public static Document OpenLastViewed(UIApplication uiApplication)
{
Application application = uiApplication.Application;

// Setup options
OpenOptions options1 = new OpenOptions();

// Default config opens all. Close all first, then open last viewed to get the correct settings.
WorksetConfiguration worksetConfig = new WorksetConfiguration(WorksetConfigurationOption.OpenLastViewed);
options1.SetOpenWorksetsConfiguration(worksetConfig);

// Open the document
Document openedDoc = application.OpenDocumentFile(GetWSAPIModelPath("WorkaredFileSample.rvt"), options1);

return openedDoc;
}

private static ModelPath GetWSAPIModelPath(string fileName)
{
// Utility to get a local path for a target model file
FileInfo filePath = new FileInfo(Path.Combine(@"C:\Documents\Revit Projects", fileName));
ModelPath mp = ModelPathUtils.ConvertUserVisiblePathToModelPath(filePath.FullName);

return mp;
}

以下两个示例演示了如何首先从磁盘或Revit服务器创建新的本地,然后将其打开。请注意,下面的示例使用了上一个示例中使用的GetWSAPIModelPath()方法。

代码区域:从磁盘打开新本地

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
public static Document OpenNewLocalFromDisk(UIApplication uiApplication)
{
// Create new local from a disk location
ModelPath newLocalPath = GetWSAPIModelPath("LocalWorksharing.rvt");
return (OpenNewLocalFromModelPath(uiApplication.Application, GetWSAPIModelPath("NewLocalWorksharing.rvt"), newLocalPath));
}

private static Document OpenNewLocalFromModelPath(Application app, ModelPath centralPath, ModelPath localPath)
{
// Create the new local at the given path
WorksharingUtils.CreateNewLocal(centralPath, localPath);

// Select specific worksets to open
// First get a list of worksets from the unopened document
IList worksets = WorksharingUtils.GetUserWorksetInfo(localPath);
List worksetsToOpen = new List();

foreach (WorksetPreview preview in worksets)
{
// Match worksets to open with criteria
if (preview.Name.StartsWith("O"))
worksetsToOpen.Add(preview.Id);
}

// Setup option to open the target worksets
// First close all, then set specific ones to open
WorksetConfiguration worksetConfig = new WorksetConfiguration(WorksetConfigurationOption.CloseAllWorksets);
worksetConfig.Open(worksetsToOpen);

// Open the new local
OpenOptions options1 = new OpenOptions();
options1.SetOpenWorksetsConfiguration(worksetConfig);
Document openedDoc = app.OpenDocumentFile(localPath, options1);

return openedDoc;
}

下面的示例使用作为上一示例的一部分演示的OpenNewLocalFromModelPath()方法。

代码区域:从Revit Server打开新的本地

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
/// 
/// Get the server path for a particular model and open a new local copy
///
public static Document OpenNewLocalFromServer(UIApplication uiApp)
{
// Create new local from a server location
Application app = uiApp.Application;

// Get the host id/IP of the server
String hostId = app.GetRevitServerNetworkHosts().First();

// try to get the server path for the particular model on the server
String rootFolder = "|";
ModelPath serverPath = FindWSAPIModelPathOnServer(app, hostId, rootFolder, "WorksharingOnServer.rvt");

ModelPath newLocalPath = GetWSAPIModelPath("WorksharingLocalFromServer.rvt");
return (OpenNewLocalFromModelPath(uiApp.Application, serverPath, newLocalPath));
}

///
/// Uses the Revit Server REST API to recursively search the folders of the Revit Server for a particular model.
///
private static ModelPath FindWSAPIModelPathOnServer(Application app, string hostId, string folderName, string fileName)
{
// Connect to host to find list of available models (the "/contents" flag)
XmlDictionaryReader reader = GetResponse(app, hostId, folderName + "/contents");
bool found = false;

// Look for the target model name in top level folder
List folders = new List();
while (reader.Read())
{
// Save a list of subfolders, if found
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Folders")
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Folders")
break;

if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name")
{
reader.Read();
folders.Add(reader.Value);
}
}
}
// Check for a matching model at this folder level
if (reader.NodeType == XmlNodeType.Element && reader.Name == "Models")
{
found = FindModelInServerResponseJson(reader, fileName);
if (found)
break;
}
}

reader.Close();

// Build the model path to match the found model on the server
if (found)
{
// Server URLs use "|" for folder separation, Revit API uses "/"
String folderNameFragment = folderName.Replace('|', '/');

// Add trailing "/" if not present
if (!folderNameFragment.EndsWith("/"))
folderNameFragment += "/";

// Build server path
ModelPath modelPath = new ServerPath(hostId, folderNameFragment + fileName);
return modelPath;
}
else
{
// Try subfolders
foreach (String folder in folders)
{
ModelPath modelPath = FindWSAPIModelPathOnServer(app, hostId, folder, fileName);
if (modelPath != null)
return modelPath;
}
}

return null;
}

// This string is different for each RevitServer version
private static string s_revitServerVersion = "/RevitServerAdminRESTService2014/AdminRESTService.svc/";

///
/// Connect to server to get list of available models and return server response
///
private static XmlDictionaryReader GetResponse(Application app, string hostId, string info)
{
// Create request WebRequest request = WebRequest.Create("http://" + hostId + s_revitServerVersion + info);
request.Method = "GET";

// Add the information the request needs

request.Headers.Add("User-Name", app.Username);
request.Headers.Add("User-Machine-Name", app.Username);
request.Headers.Add("Operation-GUID", Guid.NewGuid().ToString());

// Read the response
XmlDictionaryReaderQuotas quotas =
new XmlDictionaryReaderQuotas();
XmlDictionaryReader jsonReader =
JsonReaderWriterFactory.CreateJsonReader(request.GetResponse().GetResponseStream(), quotas);

return jsonReader;
}

///
/// Read through server response to find particular model
///
private static bool FindModelInServerResponseJson(XmlDictionaryReader reader, string fileName)
{
// Read through entries in this section
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "Models")
break;

if (reader.NodeType == XmlNodeType.Element && reader.Name == "Name")
{
reader.Read();
String modelName = reader.Value;
if (modelName.Equals(fileName))
{
// Match found, stop looping and return
return true;
}
}
}

return false;
}

从中央分离开

如果附加模块将处理工作共享文件,但不需要进行永久更改,则可以打开与中心文件分离的模型。

代码区域:打开分离

1
2
3
4
5
6
7
8
9
private static Document OpenDetached(Application application, ModelPath modelPath)
{
OpenOptions options1 = new OpenOptions();

options1.DetachFromCentralOption = DetachFromCentralOption.DetachAndDiscardWorksets;
Document openedDoc = application.OpenDocumentFile(modelPath, options1);

return openedDoc;
}

如果应用程序只需要对服务器文件进行只读访问,下面的示例演示了如何在本地复制服务器模型并分离地打开它。请注意,此代码示例重用了前面示例中演示的方法。

代码区域:复制并打开分离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static Document CopyAndOpenDetached(UIApplication uiApp)
{
// Copy a server model locally and open detached
Application application = uiApp.Application;
String hostId = application.GetRevitServerNetworkHosts().First();

// Try to get the server path for the particular model on the server
String rootFolder = "|";
ModelPath serverPath = FindWSAPIModelPathOnServer(application, hostId, rootFolder, "ServerModel.rvt");

// For debugging
String sourcePath = ModelPathUtils.ConvertModelPathToUserVisiblePath(serverPath);

// Setup the target location for the copy
ModelPath localPath = GetWSAPIModelPath("CopiedModel.rvt");

// Copy, allowing overwrite
application.CopyModel(serverPath, ModelPathUtils.ConvertModelPathToUserVisiblePath(localPath), true);

// Open the copy as detached
Document openedDoc = OpenDetached(application, localPath);

return openedDoc;
}

可见性和显示

可见性

可以使用View.SetWorksetVisibility()为特定视图设置工作集的可见性。“工作集可见性”(WorksetVisibility)选项为“可见”(Visible)(如果工作集处于打开状态,则可见)、“隐藏”(Hidden)和“全局设置”(GlobalSetting)(表示不替代视图的设置)。相应的View.GetWorksetVisibility()方法检索该视图中工作集的当前可见性设置。但是,此方法不考虑工作集当前是否处于打开状态。若要确定某个工作集在视图中是否可见(包括考虑该工作集是打开还是关闭),请使用View.IsWorksetVisible()。

WorksetDefaultVisibilitySettings类管理文档中工作集的默认可见性。它不适用于家庭文件。如果在文档中禁用了工作共享,则所有图元都将移动到一个工作集中;默认情况下,无论当前设置如何,该工作集以及在重新启用工作共享时(重新)创建的任何工作集都是可见的。

下面的示例在给定视图中隐藏工作集,并在默认情况下在其他视图中隐藏该工作集。

代码区域:隐藏工作集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void HideWorkset(Document doc, View view, WorksetId worksetId)
{
// get the current visibility
WorksetVisibility visibility = view.GetWorksetVisibility(worksetId);

// and set it to 'Hidden' if it is not hidden yet
if (visibility != WorksetVisibility.Hidden)
{
view.SetWorksetVisibility(worksetId, WorksetVisibility.Hidden);
}

// Get the workset’s default visibility WorksetDefaultVisibilitySettings defaultVisibility = WorksetDefaultVisibilitySettings.GetWorksetDefaultVisibilitySettings(doc);

// and making sure it is set to 'false'
if (true == defaultVisibility.IsWorksetVisible(worksetId))
{
defaultVisibility.SetWorksetVisibility(worksetId, false);
}
}

显示模式

除了获取和设置有关工作集可见性的信息外,View类还提供了访问有关工作共享显示模式和设置的信息的方法。WorksharingDisplayMode枚举指示视图所处的模式(如果有):

Member Name 成员名称 Description 描述
Off 无活动的工作共享显示模式。
CheckoutStatus 视图显示元素的签出状态。
Owners 视图显示元素的特定所有者。
ModelUpdates 视图正在显示模型更新。
Worksets 视图显示每个图元指定给的工作集。

代码区域:激活不同的工作共享显示模式

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
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
View activeView = commandData.View;
Document doc = activeView.Document;

// Prepare settings
Color red = new Color(0xFF, 0x00, 0x00);
WorksharingDisplayGraphicSettings settingsToApply = new WorksharingDisplayGraphicSettings(true, red);

// Toggle mode based on the current mode
using (Transaction t = new Transaction(doc, "Toggle display mode"))
{
t.Start();

WorksharingDisplaySettings settings = WorksharingDisplaySettings.GetOrCreateWorksharingDisplaySettings(doc);

switch (activeView.GetWorksharingDisplayMode())
{
case WorksharingDisplayMode.Off:
activeView.SetWorksharingDisplayMode(WorksharingDisplayMode.CheckoutStatus);
settings.SetGraphicOverrides(CheckoutStatus.OwnedByOtherUser, settingsToApply);
break;
case WorksharingDisplayMode.CheckoutStatus:
activeView.SetWorksharingDisplayMode(WorksharingDisplayMode.ModelUpdates);
settings.SetGraphicOverrides(ModelUpdatesStatus.UpdatedInCentral, settingsToApply);
break;
case WorksharingDisplayMode.ModelUpdates:
activeView.SetWorksharingDisplayMode(WorksharingDisplayMode.Owners);
settings.SetGraphicOverrides("Target user", settingsToApply);
break;
case WorksharingDisplayMode.Owners:
activeView.SetWorksharingDisplayMode(WorksharingDisplayMode.Worksets);
settings.SetGraphicOverrides(doc.GetWorksetTable().GetActiveWorksetId(), settingsToApply);
break;
case WorksharingDisplayMode.Worksets:
activeView.SetWorksharingDisplayMode(WorksharingDisplayMode.Off);
break;
}

t.Commit();
}

return Result.Succeeded;
}

图形设置

WorksharingDisplaySettings类控制元素在任何工作共享显示模式下显示时的显示方式。存储在这些设置中的颜色是通用设置,由模型中的所有用户共享。是否应用给定的颜色是特定于当前用户的,不会被其他用户共享。请注意,即使在未工作共享的模型中,这些设置也可用。这是为了允许在启用工作集之前预配置显示设置,以便可以将它们存储在样板文件中。

代码区域:工作共享显示图形设置

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
public WorksharingDisplayGraphicSettings GetWorksharingDisplaySettings(Document doc, String userName, WorksetId worksetId, bool ownedbyCurrentUser)
{
WorksharingDisplayGraphicSettings graphicSettings;

// get or create a WorksharingDisplaySettings current active document
WorksharingDisplaySettings displaySettings = WorksharingDisplaySettings.GetOrCreateWorksharingDisplaySettings(doc);

// get graphic settings for a user, if specified
if (!String.IsNullOrEmpty(userName))
graphicSettings = displaySettings.GetGraphicOverrides(userName);

// get graphicSettings for a workset, if specified
else if (worksetId != WorksetId.InvalidWorksetId)
graphicSettings = displaySettings.GetGraphicOverrides(worksetId);

// get graphic settings for the OwnedByCurrentUser status
else if (ownedbyCurrentUser)
graphicSettings = displaySettings.GetGraphicOverrides(CheckoutStatus.OwnedByCurrentUser);

// otherwise get graphic settings for the CurrentWithCentral status
else
graphicSettings = displaySettings.GetGraphicOverrides(ModelUpdatesStatus.CurrentWithCentral);

return graphicSettings;
}

重载方法WorksharingDisplaySettings.SetGraphicOverrides()根据给定条件设置分配给元素的图形替代。

代码区域:图形覆盖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void SetWorksharingDisplaySettings(Document doc, WorksetId worksetId, String userName)
{
String message = String.Empty;

// get or create a WorksharingDisplaySettings current active document
WorksharingDisplaySettings displaySettings = WorksharingDisplaySettings.GetOrCreateWorksharingDisplaySettings(doc);

// set a new graphicSettings for CheckoutStatus - NotOwned
WorksharingDisplayGraphicSettings graphicSettings = new WorksharingDisplayGraphicSettings(true, new Color(255, 0, 0));
displaySettings.SetGraphicOverrides(CheckoutStatus.NotOwned, graphicSettings);

// set a new graphicSettings for ModelUpdatesStatus - CurrentWithCentral
graphicSettings = new WorksharingDisplayGraphicSettings(true, new Color(128, 128, 0));
displaySettings.SetGraphicOverrides(ModelUpdatesStatus.CurrentWithCentral, graphicSettings);

// set a new graphicSettings by a given userName
graphicSettings = new WorksharingDisplayGraphicSettings(true, new Color(0, 255, 0));
displaySettings.SetGraphicOverrides(userName, graphicSettings);

// set a new graphicSettings by a given workset Id
graphicSettings = new WorksharingDisplayGraphicSettings(true, new Color(0, 0, 255));
displaySettings.SetGraphicOverrides(worksetId, graphicSettings);
}

WorksharingDisplaySettings类还可用于控制在文档的显示用户中列出哪些用户。RemoveUsers()方法从显示的用户列表中删除用户,并永久放弃对图形的任何自定义。只有在文档中不拥有任何元素的用户才能被删除。RestoreUsers()方法将删除的用户添加回显示的用户列表,并允许为这些用户定制图形。请注意,任何恢复的用户都将显示默认的图形覆盖,并且删除用户之前存在的任何自定义设置都不会恢复。

代码区域:删除用户

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
public void RemoveAndRestoreUsers(Document doc)
{
// get or create a WorksharingDisplaySettings current active document
WorksharingDisplaySettings displaySettings = WorksharingDisplaySettings.GetOrCreateWorksharingDisplaySettings(doc);

// get all users with GraphicOverrides
ICollection users = displaySettings.GetAllUsersWithGraphicOverrides();

// remove the users from the display settings (they will not have graphic overrides anymore)
ICollection outUserList;
displaySettings.RemoveUsers(doc, users, out outUserList);

// show the current list of removed users
ICollection removedUsers = displaySettings.GetRemovedUsers();

String message = "Current list of removed users: ";
if (removedUsers.Count > 0 )
{
foreach (String user in removedUsers)
{
message += "\n" + user;
}
}
else
{
message = "[Empty]";
}

TaskDialog.Show("Users Removed", message);

// restore the previously removed users
int number = displaySettings.RestoreUsers(outUserList);

// again, show the current list of removed users
// it should not contain the users that were restored
removedUsers = displaySettings.GetRemovedUsers();

message = "Current list of removed users: ";
if (removedUsers.Count > 0 )
{
foreach (String user in removedUsers)
{
message += "\n" + user;
}
}
else
{
message = "[Empty]";
}

TaskDialog.Show("Removed Users Restored", message);
}

工作共享文件管理

有几种文档方法可用于工作共享项目文件。

启用工作共享

如果文档尚未进行工作共享(这可以通过Document.IsWorkshared属性确定),则可以使用Document.EnableWorksharing()方法通过Revit API启用工作共享。此命令将清除文档的撤消历史记录,因此此命令和在此之前执行的其他命令无法撤消。此外,所有显式启动的事务阶段(例如事务、事务组和子事务)必须在调用EnableWorksharing()之前完成。

重新载入最新

方法Document. BulladLatest()从中心模型中检索更改(由于与中心模型进行了一次或多次同步),并将这些更改合并到当前会话中。

下面的示例在与用户确认可以更新当前会话后,使用PwadLatest()更新当前会话。

代码区域:从Central重新加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void ReloadLatestWithMessage(Document doc)
{
// Tell user what we're doing
TaskDialog td = new TaskDialog("Alert");
td.MainInstruction = "Application 'Automatic element creator' needs to reload changes from central in order to proceed.";
td.MainContent = "This will update your local with all changes currently in the central model. This operation " +
"may take some time depending on the number of changes available on the central.";
td.CommonButtons = TaskDialogCommonButtons.Ok | TaskDialogCommonButtons.Cancel;

TaskDialogResult result = td.Show();

if (result == TaskDialogResult.Ok)
{
// There are no currently customizable user options for ReloadLatest.
doc.ReloadLatest(new ReloadLatestOptions());
TaskDialog.Show("Proceeding...", "Reload operation completed, proceeding with updates.");
}
else
{
TaskDialog.Show("Canceled.", "Reload operation canceled, so changes will not be made. Return to this command later when ready to reload.");
}
}

与中心模型同步

方法Document.SynchronizeWithCentral()会重新加载中心模型中的所有更改,以便当前会话保持最新,然后将本地更改保存回中心。即使未进行任何更改,也会执行保存到中心文件。

使用SynchronizeWithCentral()时,可以指定用于访问中心模型以及与中心模型同步的选项。访问中心模型的主要选项是确定在中心模型被锁定时调用的行为方式。由于同步需要临时锁定中心模型,因此如果模型已锁定,则无法执行同步。默认行为是等待并反复尝试锁定中心模型,以便继续同步。可以使用SynchronizeWithCentral()方法的TransactWithCentralOptions参数重写此行为。

该方法的SynchronizeWithCentralOptions参数用于设置实际同步的选项,例如在同步过程中是否应放弃当前用户拥有的图元或工作集。

在下面的示例中,尝试与中心模型同步。如果中心模型被锁定,它将立即放弃。

代码区域:Cumberwith Central

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
public void SyncWithoutRelinquishing(Document doc)
{
// Set options for accessing central model
TransactWithCentralOptions transOpts = new TransactWithCentralOptions();
SynchLockCallback transCallBack = new SynchLockCallback();
// Override default behavior of waiting to try again if the central model is locked
transOpts.SetLockCallback(transCallBack);

// Set options for synchronizing with central
SynchronizeWithCentralOptions syncOpts = new SynchronizeWithCentralOptions();
// Sync without relinquishing any checked out elements or worksets
RelinquishOptions relinquishOpts = new RelinquishOptions(false);
syncOpts.SetRelinquishOptions(relinquishOpts);
// Do not automatically save local model after sync
syncOpts.SaveLocalAfter = false;
syncOpts.Comment = "Changes to Workset1";

try
{
doc.SynchronizeWithCentral(transOpts, syncOpts);
}
catch (Exception e)
{
TaskDialog.Show("Synchronize Failed", e.Message);
}
}

class SynchLockCallback : ICentralLockedCallback
{
// If unable to lock central, give up rather than waiting
public bool ShouldWaitForLockAvailability()
{
return false;
}

}

在下一个示例中,用户在同步之前会收到一条消息,并可以选择在同步时是放弃所有图元,还是保持工作集的检出状态。

代码区域:带消息同步中心

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
public static void SynchWithCentralWithMessage(Document doc)
{
// Checkout workset (for use with "keep checked out worksets" option later)
FilteredWorksetCollector fwc = new FilteredWorksetCollector(doc);
fwc.OfKind(WorksetKind.UserWorkset);
Workset workset1 = fwc.First(ws => ws.Name == "Workset1");

WorksharingUtils.CheckoutWorksets(doc, new WorksetId[] { workset1.Id });

// Make a change
using (Transaction t = new Transaction(doc, "Add Level"))
{
t.Start();
Level.Create(doc, 100);
t.Commit();
}

// Tell user what we're doing
TaskDialog td = new TaskDialog("Alert");
td.MainInstruction = "Application 'Automatic element creator' has made changes and is prepared to synchronize with central.";
td.MainContent = "This will update central with all changes currently made in the project by the application or by the user. This operation " +
"may take some time depending on the number of changes made by the app and by the user.";

td.AddCommandLink(TaskDialogCommandLinkId.CommandLink1, "Do not synchronize at this time.");
td.AddCommandLink(TaskDialogCommandLinkId.CommandLink2, "Synchronize and relinquish all elements.");
td.AddCommandLink(TaskDialogCommandLinkId.CommandLink3, "Synchronize but keep checked out worksets.");
td.DefaultButton = TaskDialogResult.CommandLink1;

TaskDialogResult result = td.Show();

switch (result)
{
case TaskDialogResult.CommandLink1:
default:
{
// Do not synch. Nothing to do.
break;
}
case TaskDialogResult.CommandLink2:
case TaskDialogResult.CommandLink3:
{
// Prepare to synch
// TransactWithCentralOptions has to do with the behavior related to locked or busy central models.
// We'll use the default behavior.
TransactWithCentralOptions twcOpts = new TransactWithCentralOptions();

// Setup synch-with-central options (add a comment about our change)
SynchronizeWithCentralOptions swcOpts = new SynchronizeWithCentralOptions();
swcOpts.Comment = "Synchronized by 'Automatic element creator' with user acceptance.";

if (result == TaskDialogResult.CommandLink3)
{
// Setup relinquish options to keep user worksets checked out
RelinquishOptions rOptions = new RelinquishOptions(true);
rOptions.UserWorksets = false;
swcOpts.SetRelinquishOptions(rOptions);
}

doc.SynchronizeWithCentral(twcOpts, swcOpts);

break;
}
}
}

创建新的本地模型

WorksharingUtils. NETNewLocal()方法将中心模型复制到新的本地文件。此方法不打开新文件。有关创建新的本地模型并将其打开的示例,请参见打开工作共享文档

注:翻译自Revit API Developers Guide