WPF底层使用 DirectX 进行图形渲染。DirectX 能理解可由显卡直接渲染的高层元素,如纹理和渐变,所以 DirectX 效率更高。而 GDI/GDI+不理解这些高层元素,因此必须将他们转换成逐像素指令,而通过现代显卡渲染这些指令更慢。WinForm 的绘图技术使用的就是GDI/GDI+技术。但是xBIM并没有提供专门针对传统 WinForm 技术的的模型查看器。如果确实需要在传统的 WinForm 窗体中也要加载并显示BIM(.ifc格式)模型文件该如何处理呢?
由于WinForm与WPF技术可以互通互用,所以本文介绍一种取巧的方式,在WinForm窗体中加载WPF控件,WPF控件中渲染BIM(.ifc格式)模型文件。具体操作步骤如下详细介绍。
新建WinForm项目,.NET Framework 选择4.7版本,因为需要引用最新的 XBIM相关DLL(依赖 .NET Framework 4.7)。
通过NuGet程序包管理器添加xBIM相关的DLL引用
需要应用下列DLL
通过NuGet程序包管理器添加WPF相关的DLL引用。其中 HelixToolkit 是开发wpf3D应用的开源库,比较好用。
添加引用后,自动添加了下列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窗体。左侧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 }