Update Chart data in PowerPoint - c#

I am creating PowerPoint by loading template and updating the data and saving as a new copy. I am using GemBox.Presentation and GemBox.SpreadSheet for that. I can access the chart data using spreadsheet however I am not sure how to update it.
Here is how i access chart from the template slide.
var chart = ((GraphicFrame)currentSlide.Content.Drawings[0]).Chart;
var stackedColumn = (GemBox.Spreadsheet.Charts.ColumnChart)chart.ExcelChart;
I have tried to update the data by converting to list but it still did not update.
foreach (var item in data)
{
var seriesItem = data;
var valueList = item.Values.Cast<int>().Select(x => x).ToList();
// values to update in chart
List<int> myValues = new List<int>(new int[] { 1, 2});
for (int v = 0; v < valueList.Count(); v++)
{
valueList[v] = myValues[v];
}
}
I also saw this example https://www.gemboxsoftware.com/presentation/examples/powerpoint-charts/412 to update chart however this adds the series but not updates the current data.
If you can please share if there is any support available for this component to perform to update chart data that would be appreciated. Thanks

One way is to use one of the ChartSeries.SetValues methods, as mentioned in the previous answer.
Another way is to update the underlining Excel cell.
For instance, in that example you linked to, you could do this:
var lineChart = (LineChart)chart.ExcelChart;
var lineSeries = lineChart.Series[0];
// Get series source cells.
string[] cellsReference = lineSeries.ValuesReference.Split('!');
var cells = lineChart.Worksheet.Parent.Worksheets
.First(w => w.Name == cellsReference[0].Trim('\''))
.Cells.GetSubrange(cellsReference[1]);
// Update cells with doubled values.
foreach (var cell in cells)
cell.SetValue(cell.DoubleValue * 2);

I have managed to update the data on the spreadsheet that is associated with powerpoint charts.
Class named ChartSeries contains method named SetValues() that will set the chart series values.
There are two ways you can use this method as it takes IEnumerable and Object [] as parameters as shown below.
public void SetValues(IEnumerable values)
public void SetValues(params object[] values)
And here is how i have access that value and updated it.
foreach (var item in data)
{
var seriesItem = data;
foreach (var value in seriesItem)
{
if (value.DisplayName == "[Open]")
{
value.SetValues(50,25);
}
}
}

Related

display list items into data grid view c#

I have a super-class (abstract) and then 2 inherited classes.
SuperClass: Sessions
Class 1: Cycling
Class 2: Running
I also have a list that will hold all of my objects private List<Session> allSessions = new List<Session>();
I also have declared some arrays that hold hard-coded data to populate my objects.
Also, Running and Cycling has an overridden ToString() method that displays different data depending on the class.
public override string ToString() => $"Cycle Average RPM is {AverageRpm} and Average Resistance is {AverageResistance}";
I am using a for loop to create and add new objects into my list like this
for (int i = 0; i < id.Length; i++)
{
Cycling Cycle = new Cycling(id[i], titles[i], date[i], duration[i], difficulty[i], instructor[i],
description[i], averageRpm[i], averageResistance[i]);
// Add new objects to list
allSessions.Add(Cycle);
}
I have a dataGridView that is getting everything from my list and displays it like this:
My problem now is, that I want to display only specific data depending on what you choose in the ComboBox, but something is not working,
The overridden ToString() is not added to the list for some reason and whenever I choose a different option from the ComboBox, nothing is being displayed.
EDIT 1:
// Filter Sessions by type using Linq
var sessions = new List<Session>();
var cyclingSessions = sessions.OfType<Cycling>();
var runningSessions = sessions.OfType<Running>();
listBox1.DataSource = null;
listBox1.Items.Clear();
if (cboMenu.SelectedIndex == 0)
{
// Populate GridView with data
dataDisplay.DataSource = allSessions;
}
else if (cboMenu.SelectedIndex == 1)
{
// Populate GridView with data
dataDisplay.DataSource = cyclingSessions;
}
else
{
// Populate GridView with data
dataDisplay.DataSource = runningSessions;
}
}
You need to filter your sessions list and set that as your data source you can easily filter the list using OfType from System.Linq It would look something like this:
var sessions = new List<Sessions>();
var cyclingSessions = sessions.OfType<Cycling>();
var runningSessions = sessions.OfType<Running>();
dataDisplay.DataSource = cyclingSessions;

c# FlaUi get all the Values from DataGridView of another program

I'm trying to pull all the values from another program's DataGridBox. For that I'm using FlaUi. I made a code that does what I want. However, it is very slow. Is there a faster way to pull up all the values from another program's DataGridView using FlaUi?
my code:
var desktop = automation.GetDesktop();
var window = desktop.FindFirstDescendant(cf => cf.ByName("History: NEWLIFE")).AsWindow();
var table = window.FindFirstDescendant(cf => cf.ByName("DataGridView")).AsDataGridView();
int rowscount = (table.FindAllChildren(cf => cf.ByProcessId(30572)).Length) - 2;
// Remove the last row if we have the "add" row
for (int i = 0; i < rowscount; i++)
{
string string1 = "Row " + i;
string string2 = "Symbol Row " + i;
var RowX = table.FindFirstDescendant(cf => cf.ByName(string1));
var SymbolRowX = RowX.FindFirstDescendant(cf => cf.ByName(string2));
SCAN.Add("" + SymbolRowX.Patterns.LegacyIAccessible.Pattern.Value);
}
var message = string.Join(Environment.NewLine, SCAN);
MessageBox.Show(message);
Thank you in-advance
Searching for descendants is pretty slow as it will go thru all objects in the tree until it finds the desired control (or there are no controls left). It might be much faster to use the grid pattern to find the desired cells or get all rows at once and loop thru them.
Alternatively you could try caching as UIA uses inter process calls which are generally slow. So each Find method or value property does such a call. If you have a large grid, that can sum up pretty badly. For that exact case, using UIA Caching could make sense.
For that, you would get everything you need (all descendants of the table and the LegacyIAccessible pattern) in one go inside a cache request and then loop thru those elements in the code with CachedChildren and such.
A simple example for this can be found at the FlaUI wiki at https://github.com/FlaUI/FlaUI/wiki/Caching:
var grid = <FindGrid>.AsGrid();
var cacheRequest = new CacheRequest();
cacheRequest.TreeScope = TreeScope.Descendants;
cacheRequest.Add(Automation.PropertyLibrary.Element.Name);
using (cacheRequest.Activate())
{
var rows = _grid.Rows;
foreach (var row in rows)
{
foreach (var cell in row.CachedChildren)
{
Console.WriteLine(cell.Name);
}
}
}

Copy/Clone an Excel Chart with EPPlus

I'm using EPPlus in my project and I know you can copy an existing Shape however there is no method for copying an existing Chart.
I have setup a workbook with template charts that I need to duplicate and update the series to point to different datatable/sets.
I can populate the data no worries and can create new charts, but then need to size and position and style. It would be a lot easier to just clone the chart template and modify series and position to simplify the code.
Currently I use this approach:
// wb is an ExcelWorkbook
ExcelWorksheet ws = wb.Worksheets[sheetIdx];
ExcelChart chart = (ExcelChart)ws.Drawings[0];
ExcelChart cc = ws.Drawings.AddChart("Chart " + (i + 2), eChartType.ColumnClustered);
// invoke methods that will position and size new chart
// copy starting chart xml so will have identical styling, series, legend etc
var xml = XDocument.Parse(chart.ChartXml.InnerXml);
XNamespace nsC = "http://schemas.openxmlformats.org/drawingml/2006/chart";
XNamespace nsA = "http://schemas.openxmlformats.org/drawingml/2006/main";
// modify xml to update Category, Title and Values formulas
var fs = xml.Descendants(nsC + "f");
foreach (var f in fs)
{
f.Value = ws.Cells[f.Value].Offset(chartNumRows + 1, 0).FullAddressAbsolute;
}
// set new chart xml to modified xml.
cc.ChartXml.InnerXml = xml.ToString();
Which works, but there are several drawbacks.
1) The chart.series of the clone (cc in my example) has not been set, peeking at the code this is because it is only done during the object construction. If I could get this property to update then I would be able to easily resolve the second issue
2) I need to remove all series and add new ones and because the series property isn't initialised properly this is harder than it should be.
Any help getting properties to initialise in the chart or a better method of cloning the original would be greatly appreciated!
It seems like there is no built-in functionality for this and an other reload methods I could come up with required too many changes to the EPPlus source, so until I find a better solution I have added the following method to EPPlus\Drawings\ExcelDrawings.cs
public ExcelChart CloneChart(ExcelChart SourceChart, String NewName)
{
// Create clone
var tempClone = this.AddChart(NewName, SourceChart.ChartType, null);
tempClone.ChartXml.InnerXml = SourceChart.ChartXml.InnerXml;
// Reload clone
using (tempClone.Part.Stream = new MemoryStream())
{
// Create chart object using temps package and xml
var chartXmlBytes = Encoding.ASCII.GetBytes(tempClone.ChartXml.OuterXml);
tempClone.Part.Stream.Write(chartXmlBytes, 0, chartXmlBytes.Length);
var finalClone = ExcelChart.GetChart(this, tempClone.TopNode);
// Remove old from collection
var index = _drawingNames[tempClone.Name];
var draw = _drawings[index];
for (int i = index + 1; i < _drawings.Count; i++)
_drawingNames[_drawings[i].Name]--;
_drawingNames.Remove(draw.Name);
_drawings.Remove(draw);
// Add new to collection
finalClone.Name = tempClone.Name;
_drawings.Add(finalClone);
_drawingNames.Add(finalClone.Name, _drawings.Count - 1);
// Done
return finalClone;
}
}

Accumulate values in chart series - WPF DevExpress

I am creating several line series for a chart control in DevExpress at run-time. The series must be created at run-time since the number of series can vary from the data query I do. Here is how I create the series:
foreach (var item in lstSPCPrintID)
{
string seriesName = Convert.ToString(item);
LineSeries2D series = new LineSeries2D();
dxcSPCDiagram.Series.Add(series);
series.DisplayName = seriesName;
var meas = from x in lstSPCChart
where x.intSPCPrintID == item
select new { x.intSPCMeas };
foreach (var item2 in meas)
{
series.Points.Add(new SeriesPoint(item2.intSPCMeas));
}
}
This happens inside a backgroundworker completed event and all the data needed is in the appropriate lists. In the test instance I am running, 6 series are created.
Each series consists of some test measurements that I need in the x-axis. These measurements can be the same value (and are the same value in a lot of cases). What I want then is for the y-axis to contain the count of how many times a measurement is for example -21. This will in the end create a curve.
Right now I create a series point for each measurement, but I do not know how to handle the ArgumentDataMember/ValueDataMember in this specific scenario. Is there a way for the chart to automatically do the counting or do I need to do it manually? Can anyone help me back on track?
I ended up doing a distinct count of the measurements before adding the series points.
foreach (var item in lstSPCPrintID)
{
string seriesName = String.Format("Position: {0}", Convert.ToString(item));
LineStackedSeries2D series = new LineStackedSeries2D();
series.ArgumentScaleType = ScaleType.Numerical;
series.DisplayName = seriesName;
series.SeriesAnimation = new Line2DUnwindAnimation();
var meas = from x in lstSPCChart
where x.intSPCPrintID == item
select new { x.dblSPCMeas };
var measDistinctCount = meas.GroupBy(x => x.dblSPCMeas).Select(group => new { Meas = group.Key, Count = group.Count() }).OrderBy(y => y.Meas);
foreach (var item2 in measDistinctCount)
{
series.Points.Add(new SeriesPoint(item2.Meas, item2.Count));
}
dxcSPCDiagram.Series.Add(series);
series.Animate();
}

Is there a way to dynamically create an object at run time in .NET 3.5?

I'm working on an importer that takes tab delimited text files. The first line of each file contains 'columns' like ItemCode, Language, ImportMode etc and there can be varying numbers of columns.
I'm able to get the names of each column, whether there's one or 10 and so on. I use a method to achieve this that returns List<string>:
private List<string> GetColumnNames(string saveLocation, int numColumns)
{
var data = (File.ReadAllLines(saveLocation));
var columnNames = new List<string>();
for (int i = 0; i < numColumns; i++)
{
var cols = from lines in data
.Take(1)
.Where(l => !string.IsNullOrEmpty(l))
.Select(l => l.Split(delimiter.ToCharArray(), StringSplitOptions.None))
.Select(value => string.Join(" ", value))
let split = lines.Split(' ')
select new
{
Temp = split[i].Trim()
};
foreach (var x in cols)
{
columnNames.Add(x.Temp);
}
}
return columnNames;
}
If I always knew what columns to be expecting, I could just create a new object, but since I don't, I'm wondering is there a way I can dynamically create an object with properties that correspond to whatever GetColumnNames() returns?
Any suggestions?
For what it's worth, here's how I used DataTables to achieve what I wanted.
// saveLocation is file location
// numColumns comes from another method that gets number of columns in file
var columnNames = GetColumnNames(saveLocation, numColumns);
var table = new DataTable();
foreach (var header in columnNames)
{
table.Columns.Add(header);
}
// itemAttributeData is the file split into lines
foreach (var row in itemAttributeData)
{
table.Rows.Add(row);
}
Although there was a bit more work involved to be able to manipulate the data in the way I wanted, Karthik's suggestion got me on the right track.
You could create a dictionary of strings where the first string references the "properties" name and the second string its characteristic.

Categories

Resources