xBIM 实战04 在WinForm窗体中实现IFC模型的加载与浏览

  • 2019 年 10 月 4 日
  • 筆記

  WPF底层使用 DirectX 进行图形渲染。DirectX 能理解可由显卡直接渲染的高层元素,如纹理和渐变,所以 DirectX 效率更高。而 GDI/GDI+不理解这些高层元素,因此必须将他们转换成逐像素指令,而通过现代显卡渲染这些指令更慢。WinForm 的绘图技术使用的就是GDI/GDI+技术。但是xBIM并没有提供专门针对传统 WinForm 技术的的模型查看器。如果确实需要在传统的 WinForm 窗体中也要加载并显示BIM(.ifc格式)模型文件该如何处理呢?

  由于WinForm与WPF技术可以互通互用,所以本文介绍一种取巧的方式,在WinForm窗体中加载WPF控件,WPF控件中渲染BIM(.ifc格式)模型文件。具体操作步骤如下详细介绍。

一、新建WinForm项目

新建WinForm项目,.NET Framework 选择4.7版本,因为需要引用最新的 XBIM相关DLL(依赖 .NET Framework 4.7)。

二、添加xBIM相关DLL引用

通过NuGet程序包管理器添加xBIM相关的DLL引用

需要应用下列DLL

三、添加WPF相关DLL引用

通过NuGet程序包管理器添加WPF相关的DLL引用。其中 HelixToolkit 是开发wpf3D应用的开源库,比较好用。

添加引用后,自动添加了下列WPF的基础库。

四、在Winform项目中添加WPF用户控件

编写XAML代码如下:

 1 <UserControl x:Class="Xbim.WinformsSample.WinformsAccessibleControl"   2              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   3              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   4              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"   5              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"   6              xmlns:presentation="http://schemas.Xbim.com/Presentation"   7              mc:Ignorable="d"   8              d:DesignHeight="600" d:DesignWidth="800"   9              x:Name="MainWindow"  10              DataContext="{Binding RelativeSource={RelativeSource Self}}">  11     <Grid Name="MainFrame">  12         <presentation:DrawingControl3D x:Name="DrawingControl"  13                                        x:FieldModifier="public"  14                                        Model ="{Binding ModelProvider.ObjectInstance}"  15                                        Focusable="True"  16                                        Width="Auto"  17                                        Height="Auto"  18                                        SelectedEntityChanged="DrawingControl_SelectedEntityChanged"  19                                        ModelOpacity="1">  20         </presentation:DrawingControl3D>  21     </Grid>  22 </UserControl>

其中第12行,引用了 xBIM官方提供的 模型浏览器组件。显示效果如下:

五、在WinForm窗体中调用WPF查看器

  添加一个WinForm窗体。左侧Panel中是 按钮区域,右侧Panel填充窗体剩余的所有区域。

打开VS的工具箱,可以看到如下栏目

WPF互操作性,将 “ElementHost”控件拖拽到右侧Panel中,命名为controlHost,并设置 Dock 属性为 Fill。

后台逻辑:在第四步骤中创建了一个WPF用户控件,在此处实例化一个对象

private WinformsAccessibleControl _wpfControl;

在构造函数中初始化该对象并将对象添加到 controlHost 中

 1  public FormExample(ILogger logger = null)   2         {   3             InitializeComponent();   4   5             Logger = logger ?? new LoggerFactory().CreateLogger<FormExample>();   6   7             IfcStore.ModelProviderFactory.UseHeuristicModelProvider();   8   9             _wpfControl = new WinformsAccessibleControl();  10             _wpfControl.SelectionChanged += _wpfControl_SelectionChanged;  11  12             controlHost.Child = _wpfControl;  13         }

运行效果如下:

完整的示例代码如下:

  1 using System;    2 using System.Linq;    3 using System.Windows.Forms;    4    5 using Microsoft.Extensions.Logging;    6    7 using Xbim.Common;    8 using Xbim.Ifc;    9 using Xbim.Ifc4.Interfaces;   10 using Xbim.ModelGeometry.Scene;   11   12 namespace Xbim.WinformsSample   13 {   14     public partial class FormExample : Form   15     {   16         private WinformsAccessibleControl _wpfControl;   17   18         int starting = -1;   19   20         protected ILogger Logger { get; private set; }   21   22         public FormExample(ILogger logger = null)   23         {   24             InitializeComponent();   25   26             Logger = logger ?? new LoggerFactory().CreateLogger<FormExample>();   27   28             IfcStore.ModelProviderFactory.UseHeuristicModelProvider();   29   30             _wpfControl = new WinformsAccessibleControl();   31             _wpfControl.SelectionChanged += _wpfControl_SelectionChanged;   32   33             controlHost.Child = _wpfControl;   34         }   35   36         private void _wpfControl_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)   37         {   38             var ent = e.AddedItems[0] as IPersistEntity;   39             txtEntityLabel.Text = ent == null ? "" : ent.EntityLabel.ToString();   40         }   41   42         /// <summary>   43         /// 打开BIM(.ifc格式)文件   44         /// </summary>   45         /// <param name="sender"></param>   46         /// <param name="e"></param>   47         private void BtnLoadBimFile_Click(object sender, EventArgs e)   48         {   49             var dlg = new OpenFileDialog();   50             dlg.Filter = @"IFC Files|*.ifc;*.ifczip;*.ifcxml|Xbim Files|*.xbim";   51             dlg.FileOk += (s, args) =>   52                           {   53                               LoadXbimFile(dlg.FileName);   54                           };   55             dlg.ShowDialog(this);   56         }   57   58         /// <summary>   59         ///  查看模型实体标签   60         /// </summary>   61         /// <param name="sender"></param>   62         /// <param name="e"></param>   63         private void BtnNext_Click(object sender, EventArgs e)   64         {   65             var mod = _wpfControl.ModelProvider.ObjectInstance as IfcStore;   66             if (mod == null)   67                 return;   68   69             var found = mod.Instances.OfType<IIfcProduct>().FirstOrDefault(x => x.EntityLabel > starting);   70             _wpfControl.SelectedElement = found;   71   72             if(found != null)   73             {   74                 starting = found.EntityLabel;   75             }   76             else   77             {   78                 starting = -1;   79             }   80         }   81   82         /// <summary>   83         ///  加载BIM(.ifc格式)文件   84         /// </summary>   85         /// <param name="dlgFileName"></param>   86         private void LoadXbimFile(string dlgFileName)   87         {   88             // TODO: should do the load on a worker thread so as not to lock the UI.   89             //  如果加载的模型文件较大,耗时可能较长,建议使用后要程序处理,给用户一个好的使用体验。   90   91             Clear();   92   93             var model = IfcStore.Open(dlgFileName);   94             if (model.GeometryStore.IsEmpty)   95             {   96                 // 使用 xBIM 几何引擎创建 GeometryEngine 对象   97                 try   98                 {   99                     var context = new Xbim3DModelContext(model);  100  101                     context.CreateContext();  102  103                     // TODO: SaveAs(xbimFile); // so we don't re-process every time  104                 }  105                 catch (Exception geomEx)  106                 {  107                     Logger.LogError(0, geomEx, "Failed to create geometry for {filename}", dlgFileName);  108                 }  109             }  110             _wpfControl.ModelProvider.ObjectInstance = model;  111         }  112  113         public void Clear()  114         {  115             if (_wpfControl.ModelProvider != null)  116             {  117                 var currentIfcStore = _wpfControl.ModelProvider.ObjectInstance as IfcStore;  118                 currentIfcStore?.Dispose();  119  120                 _wpfControl.ModelProvider.ObjectInstance = null;  121             }  122         }  123     }  124 }