// https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/walkthrough-enabling-drag-and-drop-on-a-user-control
        private void canvas_DragOver(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(MyRockShape.RockViewModelDataFormatName))
            {
                //Debug.WriteLine("canvas_DragOver");

                bool isAllowedDragMove = false;

                // Canvas inherits Panel
                Panel         _canvas      = sender as Panel;
                RockViewModel selectedRock = rocksOnWallViewModel.SelectedRock;

                if (_canvas != null && selectedRock != null)
                {
                    Point mousePt = e.GetPosition(cameraIMG);

                    // check rock overlaps
                    if (rocksOnWallViewModel.IsOverlapWithRocksOnWallOtherThanSelectedRock(
                            mousePt, selectedRock.SizeOnCanvas)
                        == false)
                    {
                        CameraSpacePoint csp = jcWall.GetCamSpacePointFromMousePoint(mousePt, _mode);
                        if (!csp.Equals(default(CameraSpacePoint)))
                        {
                            // note:
                            // use RocksOnWallViewModel.MoveSelectedRock()
                            // instead of RockViewModel.MoveBoulder()
                            // because RocksOnWallViewModel.MoveSelectedRock()
                            // will set the selected rock indicator as well
                            isAllowedDragMove = rocksOnWallViewModel.MoveSelectedRock(csp);
                        }
                    }
                }

                // These Effects values are used in the drag source's
                // GiveFeedback event handler to determine which cursor to display.
                if (isAllowedDragMove)
                {
                    e.Effects = supportedDragDropEffects;
                }
                else
                {
                    e.Effects = DragDropEffects.None;
                }
            }
        }
        private void boulderSizeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs <double> e)
        {
            Slider boulderSizeSlider = sender as Slider;

            if (boulderSizeSlider.Value == 0)
            {
                UiHelper.NotifyUser("Zero size is not allowed.");
                boulderSizeSlider.Value = boulderSizeSlider.Minimum + boulderSizeSlider.TickFrequency;
            }

            if (rocksOnWallViewModel != null && rocksOnWallViewModel.SelectedRock != null)
            {
                double newBoulderWidthOnCanvas  = ConvertSliderValueToSizeOnCanvas(boulderWidthSlider.Value);
                double newBoulderHeightOnCanvas = ConvertSliderValueToSizeOnCanvas(boulderHeightSlider.Value);

                // check rock overlaps
                if (rocksOnWallViewModel.IsOverlapWithRocksOnWallOtherThanSelectedRock(
                        rocksOnWallViewModel.SelectedRock.BCanvasPoint,
                        newBoulderWidthOnCanvas, newBoulderHeightOnCanvas)
                    == false)
                {
                    string boulderSizeSliderName = boulderSizeSlider.Name;
                    switch (boulderSizeSliderName)
                    {
                    case "boulderHeightSlider":
                        rocksOnWallViewModel.ChangeHeightOfSelectedRock(newBoulderHeightOnCanvas);
                        break;

                    case "boulderWidthSlider":
                    default:
                        rocksOnWallViewModel.ChangeWidthOfSelectedRock(newBoulderWidthOnCanvas);
                        break;
                    }
                }
                else
                {
                    UiHelper.NotifyUser(RockOverlapsWarningMsg);

                    // restore original value
                    boulderSizeSlider.Value -= boulderSizeSlider.TickFrequency;
                }
            }
        }