【问题标题】:Place a single instance of a family into a Revit project using a C# plugin使用 C# 插件将族的单个实例放置到 Revit 项目中
【发布时间】:2014-01-13 19:09:16
【问题描述】:

我正在尝试创建我的第一个 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 事件,如您所见),因此该处理程序可能在某些方面有用..

【问题讨论】:

    标签: c# .net revit-api


    【解决方案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 变量中。

    【讨论】:

    • 顺便说一下,这个解决方案不允许为实例设置插入角度。它们总是以角度 = 0 放置。还有其他方法可以达到吗?
    • 我注意到在插入实例时按下空格键,它会在鼠标角度下旋转 90° 或相应于参考(例如墙壁)
    【解决方案2】:

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

    如果没有,那么你应该使用 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 方法。

    【讨论】:

    • 我需要用户选择位置点,我还需要 Revit 显示将插入的元素的预览(在光标下)。您编写的解决方案没有显示预览。应该有一种方法可以停止applicationOnDocumentChanged 内的PromptForFamilyInstancePlacement 执行(例如)
    • 我还注意到一件事:使用NewFamilyInstance,然后为新插入的实例调用属性Location,它总是返回(0,0,0) LocationPoint。使用PromptForFamilyInstancePlacement 插入的实例具有有效的LocationPoint。此外,这个值 (LocationPoint) 对我来说很重要。
    猜你喜欢
    • 2022-09-27
    • 2020-10-24
    • 2023-03-30
    • 1970-01-01
    • 2015-10-03
    • 2020-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多