diff --git a/Demo.WindowsPresentation/CustomMarkers/CircleVisual.cs b/Demo.WindowsPresentation/CustomMarkers/CircleVisual.cs new file mode 100644 --- /dev/null +++ b/Demo.WindowsPresentation/CustomMarkers/CircleVisual.cs @@ -0,0 +1,333 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Effects; +using Demo.WindowsPresentation.Controls; +using GMap.NET.WindowsPresentation; + +namespace Demo.WindowsPresentation.CustomMarkers +{ + public class CircleVisual : FrameworkElement + { + public readonly Popup Popup = new Popup(); + public readonly TrolleyTooltip Tooltip = new TrolleyTooltip(); + public readonly GMapMarker Marker; + + public CircleVisual(GMapMarker m, Brush background) + { + Marker = m; + Marker.ZIndex = 100; + + Popup.AllowsTransparency = true; + Popup.PlacementTarget = this; + Popup.Placement = PlacementMode.Mouse; + Popup.Child = Tooltip; + Popup.Child.Opacity = 0.777; + + SizeChanged += new SizeChangedEventHandler(CircleVisual_SizeChanged); + MouseEnter += new System.Windows.Input.MouseEventHandler(CircleVisual_MouseEnter); + MouseLeave += new System.Windows.Input.MouseEventHandler(CircleVisual_MouseLeave); + Loaded += new RoutedEventHandler(OnLoaded); + + Text = "?"; + + StrokeArrow.EndLineCap = PenLineCap.Triangle; + StrokeArrow.LineJoin = PenLineJoin.Round; + + RenderTransform = scale; + + Width = Height = 22; + FontSize = (Width/1.55); + + Background = background; + Angle = null; + } + + void CircleVisual_SizeChanged(object sender, SizeChangedEventArgs e) + { + Marker.Offset = new System.Windows.Point(-e.NewSize.Width/2, -e.NewSize.Height/2); + scale.CenterX = -Marker.Offset.X; + scale.CenterY = -Marker.Offset.Y; + } + + void OnLoaded(object sender, RoutedEventArgs e) + { + UpdateVisual(true); + } + + readonly ScaleTransform scale = new ScaleTransform(1, 1); + + void CircleVisual_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e) + { + if(Popup.IsOpen) + { + Popup.IsOpen = false; + } + + Marker.ZIndex -= 10000; + Cursor = Cursors.Arrow; + + this.Effect = null; + + scale.ScaleY = 1; + scale.ScaleX = 1; + } + + void CircleVisual_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e) + { + if(!Popup.IsOpen) + { + Popup.IsOpen = true; + } + + Marker.ZIndex += 10000; + Cursor = Cursors.Hand; + + this.Effect = ShadowEffect; + + scale.ScaleY = 1.5; + scale.ScaleX = 1.5; + } + + public DropShadowEffect ShadowEffect; + + static readonly Typeface Font = new Typeface(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Bold, FontStretches.Normal); + FormattedText FText; + + private Brush background = Brushes.Blue; + public Brush Background + { + get + { + return background; + } + set + { + if(background != value) + { + background = value; + IsChanged = true; + } + } + } + + private Brush foreground = Brushes.White; + public Brush Foreground + { + get + { + return foreground; + } + set + { + if(foreground != value) + { + foreground = value; + IsChanged = true; + + ForceUpdateText(); + } + } + } + + private Pen stroke = new Pen(Brushes.Blue, 2.0); + public Pen Stroke + { + get + { + return stroke; + } + set + { + if(stroke != value) + { + stroke = value; + IsChanged = true; + } + } + } + + private Pen strokeArrow = new Pen(Brushes.Blue, 2.0); + public Pen StrokeArrow + { + get + { + return strokeArrow; + } + set + { + if(strokeArrow != value) + { + strokeArrow = value; + IsChanged = true; + } + } + } + + public double FontSize = 16; + + private double? angle = 0; + public double? Angle + { + get + { + return angle; + } + set + { + if(!Angle.HasValue || !value.HasValue || (Angle.HasValue && value.HasValue && Math.Abs(angle.Value - value.Value) > 11)) + { + angle = value; + IsChanged = true; + } + } + } + public bool IsChanged = true; + + void ForceUpdateText() + { + FText = new FormattedText(text, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, Font, FontSize, Foreground); + IsChanged = true; + } + + string text; + public string Text + { + get + { + return text; + } + set + { + if(text != value) + { + text = value; + ForceUpdateText(); + } + } + } + + Visual _child; + public virtual Visual Child + { + get + { + return _child; + } + set + { + if(_child != value) + { + if(_child != null) + { + RemoveLogicalChild(_child); + RemoveVisualChild(_child); + } + + if(value != null) + { + AddVisualChild(value); + AddLogicalChild(value); + } + + // cache the new child + _child = value; + + InvalidateVisual(); + } + } + } + + public bool UpdateVisual(bool forceUpdate) + { + if(forceUpdate || IsChanged) + { + Child = Create(); + IsChanged = false; + return true; + } + + return false; + } + + int countCreate = 0; + + private DrawingVisual Create() + { + countCreate++; + + var square = new DrawingVisualFx(); + + using(DrawingContext dc = square.RenderOpen()) + { + dc.DrawEllipse(null, Stroke, new Point(Width/2, Height/2), Width/2 + Stroke.Thickness/2, Height/2 + Stroke.Thickness/2); + + if(Angle.HasValue) + { + dc.PushTransform(new RotateTransform(Angle.Value, Width/2, Height/2)); + { + PolyLineSegment polySeg = new PolyLineSegment(new Point[] { new Point(Width*0.2, Height*0.3), new Point(Width*0.8, Height*0.3) }, true); + PathFigure pathFig = new PathFigure(new Point(Width*0.5, -Height*0.22), new PathSegment[] { polySeg }, true); + PathGeometry pathGeo = new PathGeometry(new PathFigure[] { pathFig }); + dc.DrawGeometry(Brushes.AliceBlue, StrokeArrow, pathGeo); + } + dc.Pop(); + } + + dc.DrawEllipse(Background, null, new Point(Width/2, Height/2), Width/2, Height/2); + dc.DrawText(FText, new Point(Width/2 - FText.Width/2, Height/2 - FText.Height/2)); + } + + return square; + } + + #region Necessary Overrides -- Needed by WPF to maintain bookkeeping of our hosted visuals + protected override int VisualChildrenCount + { + get + { + return (Child == null ? 0 : 1); + } + } + + protected override Visual GetVisualChild(int index) + { + return Child; + } + #endregion + } + + public class DrawingVisualFx : DrawingVisual + { + public static readonly DependencyProperty EffectProperty = DependencyProperty.Register("Effect", typeof(Effect), typeof(DrawingVisualFx), + new FrameworkPropertyMetadata(null, (FrameworkPropertyMetadataOptions.AffectsRender), new PropertyChangedCallback(OnEffectChanged))); + public Effect Effect + { + get + { + return (Effect) GetValue(EffectProperty); + } + set + { + SetValue(EffectProperty, value); + } + } + + private static void OnEffectChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DrawingVisualFx drawingVisualFx = o as DrawingVisualFx; + if(drawingVisualFx != null) + { + drawingVisualFx.setMyProtectedVisualEffect((Effect) e.NewValue); + } + } + + private void setMyProtectedVisualEffect(Effect effect) + { + VisualEffect = effect; + } + } +}