UIElement

753
1
08-03-2021 01:50 PM
johnmarker
New Contributor III

Is it possible to display a WPF UIElement as a graphic overlay onto an Arcgis basemap? 

0 Kudos
1 Reply
MichaelBranscomb
Esri Frequent Contributor

It sounds like it might represent a form of symbol to style a graphic in a graphics overlay?  

You could use RenderTagetBitmap to render an image and apply that via a PictureMarkerSymbol.

Here's some code that creates a UniqueValueRenderer from a series of UIElements rendered using RenderTargetBitmap:

private async Task CreatePictureMarkerSymbols()
{
	_strobeCanvas = new Canvas();
	Ellipse ellipse = new Ellipse()
	{
		Height = 40,
		Width = 40,
		IsHitTestVisible = false,
		RenderTransform = new ScaleTransform(),
		Fill = new RadialGradientBrush()
		{
			GradientStops = new GradientStopCollection(5)
			{
				new GradientStop(Color.FromArgb(0,255, 0, 0),0),
				new GradientStop(Color.FromArgb(255,255, 0, 0),0.25),
				new GradientStop(Color.FromArgb(0,255, 0, 0),0.5),
				new GradientStop(Color.FromArgb(255,255, 0, 0),0.75),
				new GradientStop(Color.FromArgb(0,255, 0, 0),1),
			}
		}
	};
	_strobeCanvas.Children.Add(ellipse);

	for (int i = 1; i <= _numberOfPictureMarkerSymbols; i++)
	{
		// Scale up the Ellipse
		double increment = Convert.ToDouble(i) / 5;
		Transform scaleTransform = new ScaleTransform(increment, increment);
		double scaleTransformOrigin = 0;

		ellipse.Opacity -= _opacityStep;
		ellipse.RenderTransform = scaleTransform;
		ellipse.RenderTransformOrigin = new Point(scaleTransformOrigin, scaleTransformOrigin);

		_strobeCanvas.RenderTransform = scaleTransform;
		_strobeCanvas.RenderTransformOrigin = new Point(scaleTransformOrigin, scaleTransformOrigin);
		_strobeCanvas.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
		_strobeCanvas.Arrange(new Rect(ellipse.DesiredSize));
		Rect bounds = VisualTreeHelper.GetContentBounds(ellipse);
		bounds.Transform(ellipse.RenderTransform.Value);

		var dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", BindingFlags.NonPublic | BindingFlags.Static);
		var dpiX = (int)dpiXProperty.GetValue(null, null);
		double dpi = Convert.ToDouble(dpiX);

		// Create a new RenderTargetBitmap to render the ellipse
		RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(
						(Int32)(bounds.Width * dpi / 96.0),
						(Int32)(bounds.Height * dpi / 96.0),
						dpi,
						dpi,
						PixelFormats.Pbgra32);

		// Setup the drawing context
		DrawingVisual drawingVisual = new DrawingVisual();
		using (DrawingContext drawingContext = drawingVisual.RenderOpen())
		{
			VisualBrush visualBrush = new VisualBrush(ellipse);
			drawingContext.DrawRectangle(visualBrush, pen: null, rectangle: new Rect(new Point(), bounds.Size));
		}

		// Render the visual element
		renderTargetBitmap.Render(drawingVisual);

		// Use a PngBitmapEncoder to retrieve the image 
		PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder();
		pngBitmapEncoder.Frames.Add(BitmapFrame.Create(source: renderTargetBitmap));

		using (var stream = new MemoryStream())
		{
			pngBitmapEncoder.Save(stream);
			stream.Seek(0, SeekOrigin.Begin);

			// Create a new PictureMarkerSymbol and set the Source
			PictureMarkerSymbol pms = await PictureMarkerSymbol.CreateAsync(stream);


			// Create a new UniqueValueInfo class for this image instance
			UniqueValue strobeUniqueValueInfo = new UniqueValue()
			{
				Symbol = pms,
			};

			// Add the UniqueValueInfo to the UniqueValueRenderer
			strobeUniqueValueInfo.Values.Add(i);
			_strobeUniqueValueRenderer.UniqueValues.Add(strobeUniqueValueInfo);
		}
	}
	// Set the UniqueValueRenderer field
	_strobeUniqueValueRenderer.FieldNames.Add(_strobeField);
}

 

Alternatively, if it's one or more symbols defined in XAML and you are looking for a longer term solution and vectorized rendering then you could look into converting the XAML to SVG and importing the SVG into ArcGIS Pro as a symbol layer for a mobile style which you can use in ArcGIS Runtime.

Resources:

0 Kudos