I'm using the DexExpress.Xpf.Map.v15.1 MapControl and trying to create code that will be able to run in a Windows Service (i.e. no front end) to generate maps as images.
I have the following code which creates a map, adds GeoPoints to a PolyLine and then adds that PolyLine to a map. This works fine in my WPF test harness application:
public MapWindow1()
{
InitializeComponent();
var mapControl = new MapControl
{
Name = "TheMap",
ZoomLevel = 4,
CenterPoint = new GeoPoint(47, 5)
};
var imageLayer = new ImageTilesLayer
{
DataProvider = new OpenStreetMapDataProvider()
};
mapControl.Layers.Add(imageLayer);
var vectorLayer = new VectorLayer();
mapControl.Layers.Add(vectorLayer);
var mapItemStorage = new MapItemStorage();
vectorLayer.Data = mapItemStorage;
var polyLineCollection = DbfGenerator.GeneratePolyLineCollection();
polyLineCollection.ForEach(line =>
{
var mapItem = new MapPolyline();
mapItemStorage.Items.Add(mapItem);
mapItem.Points.AddRange(line);
});
mapControl.MouseDoubleClick += mapControl_MouseDoubleClick;
MainGrid.Children.Add(mapControl);
}
This runs and adds the map control to the WPF page without a problem. I've then added double click handler to export the map as an image:
void mapControl_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var m = GenerateMapPng((MapControl) sender);
m.Save(#"C:\0Temp\test.bmp");
}
public Bitmap GenerateMapPng(MapControl map)
{
Bitmap bmp;
// export image from map
using (MemoryStream ms = new MemoryStream())
{
var exportOptions = new ImageExportOptions(ImageFormat.Png);
map.ExportToImage(ms, exportOptions);
ms.Position = 0;
bmp = new Bitmap(System.Drawing.Image.FromStream(ms));
}
return bmp;
}
Again - this works without a problem.
However, If I remove the UI element and run the following code:
public MapWindow1()
{
InitializeComponent();
var mapControl = new MapControl
{
Name = "TheMap",
ZoomLevel = 4,
CenterPoint = new GeoPoint(47, 5)
};
var imageLayer = new ImageTilesLayer
{
DataProvider = new OpenStreetMapDataProvider()
};
mapControl.Layers.Add(imageLayer);
var vectorLayer = new VectorLayer();
mapControl.Layers.Add(vectorLayer);
var mapItemStorage = new MapItemStorage();
vectorLayer.Data = mapItemStorage;
var polyLineCollection = DbfGenerator.GeneratePolyLineCollection();
polyLineCollection.ForEach(line =>
{
var mapItem = new MapPolyline();
mapItemStorage.Items.Add(mapItem);
mapItem.Points.AddRange(line);
});
var m = GenerateMapPng(mapControl);
m.Save(#"C:\0Temp\test.bmp");
}
I get a NullReferenceException on the "map.ExportToImage(ms, exportOptions);" line.
I'm assuming that when the WPF application loads it calls a method to initialise the map, but I can't find anything that I can manually do to the control (Load/Init method) to mimic this.
Is there a way to trick the MapControl in to thinking it is in a WPF page when it isn't?
DevExpress said that this was not possible due to the maps reliance on the UI Thread in WPF
Related
VS 2019 with all updates.
I install Xamarin.Forms.Map using nuget.
Using Xamarin Android to display a map with a pin point.
Front end XAML is
<ContentPage.Content>
<maps:Map x:Name="Map"/>
</ContentPage.Content>
In code-behind i have
public TestPage()
{
InitializeComponent();
var map = new Map(MapSpan.FromCenterAndRadius(new Position(37, -122), Distance.FromMiles(10)));
var pin = new Pin()
{
Position = new Position(37, -122),
Label = "Some Pin!"
};
map.Pins.Add(pin);
var cp = new ContentPage
{
Content = map,
};
}
Which i have followed from MSDN https://learn.microsoft.com/en-us/dotnet/api/xamarin.forms.maps.map?view=xamarin-forms.
I then click pixel_2_pie_api_28 (F5) and once the page loads it shows a map but not to the location i set (i randomly changed the position values) or the pin point or even label? What am i missing?
you have quite a few problems in your code
// you already have a map declared in your XAML, you do not need to do it again
var map = new Map(MapSpan.FromCenterAndRadius(new Position(37, -122), Distance.FromMiles(10)));
var pin = new Pin()
{
Position = new Position(37, -122),
Label = "Some Pin!"
};
// add this to your existing XAML map, which you named "Map"
Map.Pins.Add(pin);
// you already have a ContentPage, you don't need to declare a new one
var cp = new ContentPage
{
Content = map,
};
all you need to do
var pin = new Pin()
{
Position = new Position(37, -122),
Label = "Some Pin!"
};
Map.Pins.Add(pin);
to move the map to a specific position
var span = MapSpan.FromCenterAndRadius(new Position(37, -122), Distance.FromMiles(10));
Map.MoveToRegion(span);
I am trying to save InkCanvas , InkStorkes as a SVG. I found a previous question that works for Wpf,but I have been unable to get it to work with uwp. Wpf InkCanvas save stokes as svg
. I made some changes to it, but I am running into issues with GetGrometry and XamlWriter.
var svg = new SvgDocument();
var colorServer = new SvgColourServer(System.Drawing.Color.Black);
var group = new SvgGroup { Fill = colorServer, Stroke = colorServer };
svg.Children.Add(group);
foreach (var stroke in inkCanvas.InkPresenter.StrokeContainer.GetStrokes())
{
var geometry = stroke.GetGeometry(stroke.DrawingAttributes).GetOutlinedPathGeometry();
var s = XamlWriter.Save(geometry);
if (s.IsNotNullOrEmpty())
{
var element = XElement.Parse(s);
var data = element.Attribute("Figures")?.Value;
if (data.IsNotNullOrEmpty())
{
group.Children.Add(new SvgPath
{
PathData = SvgPathBuilder.Parse(data),
Fill = colorServer,
Stroke = colorServer
});
}
}
}
I implemented an approach from the comments Xavier Xie - MSFT.
The main idea was to use own implementation of ICanvasPathReceiver interface - here is CanvasGeometryToSvgPathReader class:
var svgDocument = new CanvasSvgDocument(canvasDevice);
foreach (var stroke in InkCanvas.InkPresenter.StrokeContainer.GetStrokes())
{
var canvasGeometry = CanvasGeometry.CreateInk(drawingSession, new[] {stroke});
var pathReceiver = new CanvasGeometryToSvgPathReader();
canvasGeometry.SendPathTo(pathReceiver);
var element = svgDocument.Root.CreateAndAppendNamedChildElement("path");
element.SetStringAttribute("d", pathReceiver.Path);
element.SetColorAttribute("fill", stroke.DrawingAttributes.Color);
}
As result ballpoint pen renders fine, highlighter worse and pencil doesn't render.
Full source code: https://github.com/ycherkes/InkToSvg
I am using this code:
var highway1 = new Image { Source = new BitmapImage(uri) };
highway1.Width = wH;
highway1.Height = wH;
// highway1.Tapped += pushpinTapped;
MapLayer.SetPosition(highway1, new Location(5.3538, 101.35885));
highway1.Tag = highway1.Tag as String;
highway1.Tag = "jkhdfhdskjfhjsakhfkjajskdf";
map1.Children.Add(highway1);
How can I do this in Bing maps using C#. I manage to do this using Geoserver.
Thanks
Hello i am using windows map control described in this link and i have a MapLayer with multiple MapOverlays with differents pois on map. And i want to do the clustering thing. I try to do this but no ClusteringLayer exist and no Pushpin. How can i do the clustering?
var cluster = new ClusteringLayer();
layer = new ClusteringLayer(Mymap)
{
ClusterRadius = 10,
ClusterType = ClusteringType.Grid
};
//Add event handlers to create the pushpins
layer.CreateClusteredItemPushpin += CreateClusteredItemPushpin1;
layer.CreateItemPushpin+=layer_CreateItemPushpin;
private MapOverlay layer_CreateItemPushpin(object item, ClusteredPoint clusterInfo)
{
var x = clusterInfo.Location;
var poi = new BuildingPoi { Coordinate = x, Buid = _selectedBuild };
var imagePoiLocation = new Image
{
Source = new BitmapImage(new Uri("/Assets/MapPin.png", UriKind.Relative)),
DataContext = poi
};
var over = new MapOverlay();
imagePoiLocation.Tap += loadClickedBuilding;
over.Content = imagePoiLocation;
over.PositionOrigin = new Point(0.5, 0.5);
over.GeoCoordinate = new GeoCoordinate(x.Latitude, x.Longitude);
return over;
}
private MapOverlay CreateClusteredItemPushpin1(ClusteredPoint clusterInfo)
{
var x = clusterInfo.Location;
var poi = new BuildingPoi { Coordinate = x, Buid = _selectedBuild };
var imagePoiLocation = new Image
{
Source = new BitmapImage(new Uri("/Assets/MapPin.png", UriKind.Relative)),
DataContext = poi
};
var over = new MapOverlay();
imagePoiLocation.Tap += loadClickedBuilding;
over.Content = imagePoiLocation;
over.PositionOrigin = new Point(0.5, 0.5);
over.GeoCoordinate = new GeoCoordinate(x.Latitude, x.Longitude);
return over;
}
As they don't have any nuget package or dll to reference directly, you need to download the source code of specific classes like ClusteringLayer & PushPin with related .cs files or the project itself to your machine and add reference of this project in your windows phone project to get ClusteringLayer and PushPin classes.
See following screenshot for ClusteringLayer class. For other classes, just import the solution to visual studio and you will see source code of all the classes. BTW, ClusteringLayer constructor need at-least one argument in cluster.
I advice you to download source code and get familiar with it's usage from samples in source code.
I have a WPF Form ,I need to print it ,i use DocumentViewer to print. but when I want to Print it or Preview , I only See the first page while i have more than one page
private void Print(object sender, RoutedEventArgs e)
{
PrintSettings printSettings = PrintSettings.Default;
UIElement container = this.Content as UIElement;
ScrollViewer containerPanel = Helper.FindVisualChildren<ScrollViewer>(container).FirstOrDefault();
var origParentDirection = containerPanel.FlowDirection;
var origDirection = (containerPanel.Content as FrameworkElement).FlowDirection;
if (containerPanel != null && containerPanel.FlowDirection == FlowDirection.RightToLeft)
{
containerPanel.FlowDirection = FlowDirection.LeftToRight;
(containerPanel.Content as FrameworkElement).FlowDirection = FlowDirection.RightToLeft;
}
var window = new Window();
string tempFileName = System.IO.Path.GetTempFileName();
System.IO.File.Delete(tempFileName);
using (XpsDocument xpsDocument = new XpsDocument(tempFileName, FileAccess.ReadWrite, System.IO.Packaging.CompressionOption.Fast))
{
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
(containerPanel.Content as FrameworkElement).Margin = new Thickness(20);
writer.Write((containerPanel.Content as FrameworkElement), printSettings.PrintTicket);
var doc = xpsDocument.GetFixedDocumentSequence();
doc.PrintTicket = printSettings.PrintTicket;
window.FlowDirection = System.Windows.FlowDirection.RightToLeft;
window.Content = new DocumentViewer { Document = doc };
window.Margin = new Thickness(10);
window.ShowDialog();
}
(containerPanel.Content as FrameworkElement).FlowDirection = origDirection;
containerPanel.FlowDirection = origParentDirection;
}
user1780436, I am currently looking for a similar answer. You are trying to print a panel, correct?
I am finding that scaling the element is the biggest problem. Which brings another issue with scaling what you want to print and not the actual visible element. You will have to copy the element to a new element.
public class Copy<T>
{
public static T DeepCopy<T>(T element)
{
string xaml = XamlWriter.Save(element);
StringReader xamlString = new StringReader(xaml);
XmlTextReader xmlTextReader = new XmlTextReader(xamlString);
var DeepCopyobject = (T)XamlReader.Load(xmlTextReader);
return DeepCopyobject;
}
}
or
myNewElement = XamlReader.Parse(XamlWriter.Save(myOldElement.DataContext)) as ElementType
I have found this answer repeatedly on multiple sites to copy/clone an element, but I have had issues with string xaml = XamlWriter.Save(element); causing stackoverflows.
I am currently using.
myNewElement = new ElementType() { DataContext = myOldElement.DataContext }
Either one you use there becomes the issue of changing the size of the Element. This is what I am looking for.
I tried a rendering pass, but that just pointed out that in my situation to use a copied/cloned element. Although while writing this I did get some of it to work, but gives me a black image, note I am trying to scale a Chart.
myNewElement.Width = newWidth;
myNewElement.Height = newHeight;
myNewElement.Measure(new System.Windows.Size(newWidth, newHeight));
myNewElement.Arrange(new Rect(0, 0, newWidth, newHeight));
I tried a layout pass, and didn't get it.
I am going to keep working on mine and I will post anything new I find. Please do the same if you find the answer.
Edit - Here is what I did. My problem and solution