1

我正在尝试创建我的第一个 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;
    }
}

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

4

2 回答 2

1

最后我找到了自己的解决方案(感谢Jeremy Tammik 博客):唯一的方法似乎是在命令执行时将“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变量中。

于 2014-01-13T11:09:16.057 回答
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 方法。

于 2013-12-11T14:19:20.457 回答