我正在使用HelixToolkit在WPF中显示3D模型。加载工作正常,但模型没有正确缩放,尽管我使用的是ZoomExtentWhenLoaded="True"(我希望它能缩放到适合我的窗口(。模型在ViewModel中提供,并通过绑定添加到视口中。
这是我的代码:
查看
<h:HelixViewport3D ZoomExtentsWhenLoaded="True">
<h:HelixViewport3D.Camera>
<PerspectiveCamera/>
</h:HelixViewport3D.Camera>
<h:DefaultLights/>
<ModelVisual3D Content="{Binding CurrentModel}" />
</h:HelixViewport3D>
和ViewModel 的部分
Model3DGroup _currentModel;
public Model3DGroup CurrentModel
{
get { return _currentModel; }
set
{
_currentModel = value;
OnPropertyChanged(nameof(CurrentModel));
}
}
private void OnModelSelectionChanged(object sender, EventArgs args)
{
...
if (SelectedModel == null)
return;
var model = LoadModelFromFile(SelectedModel.Path);
CurrentModel = model;
}
private Model3DGroup LoadModelFromFile(string objPath, string texturePath = "")
{
try
{
ObjReader objReader = new ObjReader();
var model = objReader.Read(objPath);
ApplyTexture(model, texturePath);
return model;
}
catch (Exception e)
{
...
}
return null;
}
private void ApplyTexture(Model3DGroup model, string texture)
{
...
Material material;
if (!string.IsNullOrEmpty(texture))
{
material = MaterialHelper.CreateImageMaterial(texture);
}
else
{
material = MaterialHelper.CreateMaterial(Colors.LightBlue);
}
foreach (var m in model.Children)
{
var mGeo = m as GeometryModel3D;
mGeo.Material = material;
}
}
...
我尝试使用附加的属性而不是ZoomExtentsWhenLoaded="True",并从中触发ZoomExtents((,但在更改模型时,似乎找不到实际触发的正确事件。如何使ZoomExtentWhenLoaded正常工作?还是这毕竟是错误的财产?如何设置缩放和变换以使模型适合我的窗口?谢谢你的帮助!
您希望ViewPort在特性更改时缩放到Model3DGroup的范围。实现这一点的一种方法是在视图中侦听ViewModel中的事件(请参阅上的建议https://github.com/helix-toolkit/helix-toolkit/issues/1265)-我在下一段中对此进行了描述。
ViewModel在属性集方法中激发一个事件,View通过调用viewport来响应该事件。ZoomToExtents((。您可以使用一个普通的C#事件,但由于这打破了MVVM中数据绑定的解耦特性,有些人更喜欢找到另一种路由。如果您使用的是MVVM框架,通常支持以减少耦合的方式将视图附加到ViewModels(例如,请参阅Caliburn Micro的IViewAware(。
另一种方法是将ViewModel视为向视图显示"感兴趣的区域",当该属性的值更改时,视图会缩放到该区域。这让我创建了一个小的附加属性,可以如下使用(使用您的示例(。。。
<h:HelixViewport3D helixextensions:AutoFit.Bounds="{Binding Path=CurrentModel.Bounds}">
<h:HelixViewport3D.Camera>
<PerspectiveCamera />
</h:HelixViewport3D.Camera>
<h:DefaultLights/>
<ModelVisual3D Content="{Binding CurrentModel}"/>
</h:HelixViewport3D>
您可以在Window或UserControl声明中使用xmlns:helixextensions="clr-namespace:helixextensions"将其包含在XAML文件中。
这是附加属性的定义。。。
namespace HelixExtensions
{
public static class AutoFit
{
private static readonly Type OwnerType = typeof(AutoFit);
public static readonly DependencyProperty BoundsProperty =
DependencyProperty.RegisterAttached(
"Bounds",
typeof(Rect3D),
OwnerType,
new PropertyMetadata(Rect3D.Empty, OnDataContextChanged)
);
public static bool GetBounds(HelixViewport3D obj)
{
return (bool)obj.GetValue(BoundsProperty);
}
public static void SetBounds(HelixViewport3D obj, bool value)
{
obj.SetValue(BoundsProperty, value);
}
private static void OnDataContextChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var viewport = d as HelixViewport3D;
if (viewport.DataContext == null) return;
viewport.ZoomExtents((Rect3D)e.NewValue);
}
}
}
它的工作原理是允许您绑定到CurrentModel属性的Rect3D(三维边界框(。CurrentModel属性更改时,将调用OnDataContextChanged方法,该方法将ViewPort缩放到新的范围。
如果您想缩放到多个模型的范围,或者您有一个想要关注的特定边界,那么您可以在ViewModel上将其公开为Rect3D属性,并简单地绑定到该属性而不是CurrentModel。边界。在简单的应用程序中,这种额外级别的控制是不必要的。
例如
<h:HelixViewport3D helixextensions:AutoFit.Bounds="{Binding Path=CurrentFocusBounds}">
<h:HelixViewport3D.Camera>
<PerspectiveCamera />
</h:HelixViewport3D.Camera>
<h:DefaultLights/>
<ModelVisual3D Content="{Binding CurrentModel}"/>
</h:HelixViewport3D>