我正在尝试创建我的第一个Revit插件。
我正在使用Revit 2014,我想要的是放置一个从文件加载的系列的SINGLE实例。我实际上正在使用此代码:
[TransactionAttribute(TransactionMode.Manual)]
[RegenerationAttribute(RegenerationOption.Manual)]
public class InsertFamily : IExternalCommand
{
readonly List<ElementId> _addedElementIds = new List<ElementId>();
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements)
{
UIApplication uiApp = commandData.Application;
Document document = uiApp.ActiveUIDocument.Document;
FamilySymbol family = null;
bool good = false;
using (var trans = new Transaction(document, "inserting family"))
{
trans.Start();
good = document.LoadFamilySymbol(@"my file path.rfa", "my type", new FamilyLoadingOverwriteOption(), out family);
trans.Commit();
}
if (good && family != null)
{
_addedElementIds.Clear();
uiApp.Application.DocumentChanged += applicationOnDocumentChanged;
uiApp.ActiveUIDocument.PromptForFamilyInstancePlacement(family);
uiApp.Application.DocumentChanged -= applicationOnDocumentChanged;
}
return Result.Succeeded;
}
private void applicationOnDocumentChanged(object sender, DocumentChangedEventArgs documentChangedEventArgs)
{
_addedElementIds.AddRange(documentChangedEventArgs.GetAddedElementIds());
}
}
class FamilyLoadingOverwriteOption : IFamilyLoadOptions
{
public bool OnFamilyFound(bool familyInUse, out bool overwriteParameterValues)
{
overwriteParameterValues = true;
return true;
}
public bool OnSharedFamilyFound(Family sharedFamily, bool familyInUse, out FamilySource source, out bool overwriteParameterValues)
{
source = FamilySource.Family;
overwriteParameterValues = true;
return true;
}
}
问题是方法PromptForFamilyInstancePlacement
允许用户插入该系列的多个实例。我希望用户只能在项目中插入一个实例。我还编写了代码来备份插入的实例(正如你所看到的那样使用DocumentChanged
事件),所以也许这个处理程序在某些方面很有用..
答案 0 :(得分:1)
最后我找到了自己的解决方案(感谢Jeremy Tammik blog):在执行命令时,唯一的方法似乎是将“Esc”+“Esc”组合键发送到Windows:
我已经完成了一个处理低级别消息的类:
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);
}
}
}
主要代码如下:
{
...
_uiApp.Application.DocumentChanged += applicationOnDocumentChanged;
_uiApp.ActiveUIDocument.PromptForFamilyInstancePlacement(family);
_uiApp.Application.DocumentChanged -= applicationOnDocumentChanged;
var el = document.GetElement(_addedElementIds[0]);
...
}
private void applicationOnDocumentChanged(object sender, DocumentChangedEventArgs documentChangedEventArgs)
{
if (documentChangedEventArgs.GetTransactionNames().Contains("Component"))
{
_addedElementIds.AddRange(documentChangedEventArgs.GetAddedElementIds());
Press.Keys("" + (char)(int)Keys.Escape + (char)(int)Keys.Escape);
}
}
这样只放置了一个元素,我将它引用到el
变量中。
答案 1 :(得分:0)
您是否需要用户能够选择家庭实例的位置?
如果没有,那么你应该使用Document.NewFamilyInstance方法
这些帖子应该有助于澄清使用哪种重载:
http://thebuildingcoder.typepad.com/blog/2011/01/newfamilyinstance-overloads.html
http://thebuildingcoder.typepad.com/blog/2013/09/family-instance-placement.html
如果您确实需要用户选择放置族实例的位置,则可以使用Selection.PickPoint方法首先获取位置点,然后将该位置传递给NewFamilyInstance方法。