EaBIM一直以来积极响应国家“十二五”推进建筑业信息化的号召,对建筑领域的信息技术开展深入技术交流和探讨!致力于打造“BIM-建筑师-生态技术”三位一体综合资源交流共享平台,希望为BIM与可持续设计理念及技术的普及做出微小的贡献!!!

EaBIM

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
查看: 2018|回复: 44
打印 上一主题 下一主题

启动一个Revit命令Launching a Revit Command

[复制链接]

1514

主题

7465

帖子

1万

积分

admin

Rank: 10Rank: 10Rank: 10Rank: 10Rank: 10Rank: 10Rank: 10Rank: 10Rank: 10Rank: 10

积分
12406

社区QQ达人

跳转到指定楼层
楼主
发表于 2014-1-9 11:41:57 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
One question that I frequently hear is how to launch a built-in Revit command programmatically.
We recently discussed a specific case related to this issue in some depth, how to close the active document and especially pointed out why this is risky, undesirable and not supported.
To close the active document, we used the SendKeys.SendWait method provided in the .NET framework System.Windows.Forms namespace. Many of the other attempts that I have seen in the past to launch a Revit command were also based on this method, but unsuccessful.
Here is now a solution that actually does work, suggested by Robert Pleysier ofALFA Development in the Netherlands. Instead of SendKeys, he uses the native Windows API PostMessage call to send the Windows messages WM_KEYDOWN and WM_KEYUP for each required keystroke to the Revit process main window handle.
The reason for Robert to launch a Revit command at all is the requirement to predefine which wall type should be active when the user enters the new wall creation command. Since there is no API method available to programmatically predefine the current wall type to be used by this command, Robert selects an existing wall with the desired type and then uses the 'Create Similar' command to set up its type as the new default user interface wall type. If no wall with the desired type exists yet in the model, a temporary wall is created and then deleted again.
Here is the question initially raised by Robert, and his own answer and solution:
Question: In the Revit API you can use the function PromptForFamilyInstancePlacement to place family symbols.
Is it possible to place a Wall with this function or another function from code?
I want to change the wall or other system family type by code in the property dialog.
After that I want to activate the Revit wall or stair function by code, so a user can draw his or her own wall or stair with the right wall or stair type.
Answer: I solved the issue by using a key press function "cs" for "create similar".
Based on Robert's example code showing how he did this, I implemented a new Building Code sample command CmdPressKey which illustrates his solution.
First of all, here is the helper class Press which accesses the Windows API methods and implements the public method Keys to actually send keystrokes using PostMessage. It requires the namespace System.Runtime.InteropServices:
public class Press{  [DllImport( "USER32.DLL" )]  public static extern bool PostMessage(    IntPtr hWnd, uint msg, uint wParam, uint lParam );   [DllImport( "user32.dll" )]  static extern uint MapVirtualKey(    uint uCode, uint uMapType );   enum WH_KEYBOARD_LPARAM : uint  {    KEYDOWN = 0x00000001,    KEYUP = 0xC0000001  }   enum KEYBOARD_MSG : uint  {    WM_KEYDOWN = 0x100,    WM_KEYUP = 0x101  }   enum MVK_MAP_TYPE : uint  {    VKEY_TO_SCANCODE = 0,    SCANCODE_TO_VKEY = 1,    VKEY_TO_CHAR = 2,    SCANCODE_TO_LR_VKEY = 3  }   ///
<summary>  /// Post one single keystroke.  ///
</summary>  static void OneKey( IntPtr handle, char letter )  {    uint scanCode = MapVirtualKey( letter,      ( uint ) MVK_MAP_TYPE.VKEY_TO_SCANCODE );     uint keyDownCode = ( uint )      WH_KEYBOARD_LPARAM.KEYDOWN      | ( scanCode << 16 );     uint keyUpCode = ( uint )      WH_KEYBOARD_LPARAM.KEYUP      | ( scanCode << 16 );     PostMessage( handle,      ( uint ) KEYBOARD_MSG.WM_KEYDOWN,      letter, keyDownCode );     PostMessage( handle,      ( uint ) KEYBOARD_MSG.WM_KEYUP,      letter, keyUpCode );  }   ///
<summary>  /// Post a sequence of keystrokes.  ///
</summary>  public static void Keys( string command )  {    IntPtr revitHandle = System.Diagnostics.Process      .GetCurrentProcess().MainWindowHandle;     foreach( char letter in command )    {      OneKey( revitHandle, letter );    }  }}
With this in place, we can go ahead and implement the external command. It requires two helper methods: GetFirstWallTypeNamed retrieves the appropriate wall type for a given wall type name, and GetFirstWallUsingType retrieves the first wall element encountered in the model making use of a given wall type. Both of these obviously use filtered element collectors, and both of them even use parameter filters. GetFirstWallTypeNamed uses the built-in parameter SYMBOL_NAME_PARAM and a string equality filter to do its job, i.e. to return the first wall type with the given name:
static WallType GetFirstWallTypeNamed(  Document doc,  string name ){  // built-in parameter storing this   // wall type's name:   BuiltInParameter bip    = BuiltInParameter.SYMBOL_NAME_PARAM;   ParameterValueProvider provider    = new ParameterValueProvider(      new ElementId( bip ) );   FilterStringRuleEvaluator evaluator    = new FilterStringEquals();   FilterRule rule = new FilterStringRule(    provider, evaluator, name, false );   ElementParameterFilter filter    = new ElementParameterFilter( rule );   FilteredElementCollector collector    = new FilteredElementCollector( doc )      .OfClass( typeof( WallType ) )      .WherePasses( filter );   return collector.FirstElement() as WallType;}
GetFirstWallUsingType uses the built-in parameter ELEM_TYPE_PARAM and a numerical equality filter to retrieve all walls using the required wall type, because their corresponding parameter value will equal the wall type element id. We don't care which wall is used to launch the "Create Similar" command, so we simply return the first one encountered:
static Wall GetFirstWallUsingType(  Document doc,  WallType wallType ){  // built-in parameter storing this   // wall's wall type element id:   BuiltInParameter bip    = BuiltInParameter.ELEM_TYPE_PARAM;   ParameterValueProvider provider    = new ParameterValueProvider(      new ElementId( bip ) );   FilterNumericRuleEvaluator evaluator    = new FilterNumericEquals();   FilterRule rule = new FilterElementIdRule(    provider, evaluator, wallType.Id );   ElementParameterFilter filter    = new ElementParameterFilter( rule );   FilteredElementCollector collector    = new FilteredElementCollector( doc )      .OfClass( typeof( Wall ) )      .WherePasses( filter );   return collector.FirstElement() as Wall;}
With the support class and helper methods in place, the external command mainline Execute method implementation is short and sweet:
public Result Execute(  ExternalCommandData commandData,  ref string message,  ElementSet elements ){  UIApplication uiapp = commandData.Application;  UIDocument uidoc = uiapp.ActiveUIDocument;  Application app = uiapp.Application;  Document doc = uidoc.Document;   // name of target wall type that we want to use:   string wallTypeName = "Generic - 203";   WallType wallType = GetFirstWallTypeNamed(    doc, wallTypeName );   Wall wall = GetFirstWallUsingType(    doc, wallType );   // select the wall in the UI   uidoc.Selection.Elements.Add( wall );   if( 0 == uidoc.Selection.Elements.Size )  {    // no wall with the correct wall type found     FilteredElementCollector collector      = new FilteredElementCollector( doc );     Level ll = collector      .OfClass( typeof( Level ) )      .FirstElement() as Level;     // place a new wall with the     // correct wall type in the project     Line geomLine = app.Create.NewLineBound(      XYZ.Zero, new XYZ( 2, 0, 0 ) );     Transaction t = new Transaction(      doc, "Create dummy wall" );     t.Start();     Wall nw = doc.Create.NewWall( geomLine,      wallType, ll, 1, 0, false, false );     t.Commit();     // Select the new wall in the project     uidoc.Selection.Elements.Add( nw );     // Start command create similar. In the     // property menu, our wall type is set current     Press.Keys( "CS" );     // select the new wall in the project,     // so we can delete it     uidoc.Selection.Elements.Add( nw );     // erase the selected wall (remark:     // doc.delete(nw) may not be used,     // this command will undo)     Press.Keys( "DE" );     // start up wall command     Press.Keys( "WA" );  }  else  {    // the correct wall is already selected:     Press.Keys( "CS" ); // start "create similar"  }  return Result.Succeeded;}
As you see, an arbitrary dummy wall of the required type is created if none previously exists. For this, we start up a transaction of our own, so we are obviously using manual transaction mode. And so we have to, since we have to close our transaction again before the Revit commands are invoked.
Robert says the following about this code: Here is a part or our code to start a Revit command. The aim of the code is to set a wall type current in the Revit property window. We only start up the wall command with the API and let the user do the drawing of the wall. This solution can also be used to launch other Revit commands.
When I start up the standard sample project rac_basic_sample_project.rvt, switch to Level 1, and launch this command from the Revit ribbon, I immediately enter the standard Revit wall command. The desired wall type is active, in this case the type named "Generic - 203", and I can immediately start placing new walls of that type.
There have been many other queries on how to programmatically set up the type before launching a Revit command, and this looks as if it could solve them.
I have to repeat the warning about the risks involved with using this, though, and also point back to the disclaimeraccompanying that warning.
Still, if this is just for your personal use, you might find it pretty handy.
Here is version 2011.0.80.0 of The Building Coder samples including the complete source code and Visual Studio solution with the new command.

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 转播转播 分享分享 分享淘帖 支持支持 反对反对
工作时间:工作日的9:00-12:00/13:30-18:00,节假日不在线,请勿留言

4

主题

845

帖子

1337

积分

BIM经理

Rank: 6Rank: 6Rank: 6Rank: 6Rank: 6Rank: 6

积分
1337
推荐
发表于 2014-2-25 10:31:22 | 只看该作者
路过!!! 不发表意见……

12

主题

854

帖子

1923

积分

BIM经理

Rank: 6Rank: 6Rank: 6Rank: 6Rank: 6Rank: 6

积分
1923
推荐
发表于 2014-4-23 10:42:25 | 只看该作者
路过!!! 不发表意见……

30

主题

905

帖子

1486

积分

BIM经理

Rank: 6Rank: 6Rank: 6Rank: 6Rank: 6Rank: 6

积分
1486
推荐
发表于 2014-4-8 10:33:45 | 只看该作者
路过!!! 不发表意见……

13

主题

840

帖子

1580

积分

BIM经理

Rank: 6Rank: 6Rank: 6Rank: 6Rank: 6Rank: 6

积分
1580
2F
发表于 2014-2-20 15:12:09 | 只看该作者
顶...... 楼下跟上.....

6

主题

870

帖子

1737

积分

BIM经理

Rank: 6Rank: 6Rank: 6Rank: 6Rank: 6Rank: 6

积分
1737
3F
发表于 2014-2-25 10:28:41 | 只看该作者
顶...... 楼下跟上.....

66

主题

1001

帖子

2354

积分

BIM顾问

Rank: 8Rank: 8Rank: 8Rank: 8Rank: 8Rank: 8Rank: 8Rank: 8

积分
2354

元老勋章特殊贡献勋章

7F
发表于 2014-3-10 10:48:12 | 只看该作者
顶...... 楼下跟上.....
官方主页:www.eabim.net
*滑块验证:
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|EaBIM网 ( 苏ICP备2020058923号-1  苏公网安备32011502011255号

GMT+8, 2024-11-27 05:24

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表