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

时间:2013-12-10 14:39:45

标签: c# .net revit-api

我正在尝试创建我的第一个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事件),所以也许这个处理程序在某些方面很有用..

2 个答案:

答案 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方法。