I am trying to create a WPF Chart in c# code and saving it to file without displaying it on screen. This will be in a WCF Service where data is sent to the service, an image is created and and path to the image is returned.
So far I have got the image to save to file and the data on the X & Y axis is displayed but the columns on the graph are not drawn.
Does anyone know why the columns on the chart are not drawing... here is my code:
public void DoWork()
{
var newThread = new Thread(CreateImage);
newThread.SetApartmentState(ApartmentState.STA);
newThread.Start();
}
public void CreateImage()
{
var grid = new Grid {Width = 800, Height = 600, Background = new SolidColorBrush(Colors.LightBlue)};
var chart = new Chart();
grid.Children.Add(chart);
var renderTarget = new RenderTargetBitmap((int)grid.Width, (int)grid.Height, 96d, 96d, PixelFormats.Default);
var barSeries = new BarSeries
{
Title = "Fruit in Cupboard",
ItemsSource = new[]
{
new KeyValuePair<string, int>("Oranges", 18),
new KeyValuePair<string, int>("Apples", 15),
new KeyValuePair<string, int>("Melons", 2),
new KeyValuePair<string, int>("Pineapples", 4),
new KeyValuePair<string, int>("Plums", 25)
},
IndependentValueBinding = new Binding("Key"),
DependentValueBinding = new Binding("Value")
};
chart.Series.Add(barSeries);
grid.Measure(new Size(grid.Width, grid.Height));
grid.Arrange(new Rect(new Size(grid.Width, grid.Height)));
grid.UpdateLayout();
renderTarget.Render(grid);
var png = new PngBitmapEncoder();
png.Frames.Add(BitmapFrame.Create(renderTarget));
using (Stream stm = File.Create(string.Format(#"C:/Images/{0}.png", Guid.NewGuid())))
{
png.Save(stm);
}
}
I had exactly this problem with Telerik's RadChart. The explanation was as you wrote in your comment above - the series was being animated in and the snapshot was being taken before they were visible.
My solution was to set the value of the ChartArea.EnableAnimations property to false before rendering it. I am not familiar with the WPF Toolkit Chart but it is likely that there is a similar setting that you can use to disable the animations.
Note that there is a further, related issue with Telerik's BarSeries - it is necessary to set the opacity of the bars to 1. See the Telerik documentation for details.
Related
I really can't find any good documentation or any good samples on how to do this.
Here is my code. This is running in an Asp.net Core View.
var imageMarker = "https://unpkg.com/leaflet#1.7.1/dist/images/marker-icon.png";
for (var i = 0; i < locationData; i++) {
let imageName = 'image' + i;
map.imageSprite.add(imageName, imageMarker).then(function () {
//Create a data source and add it to the map.
datasource = new atlas.source.DataSource();
map.sources.add(datasource);
//Create a point feature and add it to the data source.
datasource.add(new atlas.data.Feature(new atlas.data.Point(i.longitude, i.latitude), {
name: i.name
}));
//Add a layer for rendering point data as symbols.
map.layers.add(new atlas.layer.SymbolLayer(datasource, null, {
iconOptions: {
//Pass in the id of the custom icon that was loaded into the map resources.
image: imageName,
//Optionally scale the size of the icon.
size: 0.5
},
textOptions: {
//Add some text
textField: name,
//Offset the text so that it appears on top of the icon.
offset: [0, -2]
}
}));
});
}
I'm not getting any errors. The Symbols just don't appear on the map.
The sample linked below works in my map.events.add ready function.
Image Sprite sample
Any help is much appreciated! Thanks!
Here is what ended up working for me. I worked with Microsoft Support on this. locationData contains the image, longitude and latitude. The min and max of both longitude and latitude is passed in as well to set the camera boundry. The biggest issue with my original code was setting iconOptions size to 0.5. The plugin did not like that. It's now set to 1.
function addMarkerSymbols(locationData, min_long, min_lat, max_long, max_lat)
{
map.setCamera({
bounds: [min_long, min_lat, max_long, max_lat],
padding: 50
});
$.each(locationData, function (i, item)
{
map.imageSprite.add('default-icon' + i, item.locationImage);
//Create a data source and add it to the map.
var datasource = new atlas.source.DataSource();
map.sources.add(datasource);
//Add a data set to the data source.
datasource.add(new atlas.data.Feature(new atlas.data.Point([item.longitude, item.latitude]), null));
//Create a symbol layer to render the points.
layer = new atlas.layer.SymbolLayer(datasource, null, {
iconOptions: {
//The map control has built in icons for bar, coffee and restaurant that we can use.
image: 'default-icon' + i,
anchor: 'center',
allowOverlap: true,
size: 1
}
});
map.layers.add(layer);
});
}
So in my application i have chat and i added CartesianGridLineAnnotation:
RadCartesianChart chart;
CartesianGridLineAnnotation cartesianGridLineAnnotation;
cartesianGridLineAnnotation = new CartesianGridLineAnnotation
{
Foreground = Brushes.White,
Background = Brushes.Green,
Axis = chart.VerticalAxis,
Stroke = Brushes.Red,
StrokeThickness = 1,
};
chart.Annotations.Add(...);
So as you can see i try to set Background color but the problem is that i cannot see any change in my CartesianGridLineAnnotation Label.
Other properties when changed look as expected.
Update
This is how i am populate my label:
cartesianGridLineAnnotation.Label = "bla bla";
As you can see i cannot see the backgroud color.
i will generate Stacked column chart, which will display on the x-axis cities and on the y-axis are count of genders in column. How to binding chart and this data:
List<Cities> Cities = new List<Cities>()
{
new Cities
{
Name = "Paris",
Genders = new Genders()
{
Man = 350000,
Woman = 436000
}
},
new Cities()
{
Name = "London",
Genders = new Genders()
{
Man = 698056,
Woman = 736982
}
}
};
I am trying to generate Series:
int i = 0;
foreach (var city in Cities)
{
Series s1 = new ColumnSeries();
s1.Title = city.Name;
s1.Name = "S"+i;
StackedColumnChart.Series.Add(s1);
i++;
}
i dont know how to binding data. Thanks
you can try to download a trial version of LightningChart with included demonstration examples for WinForms, WPF with Non-bindable, Semibindable and Fully Bindable examples.
You can easily look at the source code examples, e.g. ExampleStackedBars.
In a code behind you can create a field, which will be binded to the chart BarSeriesCollection property:
public static readonly DependencyProperty BarSeriesProperty =
DependencyProperty.Register(
"BarSeries",
typeof(BarSeriesCollection),
typeof(ExampleStackedBars)
);
public BarSeriesCollection BarSeries
{
get { return GetValue(BarSeriesProperty) as BarSeriesCollection; }
set { SetValue(BarSeriesProperty, value); }
}
And after create a new instance and add data to the collection:
BarSeries = new BarSeriesCollection();
GenerateData();
In XAML you can easily bind it using this line:
<lcusb:LightningChartUltimate.ViewXY>
**<lcusb:ViewXY BarSeries="{Binding BarSeries}">**
<lcusb:ViewXY.BarViewOptions>
<lcusb:BarViewOptions BarSpacing="30" Grouping="ByIndexFitWidth" Stacking="Stack" IndexGroupingFitGroupDistance="20"/>
</lcusb:ViewXY.BarViewOptions>
</lcusb:ViewXY>
</lcusb:LightningChartUltimate.ViewXY>
</lcusb:LightningChartUltimate>
or set binding path in the property tree
Example in Sparrow toolkit is for other chart type. i need Stacked bar chart. My graph must in x-axis cities and for each city must be multiple values in column. This is my chart:
<charting:Chart
Grid.Column="0"
Grid.Row="0"
x:Name="StackedColumnChart"
Title="Stacked Column"
Margin="5">
<charting:StackedColumnSeries>
</charting:StackedColumnSeries>
</charting:Chart>
I dont know what is series. Series is columns? How to assign multiple value (in my example genders) to one column?
is there any way to manage items in legend? I mean e.q. remove some items from legend but not from whole plot? I know that each serie in plot is linked with one item in legend, but i want to break this rule and put to legend only selected series.
Thanks for your request.
If you don't assign a title to the series (LineSeries, etc), it won't be represented on the legend:
var plotModel = new PlotModel();
plotModel.LegendTitle = "Legend";
plotModel.LegendPosition = LegendPosition.RightBottom;
plotModel.Series.Add(new LineSeries
{
ItemsSource = someItemSource,
Color = OxyColors.Red,
//Title = "title" <- Title commented
});
Starting v2.0.1, the legend doesn't automatically appear and all PlotModel.LegendXxx members have been removed. You now have to manually add one legend:
var plotModel = new PlotModel();
plotModel.Legends.Add(new Legend() {
LegendTitle = "Legend",
LegendPosition = LegendPosition.RightBottom,
});
plotModel.Series.Add(new LineSeries
{
ItemsSource = someItemSource,
Color = OxyColors.Red,
//Title = "title" <- Title commented
});
I have the following code:
void Test()
{
currentImage.Source = GetBitmap();
RenderTargetBitmap rtb = new RenderTargetBitmap(100, 100, 96.0, 96.0, PixelFormats.Default);
rtb.Render(currentImage);
}
This code is supposed to render currentImage, which is an Image control in my xaml to a RenderTargetBitmap.
It doesn't work, rtb returns a blank image, the problem is currentImage didn't render itself yet and so this behavior is expected, I think...
To workaround this problem, I wrote this code:
void Test()
{
currentImage.Source = GetBitmap();
this.Dispatcher.BeginInvoke((Action)delegate()
{
RenderTargetBitmap rtb = new RenderTargetBitmap(100, 100, 96.0, 96.0, PixelFormats.Default);
rtb.Render(currentImage);
}, System.Windows.Threading.DispatcherPriority.Render, null);
}
Basically, I wait for currentImage to be rendered and then I can get it properly rendered to my RenderTargetBitmap.
Is there any way to make it work without using this workaround? Force the Image control to render in memory maybe?
thanks!
use a ViewBox to render in memory
Grid grid = new System.Windows.Controls.Grid() { Background = Brushes.Blue, Width = 200, Height = 200 };
Viewbox viewbox = new Viewbox();
viewbox.Child = grid; //control to render
viewbox.Measure(new System.Windows.Size(200, 200));
viewbox.Arrange(new Rect(0, 0, 200, 200));
viewbox.UpdateLayout();
RenderTargetBitmap render = new RenderTargetBitmap(200, 200, 150, 150, PixelFormats.Pbgra32);
render.Render(viewbox);
I think this is a BETTER answer . The viewbox didn't work completely as expected, and it turned out to be an unnecessary overhead.
Here is a copy of that answer (instead of just link)
You need to force a render of the item, or wait for the item to be rendered. You can then use the ActualHeight and ActualWidth properties.
To force a render:
MenuItem item = new MenuItem();
item.Header = "bling";
item.Icon = someIcon;
//Force render
item.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
item.Arrange(new Rect(item.DesiredSize));
In this example the MenuItem has not been given an explicit height or width. However, forcing the render will render it taking the supplied header text and icon into consideration.