Search code examples
c#helix-3d-toolkit

ZoomExtents method call works different than activating ZoomExtents through gesture


I've been working on a small 3D preview window in a MVVM style application... The view is created then its data context is set. Therefore it seems that ZoomExtentsWhenLoaded="True" doesn't seem to help do what I need. I need something like, ZoomExtentsWhenDataContextChanges.

Interestingly, I've found that if I use a mouse gesture like the one defined below, I can physically click on the HelixViewport3D and it will perform a ZoomExtents.

HelixViewport3D.ZoomExtentsGesture = new MouseGesture(MouseAction.LeftDoubleClick);

However, if do something like this...

HelixViewport3D.DataContextChanged += (o, e) => ResetCamera();

private void ResetCamera()
{
    var dc = HelixViewport3D.DataContext as WellSurveyPlot3DViewModel;
    HelixViewport3D.ResetCamera();
    HelixViewport3D.Camera = dc.PerspectiveCamera;
    HelixViewport3D.ZoomExtents();
}

The viewport does zoom, it just doesn't center itself, like it does when activating ZoomExtents when using the mouse gesture.

I tried ResetCamera, and several other things... What is the standard way of dealing with keeping a viewport around and swapping out the DataContext instead of creating a new one each time?


Solution

  • I fixed this with an attached property. I read through the HelixViewport3D source code and got this idea, after noticing how the camera works. It seems an update to the default camera through a property binding doesn't really do anything after the control is initialized.

          public static class HelixViewport3DZoomExtent
        {
            private static readonly Type OwnerType = typeof(HelixViewport3DZoomExtent);
            public static readonly DependencyProperty ZoomExtentsOnUpdateProperty = DependencyProperty.RegisterAttached("ZoomExtentsOnUpdate", typeof(bool), OwnerType, new PropertyMetadata(false, OnDataContextChanged));
    
            public static bool GetZoomExtentsOnUpdate(DependencyObject obj)
            {
                return (bool)obj.GetValue(ZoomExtentsOnUpdateProperty);
            }
            public static void SetZoomExtentsOnUpdate(DependencyObject obj, bool value)
            {
                obj.SetValue(ZoomExtentsOnUpdateProperty, value);
            }
            private static void OnDataContextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
            {
                var viewport = d as HelixViewport3D;
                if (viewport == null) return;
                if (viewport.DataContext == null) return;
                viewport.Camera = viewport.DefaultCamera;
                viewport.ZoomExtents();
            }
        }
    

    Here is the Xaml

         <Border BorderBrush="Black" BorderThickness="1">
    
            <Grid>
    
                <h:HelixViewport3D Name="HelixViewport3D" 
                                   PanGesture="LeftClick"
                                   DataContext="{Binding PreviewPlot, UpdateSourceTrigger=PropertyChanged}"
                                   DefaultCamera="{Binding PerspectiveCamera, UpdateSourceTrigger=PropertyChanged}" 
                                   services:HelixViewport3DZoomExtent.ZoomExtentsOnUpdate="{Binding RelativeSource={RelativeSource AncestorType={x:Type views:WellSurveyPlot3DPreview}}, 
                                                                                            Path=DataContext.PreviewUpdatedReZoom, UpdateSourceTrigger=PropertyChanged}">
    
                    <h:SunLight/>
    
                    <h:TubeVisual3D  Path="{Binding TubePath}" Diameter="75" ThetaDiv="12" IsPathClosed="False" Fill="LightGray"/>
    
                    <h:GridLinesVisual3D Width="{Binding GridLength}" Length="{Binding GridLength}" MajorDistance="{Binding MajorGridLines}" Thickness="25"
                                     MinorDistance="{Binding MajorGridLines, UpdateSourceTrigger=PropertyChanged}" LengthDirection="1,0,0" Normal="0,0,1" 
                                     Center="{Binding BottomPlaneCenter,UpdateSourceTrigger=PropertyChanged}" Fill="Red"   />
                    <h:GridLinesVisual3D Width="{Binding GridLength}" Length="{Binding GridLength, UpdateSourceTrigger=PropertyChanged}" LengthDirection="0,0,1" Normal="1,0,0"  Thickness="25"
                                     MajorDistance="{Binding MajorGridLines}" MinorDistance="{Binding MajorGridLines}"
                                     Center="{Binding BackLeftPlaneCenter, UpdateSourceTrigger=PropertyChanged}" Fill="Blue" />
                    <h:GridLinesVisual3D Width="{Binding GridLength}" Length="{Binding GridLength, UpdateSourceTrigger=PropertyChanged}" LengthDirection="1,0,0" Normal="0,1,0" Thickness="25"
                                     MajorDistance="{Binding MajorGridLines}" MinorDistance="{Binding MajorGridLines}" 
                                     Center="{Binding BackRightPlaneCenter,UpdateSourceTrigger=PropertyChanged}" Fill="Green" />
    
                </h:HelixViewport3D>
    
                <Button Content="Open Well Viewer" HorizontalAlignment="Left"  VerticalAlignment="Top" Command="{Binding OpenWindowCmd}"/>
    
            </Grid>
        </Border>
    

    In my view model I have to toggle my PreviewUpdateReZoom property.

        private void LoadSurveyPoints(List<WellSurveyPointCalculated> surveyPoints)
            {
                _coordinatesCalculator = _calcGlobalCoordsFactory.Create(surveyPoints);
                _wellXyzCoordinates = _coordinatesCalculator.PlotGlobalCoordinates(100).ToList();
                PreviewPlot = WellSurveyPlot3DViewModel();
                PreviewUpdatedReZoom = false;//Toggle true false to send property changed and get attached property to fire.
                PreviewUpdatedReZoom = true;
            }
    

    This now works such that every new item drawn into the viewport has the correct camera settings and zooms to extents...