You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Revit停靠式WPF按钮执行IExternalCommand并解决崩溃问题

问题描述

已在Revit中创建停靠式WPF面板,可正常显示/隐藏。目标是在WPF面板按钮中运行自定义命令(替代功能区按钮),该命令在Add-In Manager中可正常执行,但点击WPF按钮时Revit崩溃,不清楚如何为Execute方法传递所需参数。

命令代码(PrecastDisallowJoin.cs)

using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System.Collections.Generic;
using System.Linq;

namespace Adams.Commands
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class PrecastDisallowJoin : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            var uiApplication = commandData.Application;
            var application = uiApplication.Application;
            var uiDocument = uiApplication.ActiveUIDocument;
            var document = uiDocument.Document;

            // 提示用户选择墙体
            var references = uiDocument.Selection
                .PickObjects(
                ObjectType.Element,
                new WallSelectionFilter(),
                "Please select walls");

            var components = references.Select(r => document.GetElement(r)).ToList();

            // 启动事务
            using (Transaction t = new Transaction(document, "Change Wall Join Behavior"))
            {
                t.Start();

                // 遍历选中墙体,修改连接行为
                foreach (Reference reference in references)
                {
                    Wall wall = document.GetElement(reference) as Wall;
                    WallUtils.DisallowWallJoinAtEnd(wall, 0);
                    WallUtils.DisallowWallJoinAtEnd(wall, 1);

                }

                // 提交事务
                t.Commit();
            }

            return Result.Succeeded;
        }

        public class WallSelectionFilter : ISelectionFilter
        {
            public bool AllowElement(Element elem)
            {
                //return elem is FamilyInstance;
                return elem.Name.Contains("Precast");
            }

            public bool AllowReference(Reference reference, XYZ position)
            {
                return true;
            }
        }
    }
}

WPF后台代码(Customers.xaml.cs)

using Autodesk.Revit.UI;
using System.Windows.Controls;
using Adams.Commands;
using System.Windows;


namespace Adams.ui
{
    public partial class Customers : UserControl
    {
        
        public UIDocument uIDocument { get; }
        public ExternalCommandData commandData { get; }

        public Customers(UIDocument uIDocument )
        {
        
            InitializeComponent();
        }
        private void btnStartExcelElementsApp_Click(object sender, RoutedEventArgs e)
        {
            string message = string.Empty;
            PrecastDisallowJoin precastDisallow = new PrecastDisallowJoin();
            precastDisallow.Execute(commandData, ref message, null);
        }
    }
}
解决方案

1. 崩溃原因

你的commandData字段从未赋值,始终为null。调用Execute时传入null,导致访问commandData.Application触发空引用异常,直接导致Revit崩溃。

2. 优化方案:分离命令逻辑(推荐)

将命令的核心逻辑从IExternalCommand.Execute中抽离成独立方法,既保留外部命令的兼容性,又能在WPF面板中直接调用,无需依赖ExternalCommandData参数。

修改命令类代码

using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System;
using System.Linq;

namespace Adams.Commands
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class PrecastDisallowJoin : IExternalCommand
    {
        // 抽离核心逻辑为静态方法,供WPF直接调用
        public static Result Run(UIApplication uiApp)
        {
            try
            {
                var uiDocument = uiApp.ActiveUIDocument;
                var document = uiDocument.Document;

                // 处理用户取消选择的情况
                var references = uiDocument.Selection
                    .PickObjects(
                        ObjectType.Element,
                        new WallSelectionFilter(),
                        "Please select walls");

                using (Transaction t = new Transaction(document, "Change Wall Join Behavior"))
                {
                    t.Start();
                    foreach (Reference reference in references)
                    {
                        if (document.GetElement(reference) is Wall wall)
                        {
                            WallUtils.DisallowWallJoinAtEnd(wall, 0);
                            WallUtils.DisallowWallJoinAtEnd(wall, 1);
                        }
                    }
                    t.Commit();
                }
                return Result.Succeeded;
            }
            catch (OperationCanceledException)
            {
                // 用户取消选择,返回已取消状态
                return Result.Cancelled;
            }
            catch (Exception ex)
            {
                TaskDialog.Show("命令执行失败", ex.Message);
                return Result.Failed;
            }
        }

        // 保留原Execute方法,兼容Add-In Manager和功能区按钮
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            return Run(commandData.Application);
        }

        public class WallSelectionFilter : ISelectionFilter
        {
            public bool AllowElement(Element elem)
            {
                return elem.Name.Contains("Precast");
            }

            public bool AllowReference(Reference reference, XYZ position)
            {
                return true;
            }
        }
    }
}

修改WPF后台代码

using Autodesk.Revit.UI;
using System.Windows;
using System.Windows.Controls;
using Adams.Commands;

namespace Adams.ui
{
    public partial class Customers : UserControl
    {
        private readonly UIApplication _uiApp;

        // 构造函数接收UIApplication实例(从面板初始化处传入)
        public Customers(UIApplication uiApp)
        {
            InitializeComponent();
            _uiApp = uiApp;
        }

        private void btnStartExcelElementsApp_Click(object sender, RoutedEventArgs e)
        {
            PrecastDisallowJoin.Run(_uiApp);
        }
    }
}

3. 面板初始化时传入UIApplication

在注册停靠面板的代码中,初始化Customers控件时需要传入当前的UIApplication实例,例如:

// 注册停靠面板的代码片段
public void RegisterPanel(UIControlledApplication app)
{
    // 创建面板
    var panel = app.CreateDockablePanel("YourPanelId", "自定义面板", new Customers(app.ActiveUIApplication));
    // 设置面板属性(如停靠位置等)
}

关键注意事项

  • 始终添加异常捕获:用户取消选择(OperationCanceledException)、元素类型错误等情况都可能导致崩溃,必须捕获并处理。
  • 避免直接调用IExternalCommand.Execute:该方法是Revit外部命令的标准入口,适合功能区/Add-In Manager调用,WPF面板中直接调用核心逻辑更灵活。

内容的提问来源于stack exchange,提问作者raj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 00:25:26