使用C#插件将族的单个实例放置到Revit项目中

使用C#插件将族的单个实例放置到Revit项目中,c#,.net,revit-api,C#,.net,Revit Api,我正在尝试创建我的第一个Revit插件 我使用的是Revit 2014,我想要的是放置从文件加载的族的单个实例。我实际使用的代码是: [TransactionAttribute(TransactionMode.Manual)] [RegenerationAttribute(RegenerationOption.Manual)] public class InsertFamily : IExternalCommand { readonly List<ElementId> _ad

我正在尝试创建我的第一个Revit插件

我使用的是Revit 2014,我想要的是放置从文件加载的族的单个实例。我实际使用的代码是:

[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;
    }
}
[TransactionAttribute(TransactionMode.Manual)]
[再生属性(再生选项.手动)]
公共类InsertFamily:IExternalCommand
{
只读列表_addedElementIds=新列表();
公开结果执行(
外部命令数据命令数据,
参考字符串消息,
元素集元素)
{
UIApplication uiApp=commandData.Application;
Document Document=uiApp.ActiveUIDocument.Document;
FamilySymbol family=null;
bool good=false;
使用(var trans=新交易(文档“插入系列”))
{
trans.Start();
good=document.LoadFamilySymbol(@“我的文件路径.rfa”,“我的类型”,new FamilyLadingGoverWriteOption(),out family);
trans.Commit();
}
if(良好和家庭!=null)
{
_addedElementIds.Clear();
uiApp.Application.DocumentChanged+=应用程序文件更改;
uiApp.ActiveUIDocument.PromptForFamilyInstancePlacement(家庭);
uiApp.Application.DocumentChanged-=应用程序内文档已更改;
}
返回结果。成功;
}
私有void应用程序内文档已更改(对象发送方,DocumentChangedEventArgs DocumentChangedEventArgs)
{
_addedElementIds.AddRange(documentChangedEventArgs.GetAddedElementIds());
}
}
类FamilyLadingGoverWrite选项:IFamilyLoadOptions
{
公共bool-OnFamilyFound(bool-familyInUse、out-bool-overwriteparametervalue)
{
overwriteParameterValues=true;
返回true;
}
公共bool-OnSharedFamilyFound(家庭共享家庭、bool-FamilyUse、out-FamilySource源、out-bool覆盖参数值)
{
source=FamilySource.Family;
overwriteParameterValues=true;
返回true;
}
}

问题在于,方法
PromptForFamilyInstancePlacement
允许用户插入该族的多个实例。我希望用户只能在项目中插入一个实例。我还编写了返回插入实例的代码(如您所见,使用
DocumentChanged
事件),因此该处理程序在某些方面可能有用。

您是否需要用户能够选择族实例的位置

如果没有,则应使用Document.NewFamilyInstance方法

这些帖子应该有助于澄清使用哪种重载:


如果确实需要用户选择放置族实例的位置,则可以使用Selection.PickPoint方法首先获取位置点,然后将该位置传递给NewFamilyInstance方法。

是否需要用户能够选择族实例的位置

如果没有,则应使用Document.NewFamilyInstance方法

这些帖子应该有助于澄清使用哪种重载:


如果确实需要用户选择放置族实例的位置,则可以使用Selection.PickPoint方法首先获取位置点,然后将该位置传递给NewFamilyInstance方法。

最后我找到了自己的解决方案(感谢):唯一的方法似乎是发送“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);
        }
    }
}

通过这种方式,只放置了一个元素,我将其引用到
el
变量中。

最终我找到了自己的解决方案(感谢):执行命令时,唯一的方法似乎是将“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);
        }
    }
}

通过这种方式,只放置一个图元,并将其引用到
el
变量中。

我需要用户选择位置点,还需要Revit显示将插入的图元的预览(在光标下)。您编写的解决方案没有显示预览..应该有一种方法可以停止
应用程序内的
PromptForFamilyInstancePlacement
执行,并更改了
(例如)我还注意到一件事:使用
NewFamilyInstance
,然后调用新插入实例的属性位置,它总是返回一个
(0,0,0)位置点
。使用
promptforfamilyinstanceplace
插入的实例具有有效的
LocationPoint
。此外,此值(
LocationPoint
)对我来说很重要,我需要用户选择位置点,我还需要Revit显示将插入的图元的预览(在光标下)。您编写的解决方案没有显示预览..应该有一种方法可以停止
应用程序内的
PromptForFamilyInstancePlacement
执行,并更改了
(例如)我还注意到一件事:使用
NewFamilyInstance
,然后调用新插入实例的属性位置,它总是返回一个
(0,0,0)位置点
。使用
promptforfamilyinstanceplace
插入的实例具有有效的
LocationPoint
。此外,此值(
LocationPoint
)对我来说是有效的,这一点很重要。顺便说一下,此解决方案不允许为实例设置插入角度。它们始终以角度=0的方式放置。还有其他方法吗?我注意到,在插入实例时按空格键,它将旋转90°,或相应地旋转到鼠标角度下的参考(如墙),因为此解决方案不允许为实例设置插入角度。它们始终以角度=0的方式放置。还有别的方法吗?我注意到了