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.
Related
I'm trying to create a group of chart series, add data to the series and then show a few of the series by making them visible. The data changes and a user can select which series to view. I think this method will be better than clearing all series with chart.Series.Clear(); and then recreating the series in the same method.
For example a list of cars in a carpool with random mileages and then select which cars to show.
The code below doesn't work (I've commented where). The series aren't public and I think they need to be added to a public collection like a SeriesCollection but I'm not sure how.
Thanks for any help.
// create new chart series and add to a chartarea
ChartArea TestChartArea = new ChartArea();
public void CreateChartSeries()
{
List<string> lstCars = new List<string> { "Mazda", "Tesla", "Honda", "Jaguar", "Ford", "Toyota" };
foreach (string Car in lstCars)
{
// car series created correctly?
var Srs = new Series(Car);
Srs.ChartArea = TestChart.Name;
Srs.YAxisType = AxisType.Primary;
Srs.Color = Color.Red;
Srs.ChartType = SeriesChartType.Line;
TestChart.Series.Add(Srs);
}
}
// add data to chart series
public void SeriesData()
{
List<string> lstCars = new List<string> { "Mazda", "Tesla", "Honda", "Jaguar", "Ford", "Toyota" };
int[] Xseries = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] Milage = new int[10];
Random Random = new Random();
foreach (string Car in lstCars)
{
for (int M = 0; M < 10; M++)
Milage[M] = Random.Next(150, 15000);
// not sure how to call and add data to each series
Srs.Points.DataBindXY(Xseries, Milage);
}
}
// plot series - some visible
public void PlotCreatedSeries()
{
// not sure how to refer to each series
Mazda.Enabled = true;
Tesla.Enabled = false;
Honda.Enabled = true;
Jaguar.Enabled = false;
Ford.Enabled = true;
Toyota.Enabled = false;
}
The name 'Srs' you use to create the Series is only in scope i.e. usable within the loop. At the end of the loop you do add the newly created Series to your Chart:
TestChart.Series.Add(Srs);
The Series property is a public SeriesCollection. This is a bit confusing, as the singular type name and the plural property name are the same in this case, as oppose to, say, Legend(s) or ChartArea(s)..
From now on you can access it either by its index..
Series s = TestChart.Series[0] // the series you have added first
..or, more readable and more stable, by its Name property:
Series s = TestChart.Series["Mazda"] // the same series
TestChart.Series["Mazda"].Enabled = true;
Note that 'name' is also a tricky word:
When you declare a variable you give it a 'name'. Series s = new Series();
But many objects also have a property called Name: s.Name = "Volvo";
The former must be unique but the latter is just a string; do keep it unique as well, but the system will not guard you.
The former can never change, but, as you have seen, can go out of scope; the latter is just a string and you can change it.
Note that the variable itself doesn't go out of scop as long as it is still referenced somewhere, here as an element of the SeriesiesCollection Series..
Whether you want to add DataPoints bound or directly is up to you.
For the former there are many binding options.
For the latter you can use the Chart.Points methods..:
Add(DataPoint)
AddY(YValue)
AddXY(XValues, YValue(s))
Note that sometimes, especially with live charts it makes sense to insert a DataPoint using one of the InsertXXX methods!
Do look it up in MSDN! The middle version makes only sense if the x-values are either non-numeric or have to no real meaning, like, say, names.. - Note that adding meaningful x-values as numbers (or DateTimes) is crucial to use them for further goals, like tooltips, zoom or display ranges etc..
Failing to do so is probably the most common mistake newbies make. The Chart looks ok, but the data inside are broken, read lost.
I'm new to C# and programming as a whole and I've been unable to come up with a solution to what I want to do. I want to be able to create a way to display several arrays containing elements from three external text files with values on each line (e.g. #"Files\Column1.txt", #"Files\Column2.txt" #"Files\Column3.txt"). They then need to be displayed like this in the command line:
https://www.dropbox.com/s/0telh1ils201wpy/Untitled.png?dl=0
I also need to be able to sort each column individually (e.g. column 3 from lowest to highest).
I've probably explained this horribly but I'm not sure how else to put it! Any possible solutions will be greatly appreciated!
One way to do it would be to store the corresponding items from each file in a Tuple, and then store those in a List. This way the items will all stay together, but you can sort your list on any of the Tuple fields. If you were doing anything more detailed with these items, I would suggest creating a simple class to store them, so the code would be more maintainable.
Something like:
public class Item
{
public DayOfWeek Day { get; set; }
public DateTime Date { get; set; }
public string Value { get; set; }
}
The example below could easily be converted to use such a class, but for now it uses a Tuple<string, string, string>. As an intermediate step, you could easily convert the items as you create the Tuple to get more strongly-typed versions, for example, you could have Tuple<DayOfWeek, DateTime, string>.
Here's the sample code for reading your file items into a list, and how to sort on each item type:
public static void Main()
{
// For testing sake, I created some dummy files
var file1 = #"D:\Public\Temp\File1.txt";
var file2 = #"D:\Public\Temp\File2.txt";
var file3 = #"D:\Public\Temp\File3.txt";
// Validation that files exist and have same number
// of items is intentionally left out for the example
// Read the contents of each file into a separate variable
var days = File.ReadAllLines(file1);
var dates = File.ReadAllLines(file2);
var values = File.ReadAllLines(file3);
var itemCount = days.Length;
// The list of items read from each file
var fileItems = new List<Tuple<string, string, string>>();
// Add a new item for each line in each file
for (int i = 0; i < itemCount; i++)
{
fileItems.Add(new Tuple<string, string, string>(
days[i], dates[i], values[i]));
}
// Display the items in console window
fileItems.ForEach(item =>
Console.WriteLine("{0} {1} = {2}",
item.Item1, item.Item2, item.Item3));
// Example for how to order the items:
// By days
fileItems = fileItems.OrderBy(item => item.Item1).ToList();
// By dates
fileItems = fileItems.OrderBy(item => item.Item2).ToList();
// By values
fileItems = fileItems.OrderBy(item => item.Item3).ToList();
// Order by descending
fileItems = fileItems.OrderByDescending(item => item.Item1).ToList();
// Show the values based on the last ordering
fileItems.ForEach(item =>
Console.WriteLine("{0} {1} = {2}",
item.Item1, item.Item2, item.Item3));
}
I have a huge list of data say like 5k values and I want to push them into the logger.Info of log4net.
If there are individual string values, I am able to do so and accordingly modified the conversion pattern but now I want to push all those values in the list and thus is there anyway I can do so.
Also, I need to modify the conversion pattern too. For conversion pattern, i can make a string using for loop and override the Patternlayout class and set the conversion pattern for all the 5k values but how to put those values from List to those values?
string convpatt = "%date{M/d/yyyy H:mm:ss.fff}%newfield%property{Latitude}";
for (int i = 0; i < 512; i++)
{
log4net.ThreadContext.Properties[i.ToString()] = i.ToString();
convpatt += "%newfield%property{" + i.ToString()+"}";
}
var appenders = log4net.LogManager.GetRepository().GetAppenders();
foreach (var rollingFileAppender in appenders.OfType<log4net.Appender.RollingFileAppender>())
{
log4net.Layout.PatternLayout myPatten = new MyPatternLayout ();
My_app.MyPatternLayout mypatt1 = new MyPatternLayout();
mypatt1.ConversionPattern = convpatt;
mypatt1.AddConverter("newfield", typeof(NewFieldConverter));
mypatt1.ActivateOptions();
rollingFileAppender.Layout = mypatt1;
rollingFileAppender.ActivateOptions();
}
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();
}
I have a list of arrays, of which i want to take one value from each array and build up a JSON structure. Currently for every managedstrategy the currency is always the last value in the loop. How can i take the 1st, then 2nd value etc while looping the names?
List<managedstrategy> Records = new List<managedstrategy>();
int idcnt = 0;
foreach (var name in results[0])
{
managedstrategy ms = new managedstrategy();
ms.Id = idcnt++;
ms.Name = name.ToString();
foreach (var currency in results[1]) {
ms.Currency = currency.ToString();
}
Records.Add(ms);
}
var Items = new
{
total = results.Count(),
Records
};
return Json(Items, JsonRequestBehavior.AllowGet);
JSON structure is {Records:[{name: blah, currency: gbp}]}
Assuming that I understand the problem correctly, you may want to look into the Zip method provided by Linq. It's used to "zip" together two different lists, similar to how a zipper works.
A related question can be found here.
Currently, you are nesting the second loop in the first, resulting in it always returning the last currency, you have to put it all in one big for-loop for it to do what you want:
for (int i = 0; i < someNumber; i++)
{
// some code
ms.Name = results[0][i].ToString();
ms.Currency = results[1][i].ToString();
}