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();
}
Related
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);
}
}
}
I have a typical web page that has check boxes to filter on and then there is a drop down to sort by low to high and high to low. The list/xpath code below returns 25 elements with varying prices.
What I want to do is assert that when I select the sort option from low to high, that my prices should look like $10, $10, $12, $15, etc. What is my next step here? It has been awhile since I have worked with list and my mind is drawing a blank.
I think I used Linq last time, sorted the results and ran a compare but after looking at this for three hours I have gone brain dead.
Suggestions?
public List<string> GetInitialSortByPrice ()
{
List<String> item = new List<string>();
IReadOnlyList<IWebElement> cells =
Browser.FindElements_byXpath("//h4[#class='price']");
foreach (IWebElement cell in cells)
{
item.Add(cell.Text);
}
return item;
}
Solution....
List<String> item = new List<string>();
IReadOnlyList<IWebElement> cells =
Browser.FindElements_byXpath("//h4[#class='price']");
foreach (IWebElement cell in cells)
{
item.Add(cell.Text.Replace("$", ""));
}
List<decimal> listA = item.Select(s =>
decimal.Parse(s)).ToList();
List<decimal> listB = listA.OrderBy(x => x).ToList();
Assert.IsTrue(listA.SequenceEqual(listB));
This was for the descending order:
List<decimal> listB = listA.OrderByDescending(i => i).ToList();
I have data set that I have generate every permutation, then check some properties on it to see if is an object that I want to keep and use. The number of permutations is staggering, in the quadrillions. Is there anything that you can see in the code below that I can use to speed this up? I suspect that I can't speed it up to a reasonable amount of time, so I'm also looking at possibly sharding it onto multiple servers to process, but I'm having a hard time deciding where to shard it.
Any opinions or ideas is appreciated.
var boats = _warMachineRepository.AllBoats();
var marines = _warMachineRepository.AllMarines();
var bombers = _warMachineRepository.AllBombers().ToList();
var carriers = _warMachineRepository.AllCarriers().ToList();
var tanks = _warMachineRepository.AllTanks().ToList();
var submarines = _warMachineRepository.AllSubmarines();
var armies = new List<Army>();
int processed = 0;
Console.WriteLine((long)boats.Count*marines.Count*bombers.Count*carriers.Count*tanks.Count*submarines.Count);
// 70k of these
Parallel.ForEach(boats, new ParallelOptions(){MaxDegreeOfParallelism = Environment.ProcessorCount},boat =>
{
// 7500 of these
foreach (var marine in marines)
{
// 200 of these
foreach (var bomber in bombers)
{
// 200 of these
foreach (var carrier in carriers)
{
// 400 of these
foreach (var tank in tanks)
{
// 50 of these
foreach (var submarine in submarines)
{
var lineup = new Army()
{
Tank = tank,
Submarine = submarine,
Carrier = carrier,
Marine = marine,
Bomber = bomber,
Boats = boat
};
if (army.Hitpoints > 50000)
{
lock (lockObject)
{
armies.Add(lineup);
}
}
processed++;
if (processed%10000000 == 0)
{
Console.WriteLine("Processed: {0}, valid: {1}, DateTime: {2}", processed, armies.Count, DateTime.Now);
}
}
}
}
}
}
});
return armies;
If this code is referring to a simulation you might want to add some optimizations by:
Mark an object as changed (put it in a list) when it changes so there is no need to search multiple times
Decrease/throttle/tune the object update frequency
Use other information available to filter objects: are objects close to one another so they might affect/hurt/heal each other -> only then investigate changes
Change the data structure; by putting all attributes of all objects in a smartly setup matrix you might be able to use simple matrix multiplication to have the object interact. You might even be able to offload the multiplication to the GPU
You might be asking too much: so scale out by using more nodes/machines.
I am trying to sort a collection of objects in C# by a custom property.
(For context, I am working with the Twitter API using the Twitterizer library, sorting Direct Messages into conversation view)
Say a custom class has a property named label, where label is a string that is assigned when the class constructor.
I have a Collection (or a List, it doesn't matter) of said classes, and I want to sort them all into separate Lists (or Collections) based on the value of label, and group them together.
At the moment I've been doing this by using a foreach loop and checking the values that way - a horrible waste of CPU time and awful programming, I know. I'm ashamed of it.
Basically I know that all of the data I have is there given to me, and I also know that it should be really easy to sort. It's easy enough for a human to do it with bits of paper, but I just don't know how to do it in C#.
Does anyone have the solution to this? If you need more information and/or context just ask.
Have you tried Linq's OrderBy?
var mySortedList = myCollection.OrderBy(x => x.PropertyName).ToList();
This is still going to loop through the values to sort - there's no way around that. This will at least clean up your code.
You say sorting but it sounds like you're trying to divide up a list of things based on a common value. For that you want GroupBy.
You'll also want ToDictionary to switch from an IGrouping as you'll presumably be wanting key based lookup.
I assume that the elements within each of the output sets will need to be sorted, so check out OrderBy. Since you'll undoubtedly be accessing each list multiple times you'll want to collapse it to a list or an array (you mentioned list) so I used ToList
//Make some test data
var labels = new[] {"A", "B", "C", "D"};
var rawMessages = new List<Message>();
for (var i = 0; i < 15; ++i)
{
rawMessages.Add(new Message
{
Label = labels[i % labels.Length],
Text = "Hi" + i,
Timestamp = DateTime.Now.AddMinutes(i * Math.Pow(-1, i))
});
}
//Group the data up by label
var groupedMessages = rawMessages.GroupBy(message => message.Label);
//Convert to a dictionary for by-label lookup (this gives us a Dictionary<string, List<Message>>)
var messageLookup = groupedMessages.ToDictionary(
//Make the dictionary key the label of the conversation (set of messages)
grouping => grouping.Key,
//Sort the messages in each conversation by their timestamps and convert to a list
messages => messages.OrderBy(message => message.Timestamp).ToList());
//Use the data...
var messagesInConversationA = messageLookup["A"];
var messagesInConversationB = messageLookup["B"];
var messagesInConversationC = messageLookup["C"];
var messagesInConversationD = messageLookup["D"];
It sounds to me like mlorbetske was correct in his interpretation of your question. It sounds like you want to do grouping rather than sorting. I just went at the answer a bit differently
var originalList = new[] { new { Name = "Andy", Label = "Junk" }, new { Name = "Frank", Label = "Junk" }, new { Name = "Lisa", Label = "Trash" } }.ToList();
var myLists = new Dictionary<string, List<Object>>();
originalList.ForEach(x =>
{
if (!myLists.ContainsKey(x.Label))
myLists.Add(x.Label,new List<object>());
myLists[x.Label].Add(x);
});
We are using the dotNETCHARTING to portray our charts, they accepts Series for their SeriesCollection. This is a new chart I'm working on, all the previous ones have a 1:1 relation between value shown and value extracted. Now I have a list of values to show as a list of values 12:12.
I currently have my 2 lists of data showing (Actual vs Budgetted over the past 12 months) - but in a single Series, where they should be 2 Series. I have the data sorted and listed as needed, well almost listed right.
Restrictions: .NET 3.5 (VS2008), dotNETCHARTING.
It will be a very sad solution if I had to create 12 SQLs for each month and 12 for the budgetted. From what I see that is not necessary, as soon as I find a way to seperate each list into seperate Series.
Each Module has a List<ModuleValue>, I have tried with a Dictionary<int, List<ModuleValue>> so that each series of values (12months) could have a seperate List.
I have tried the For each list of Values, add each value in the list to a Series, repeat until out of List of values. (Foreach in a Foreach)
My question is: Can anyone give me some pointers to a possible solution. Graph below is per say correct, if there weren't lined up one after the other, but started and ended at the same timeframe (month). Eg budget for Jan compares to actual for Jan. I'm not asking about the dotNETCHARTING module, they have plenty of help. I'm asking for this mid-between and how it feeds the data to the module.
Main logic body:
protected override void CreateChildControls()
{
base.CreateChildControls();
//_chart.Type = ChartType.Combo;
_chart.DefaultSeries.Type = SeriesType.Line;
// Up for change - between here
IList listSeries = new List();
listSeries.Add(GetSeries(_module)); // This line should be listSeries = GetMultipleSeries(_module); or to that effect.
foreach (var series in listSeries)
{
_chart.SeriesCollection.Add(series);
}
// Up for change - and here
// This shows the title above the chart:
_chart.Title = _module.Title;
// This shows the title below the chart:
//_chart.XAxis.Label = new Label(_module.Title);
_chart.TitleBox.Line.Color = Charter.BackgroundColor;
base.SetAreaStyles();
base.SetLinkUrl(_module.LinkUrl);
}
This logic is the old logic, should remain as is - because all the other charts rely on it.
Can be used as a point of reference. Consider this logic locked.
protected Series GetSeries(FrontModule module)
{
Series series = new Series(module.Title);
foreach (var value in module.Values)
{
string sFieldTitle = value.Text;
Element element = new Element(sFieldTitle, value.Value);
element.Color = Charter.GetColor(value.ColorIndex);
series.Elements.Add(element);
string sToolTip = string.Format
("{0}: {1:N0}"
, value.Tooltip
, value.Value);
element.ToolTip = sToolTip;
if (!string.IsNullOrEmpty(value.LinkUrl))
{
element.URL = Page.ResolveUrl(value.LinkUrl);
}
ChartTooltip += string.Concat(sToolTip, ", ");
}
ChartTooltip += "\n";
return series;
}
This is the new Logic and should be changed to reflect the desired logic. Consider this as free as can be.
protected List GetMultipleSeries(FrontModule module)
{
List listSeries = new List();
Series series = new Series(module.Title);
foreach (var keyPair in module.DictionaryValues)
{
string sFieldTitle = keyPair.Value.Text;
Element element = new Element(sFieldTitle, keyPair.Value.Value);
element.Color = Charter.GetColor(keyPair.Value.ColorIndex);
series.Elements.Add(element);
string sToolTip = string.Format
("{0}: {1:N0}"
, keyPair.Value.Tooltip
, keyPair.Value.Value);
element.ToolTip = sToolTip;
if (!string.IsNullOrEmpty(keyPair.Value.LinkUrl))
{
element.URL = Page.ResolveUrl(keyPair.Value.LinkUrl);
}
ChartTooltip += string.Concat(sToolTip, ", ");
}
listSeries.Add(series);
ChartTooltip += "\n";
return listSeries;
}
This is how it shouldn't be, listing data in a sequal line. Though it shows it has all the required data.
I'd appreciate anything you could add. Thank you.
You need two Series objects:
protected List GetMultipleSeries(FrontModule module)
{
List listSeries = new List();
Series seriesActual = new Series(module.Title);
Series seriesBudgetted = new Series(module.Title);
foreach (var keyPair in module.DictionaryValues)
{
string sFieldTitle = keyPair.Value.Text;
Element element = new Element(sFieldTitle, keyPair.Value.Value);
element.Color = Charter.GetColor(keyPair.Value.ColorIndex);
// Is is actual or budgetted
if (keyPair.Value.IsActual)
seriesActual.Elements.Add(element);
else
seriesBudgetted.Elements.Add(element);
string sToolTip = string.Format
("{0}: {1:N0}"
, keyPair.Value.Tooltip
, keyPair.Value.Value);
element.ToolTip = sToolTip;
if (!string.IsNullOrEmpty(keyPair.Value.LinkUrl))
{
element.URL = Page.ResolveUrl(keyPair.Value.LinkUrl);
}
ChartTooltip += string.Concat(sToolTip, ", ");
}
listSeries.Add(seriesActual);
listSeries.Add(seriesBudgetted);
ChartTooltip += "\n";
return listSeries;
}
I'm assuming you have some way of testing whether the points are actual or budgetted for the if statement.