Controlling the Scene example

Now that we've learned some important concepts regarding 3D, let's take our app and make it so the user can control the scene using an intuitive UI, while at the same time extending our understanding of MVVM by adding another ViewModel class to our app that makes the use of a reusable user control:

  1. Create a new ArcGIS Runtime project, add MVVM Light, Json.NET, copy the code files from the previous project, and then update all references and the using statements.
  2. Copy the XAML code from the MainWindow.xaml of Chapter4 to the new MainWindow.xaml and update the using statements. Make sure the app runs as with the previous exercise. If you have any issues with the app not binding to the ViewModel class, check the binding in the XAML code and update them, like in this example:
    <TextBox Name="SearchTextBox" Text="{Binding SearchText, Source={StaticResource Locator}}"></TextBox>
  3. Create a new MVVM Light ViewModel class. Be sure to select MvvmViewModel (WPF). Call it CameraViewModel.cs and place it in the ViewModels folder. Add the following code to it:
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Messaging;
    
    using Esri.ArcGISRuntime.Geometry;
    using Esri.ArcGISRuntime.Controls;
    
    namespace Chapter4a.ViewModels
    {
        /// <summary>
        /// This class contains properties that a View can data bind to.
        /// <para>
        /// See http://www.galasoft.ch/mvvm
        /// </para>
        /// </summary>
        public class CameraViewModel : ViewModelBase
        {
            Esri.ArcGISRuntime.Controls.SceneView sceneView = null;
            double heading = 0.0;
            double pitch  = 0.0;
            double elevation = 11000000;
            double latitude = 0.0;
            double longitude = 0.0;
            /// <summary>
            /// Initializes a new instance of the LocationViewModel class.
            /// </summary>
            public CameraViewModel()
            {
                Messenger.Default.Register<SceneView>(this, (sceneView) =>
                {
                    this.sceneView = sceneView;
                });
            }
    
            public double Heading
            {
                get { return this.heading; }
                set
                {
                    this.heading = value;
                    RaisePropertyChanged("Heading");
    
                    MapPoint myLocation = new MapPoint(this.longitude, 
                        this.latitude, this.elevation, SpatialReferences.Wgs84);
                    Camera myCamera = new Camera(myLocation, value, this.pitch);
                    this.sceneView.SetView(myCamera);
                }
            }
            public double Pitch
            {
                get { return this.pitch; }
                set
                {
                    this.pitch = value;
                    RaisePropertyChanged("Pitch");
    
                    MapPoint myLocation = new MapPoint(this.longitude, 
                        this.latitude, this.elevation, SpatialReferences.Wgs84);
                    Camera myCamera = new Camera(myLocation, this.heading, value);
                    this.sceneView.SetView(myCamera);
                }
            }
            public double Elevation
            {
                get { return this.elevation; }
                set
                {
                    this.elevation = value;
                    RaisePropertyChanged("Elevation");
    
                    MapPoint myLocation = new MapPoint(this.longitude, 
                        this.latitude, value, SpatialReferences.Wgs84);
                    Camera myCamera = new Camera(myLocation, this.heading, 
                          this.pitch);
                    this.sceneView.SetView(myCamera);
                }
            }
            public double Latitude
            {
                get { return this.latitude; }
                set
                {
                    this.latitude = value;
                    RaisePropertyChanged("Latitude");
    
                    MapPoint myLocation = new MapPoint(this.longitude, value, 
                        this.elevation, SpatialReferences.Wgs84);
                    Camera myCamera = new Camera(myLocation, this.heading, 
                         this.pitch);
                    this.sceneView.SetView(myCamera);
                }
            }
            public double Longitude
            {
                get { return this.longitude; }
                set
                {
                    this.longitude = value;
                    RaisePropertyChanged("Longitude");
    
                    MapPoint myLocation = new MapPoint(value, this.latitude, 
                          this.elevation, SpatialReferences.Wgs84);
                    Camera myCamera = new Camera(myLocation, this.heading, 
                         this.pitch);
                    this.sceneView.SetView(myCamera);
                }
            }
        }
    }
  4. In ViewModelLocator, register the new ViewModel class just like MainViewModel is registered:
    SimpleIoc.Default.Register<CameraViewModel>();
  5. Add a new property to ViewModelLocator like this:
    public CameraViewModel MainViewModel
    {
        get
        {
            return ServiceLocator.Current.GetInstance<CameraViewModel>();
        }
    }
  6. Create a new folder in your project and name it UserControls. Name the UserControl folder's CameraUserControl.xaml file like this:
    Controlling the Scene example
  7. Add the following XAML code to UserControl:
    <UserControl x:Class="Chapter4a.UserControls.CameraUserControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
        xmlns:vm="clr-namespace:Chapter4a.ViewModels"
            mc:Ignorable="d" 
            d:DesignHeight="151" d:DesignWidth="240">
    
        <UserControl.Resources>
            <vm:CameraViewModel x:Key="Locator" d:IsDataSource="True"/>
        </UserControl.Resources>
    
        <Grid DataContext="{Binding CameraViewModel, Source={StaticResource  
            Locator}}">
    
            <StackPanel Orientation="Vertical" Margin="10">
    
                <!-- Slider for the Heading (in Degrees) of the Camera. -->
                <StackPanel Orientation="Horizontal">
                    <Label Content="Heading: " Foreground="Green" 
                        FontWeight="Bold" Width="70" />
                    <Slider x:Name="Slider_Heading" Width="100" 
                        AutoToolTipPlacement="BottomRight"
                            Minimum="0" Maximum="360" TickFrequency="1" 
                            Value="{Binding Heading, Source={StaticResource 
                            Locator}}"/>
    
                    <TextBlock x:Name="Heading_Value" VerticalAlignment="Center" 
                        FontWeight="Bold"
                        Foreground="Green" 
                        Text="{Binding Heading, StringFormat='N0', 
                        Source={StaticResource Locator}}"/>
                </StackPanel>
    
                <!-- Slider for the Pitch (in Degrees) of the Camera. -->
                <StackPanel Orientation="Horizontal">
                    <Label Content="Pitch: " Foreground="Green" FontWeight="Bold" 
                        Width="70" />
                    <Slider x:Name="Slider_Pitch" Width="100"  
                        AutoToolTipPlacement="BottomRight"
                        Minimum="0" Maximum="180" TickFrequency="1"
                            Value="{Binding Pitch, Source={StaticResource 
                            Locator}}"/>
    
                    <TextBlock x:Name="Pitch_Value" VerticalAlignment="Center" 
                        FontWeight="Bold"
                        Foreground="Green" Text="{Binding Pitch, 
                        StringFormat='N0', 
                        Source={StaticResource Locator}}"/>
                </StackPanel>
    
                <!-- Slider for the Z value (aka. Elevation in Meters) of the 
                    Camera's Location. -->
                <StackPanel Orientation="Horizontal">
                    <Label Content="Elevation: " Foreground="Green" 
                            FontWeight="Bold" Width="70" />
                    <Slider x:Name="Slider_Z" Width="200"  
                        AutoToolTipPlacement="BottomRight"
                        Minimum="0" Maximum="15000000" TickFrequency="1" 
                            Value="{Binding Elevation, StringFormat='N0', 
                            Source={StaticResource Locator}}"/>
                    
                    <TextBlock x:Name="Z_Value" VerticalAlignment="Center"
                        Foreground="Green" Text="{Binding Elevation, 
                                StringFormat='N0',
                                Source={StaticResource Locator}}"/>
                </StackPanel>
    
                <!-- Slider for the X value (aka. Longitude in Decimal Degrees) of 
                        the Camera's Location. -->
                <StackPanel Orientation="Horizontal">
                    <Label Content="Longitude: " Foreground="Green" 
                            FontWeight="Bold" Width="70"
                    />
                    <Slider x:Name="Slider_X" Width="200"  
                        AutoToolTipPlacement="BottomRight" 
                        AutoToolTipPrecision="2" Minimum="-180" Maximum="180" 
                            TickFrequency="0.01" 
                            Value="{Binding Longitude, Source={StaticResource 
                            Locator}}" />
    
                    <TextBlock x:Name="X_Value" Foreground="Green" 
                        FontWeight="Bold" VerticalAlignment="Center"
                        Text="{Binding Longitude,StringFormat='N2', 
                        Source={StaticResource Locator}}"/>
                </StackPanel>
    
                <!-- Slider for the Y value (aka. Latitude in Decimal Degrees) of 
                    the Camera's Location. -->
                <StackPanel Orientation="Horizontal">
                    <Label Content="Latitude: " Foreground="Green" 
                        FontWeight="Bold" Width="70" />
                    <Slider x:Name="Slider_Y" Width="200"  
                        AutoToolTipPlacement="BottomRight" 
                        AutoToolTipPrecision="2" Minimum="-90" Maximum="90" 
                        TickFrequency="0.01"
                        Value="{Binding Latitude, Source={StaticResource 
                    Locator}}"/>
    
                    <TextBlock x:Name="Y_Value" Foreground="Green" 
                        FontWeight="Bold" VerticalAlignment="Center"
                        Text="{Binding Latitude, 
                        StringFormat='N2',Source={StaticResource 
                        Locator}}"/>
                </StackPanel>
    
    
            </StackPanel>
        </Grid>
    </UserControl>
  8. In MainWindow.xaml, add the following using statement:
    xmlns:uc="clr-namespace:Chapter4a.UserControls"
  9. After the closing SceneView tag, add this line:
    <uc:CameraUserControl HorizontalAlignment="Right" Margin="0, 50"> </uc:CameraUserControl>
  10. Run the app. You will see a set of controls in the upper-right corner, as shown in the following screenshot:
    Controlling the Scene example

    Display the set of controls in the upper-right corner

  11. With these sliders, you can change the heading, pitch, elevation, longitude, and latitude. Note that the mouse isn't aware of these changes, so just use the sliders to change these parameters.

Several changes were made to this app to add these new capabilities. We created a new ViewModel class and added it to the MVVM Light registry of ViewModel classes. This required adding a property to ViewModel Locator so that the View class could find it. We then populated the new ViewModel class (CameraViewModel) with several properties so that the user control could bind to it. We then created a user control and set its DataContext to ViewModel so that it knows which one to use via the Locator. In this case, it's set to CameraViewModel.

In the user control, we bound each slider to a property on the ViewModel class. For example, the pitch slider was bound to the Pitch property so that when a user changes the pitch, it adjusts the globe's pitch. If you look at the pitch slider, you'll note that it has a range from 0 to 180, which is the range it should have. The latitude slider also has a range from -90 to 90 degrees, as it should be. The other sliders also have appropriate ranges. In the ViewModel class, the properties are updated every time the user changes a slider because RaisePropertyChanged is called for the respective property. Also, the camera is updated with the new position from a newly created MapPoint geometry. (We'll talk more about MapPoint and other geometries in the next chapter.) Lastly, the SceneView property is updated with this.sceneView.SetView(myCamera).

The really important new capability that we have created here is a reusable UserControl folder that we could put into another project if we needed it. All that would be necessary is we just copy the ViewModel and UserControl properties over into the project and update its Locator, and it should work perfectly fine. An even better solution would be to place UserControl and ViewModel into its own reusable class library, and then add it to any project. Of course, before this new user control could really be used, it would need to take into account the mouse movements.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.106.233