I am trying to plot multiple curves in different color on my graph. I am currently using one plotter (not sure if that will work, and that is why I am posting a thread here), and here is my code:
if (_dataXChA != null && _dataXChA.Length > 1)
{
EnumerableDataSource<double> xChA = new EnumerableDataSource<double>(_dataXChA);
xChA.SetXMapping(xVal => xVal);
if (_dataYChA != null && _dataYChA.Length == _dataXChA.Length)
{
EnumerableDataSource<double> yChA = new EnumerableDataSource<double>(_dataYChA);
yChA.SetYMapping(yVal => yVal);
CompositeDataSource dsChA = new CompositeDataSource(xChA, yChA);
((LineGraph)plotter.Children.ElementAt(startIndex)).DataSource = dsChA;
plotter.FitToView();
}
}
if (_dataXChB != null && _dataXChB.Length > 1)
{
EnumerableDataSource<double> xChB = new EnumerableDataSource<double>(_dataXChB);
xChB.SetXMapping(xVal => xVal);
if (_dataYChB != null && _dataYChB.Length == _dataXChB.Length)
{
EnumerableDataSource<double> yChB = new EnumerableDataSource<double>(_dataYChB);
yChB.SetYMapping(yVal => yVal);
CompositeDataSource dsChB = new CompositeDataSource(xChB, yChB);
((LineGraph)plotter.Children.ElementAt(startIndex)).DataSource = dsChB;
//LineGraph lgChA = plotter.AddLineGraph(dsChB, _dataBrushColorChB, 1, "Data");
plotter.FitToView();
}
}
The first curve should be in green, and the second curve should be in red. plotter is CharterPlotter But when I look at the graph,I only got one curve. Then I looked at the data, it seems the curve displays the data from second data source, but the color of the curve is green.
The constructor assigns the color like this:
LineGraph lgChA = plotter.AddLineGraph(dsChA, _dataBrushColorChA, 1, "Data");
LineGraph lgChB = plotter.AddLineGraph(dsChB, _dataBrushColorChB, 1, "Data");
where,
_dataBrushColorChA = Colors.Green;
_dataBrushColorChB = Colors.Red;
Basically, I only update the data points each time when event occurs, because I have tried AddLineGraph(), but it turned out to be very slow,
so I only update the data points.
So, anyone give me any pointers? How can I handle this multiple data source situation?
It looks like you are setting the data source for the same plotter child at startIndex for both channels:
((LineGraph)plotter.Children.ElementAt(startIndex)).DataSource = dsChA;
...
((LineGraph)plotter.Children.ElementAt(startIndex)).DataSource = dsChB;
The second assignment would cause the DataSource to be overridden by dsChB, which would make it only display one line.
Maybe the index should be different for A and B?
Related
Can't figure out why this won't work. I get the same "Specified Cast is Invalid" error message. New to C#, be kind. It fails at the if(!((int)WrkRow["ManualWeight"] == 1 | etc. line. I tried a few variations of code, not all pasted here. ManualWeight is a number field in the table.
if (dt.Rows.Count > 0)
{
DataRow WrkRow = dt.Rows[0]; // ds.Tables(0).Rows(0)
if (mod1.IsTareout == true)
trim = (string)WrkRow["Trucker"];
sBarcode = $"{trim.Trim()}{(string)WrkRow["TruckNo"]} ";
XRSwatLaserCert rSwatLaserCert = new XRSwatLaserCert();
rSwatLaserCert.DataSource = dt;
DevExpress.XtraReports.UI.ReportPrintTool rpt = new DevExpress.XtraReports.UI.ReportPrintTool(rSwatLaserCert);
{
XRBarCode XrBCTareOut = new XRBarCode();
rSwatLaserCert.XrBCTareOut = new XRBarCode
{
Text = sBarcode
};
if (!((int)WrkRow["ManualWeight"] == 1 | (int)WrkRow["ManualWeight"] == 3))
{
rSwatLaserCert.XrLabelManualGross1.Visible = false;
rSwatLaserCert.XrLabelManualGross2.Visible = false;
rSwatLaserCert.XrLabelManualGross3.Visible = false;
}
2nd Try:
if (dt.Rows.Count > 0)
{
DataRow WrkRow = dt.Rows[0]; // ds.Tables(0).Rows(0)
if (mod1.IsTareout == true)
trim = (string)WrkRow["Trucker"];
sBarcode = $"{trim.Trim()}{(string)WrkRow["TruckNo"]} ";
XRSwatLaserCert rSwatLaserCert = new XRSwatLaserCert();
rSwatLaserCert.DataSource = dt;
DevExpress.XtraReports.UI.ReportPrintTool rpt = new DevExpress.XtraReports.UI.ReportPrintTool(rSwatLaserCert);
{
XRBarCode XrBCTareOut = new XRBarCode();
rSwatLaserCert.XrBCTareOut = new XRBarCode
{
Text = sBarcode
};
if (WrkRow.Field<int>("ManualWeight") != 1 | (int)WrkRow.Field<int>("ManualWeight") != 3)
{
rSwatLaserCert.XrLabelManualGross1.Visible = false;
rSwatLaserCert.XrLabelManualGross2.Visible = false;
rSwatLaserCert.XrLabelManualGross3.Visible = false;
}
3rd try:
if (dt.Rows.Count > 0)
{
DataRow WrkRow = dt.Rows[0]; // ds.Tables(0).Rows(0)
if (mod1.IsTareout == true)
trim = (string)WrkRow["Trucker"];
sBarcode = $"{trim.Trim()}{(string)WrkRow["TruckNo"]} ";
XRSwatLaserCert rSwatLaserCert = new XRSwatLaserCert();
rSwatLaserCert.DataSource = dt;
ReportPrintTool rpt = new ReportPrintTool(rSwatLaserCert);
{
XRBarCode XrBCTareOut = new XRBarCode();
rSwatLaserCert.XrBCTareOut = new XRBarCode
{
Text = sBarcode
};
var manweight = WrkRow.Field<int>("ManualWeight");
if (manweight != 1 | manweight == 3)
{
rSwatLaserCert.XrLabelManualGross1.Visible = false;
rSwatLaserCert.XrLabelManualGross2.Visible = false;
rSwatLaserCert.XrLabelManualGross3.Visible = false;
}
Take a look at this image, there's a lot going on in it, but hopefully it helps make sense of stuff:
Quick datatable, with a string, int and decimal; John's 20 years old and weighs 10.123 whatevers
When a datatable stores data in its rows, each row is effectively an array of object which means that any time you get a value of a particular column out, it comes out looking like it's an object
See where i've pulled var johnWeight - I used var to let C# decide the type, so I'm not misleading the witness. Take a look in the Locals window (bottom left) - the type is object{decimal} - this means it's a decimal, but boxed inside an object
To get it out as a decimal 10.123 we have to cast using (decimal). This is the only thing you can do at this stage; you can't strip the object wrapping off using any other type, not even one the decimal can be converted to. Check out the decimal johnWeightDecimal = (decimal)johnRow["Weight"] line; that's stripping off the object wrapping and getting the decimal value out. You can see in the locals window that it's a decimal in the type column
If you want it as an int even though it's truly a decimal and wrapped inside object, you first have to pull the object wrapping off using a cast to the true type (decimal) and then cast it again to int using (int). That's the decimal johnWeightInt = (int)(decimal)johnRow["Weight"] line..
So, if you don't know what type your item is, you can either dump it into the Immediate Window (see lower right of screenshot), or you can navigate the Locals window and expand the tree, find the Columns collection, expand it, find the entries, check their DataType..
All this adds up to the reason why plain DataTables are a massive pain in the ass to work with.. There is a solution:
Add a DataSet type of file to your project
Open it, right click, Add a DataTable to it, Add Columns to the datatable, set their datatype
Now you can just use it like any other reasonable class, that has named properties:
.. none of this casting casting casting tedium. dt[0].Weight, not (decimal)dt.Rows[0]["Weight"]
Critically, a PersonDataTable is still a DataTable (it inherits from it) so you can e.g. pass it to some data adapter that is doing SELECT Name, Age, Weight FROM users
var dt = new PersonDataTable();
someDataAdapter.Fill(dt);
There's actually a whole ecosystem within a DataSet to generate type specific DataAdapters too, but that's a bit out of scope for the post
Can you check to see if that column value is null?
Just to be safe, can you convert it to string and use Convert.ToInt32 method?
Can you make a weight variable so you can debug and hover over it to see what it is?
if(!WrkRow.IsNull("ManualWeight"))
{
var weight = Convert.ToInt32(WrkRow["ManualWeight"].ToString());
if(weight == 1 || weight == 3)
{
....
}
}
I try to add image to my first column(it might be change to another column later, it's in first column for now), so far I have do
if (item.Index == 0)
{
item.ImageGetter = delegate (object RowObj)
{
return ((RowObjectModel)RowObj).ImageToShow.ToString();
};
}
this part at start, I use a custom headerstyle and apply it on constructor while I do that I also do ImageGetter part. I also set my SmallImageList like this
ImageList IList = new ImageList();
IList.Images.Add("MyIcon", Properties.Resources.MyIcon);
mainForm.objListview.SmallImageList = IList;
I have 2 problems with this code, first I can't set my image. It's not showing on my listview.
What I do to achieve that is this :
(objListview.GetItem(z).RowObject as RowObjectModel).ImageToShow = ThumbnailImages.MyIcon;
my enum is like this :
public enum ThumbnailImages
{
NULL = 0,
MyIcon = 1,
MyIcon2 = 2,
MyIcon3 = 3,
MyIcon4 = 4,
MyIcon5 = 5
}
Second problem is I have literally no clue on how I can add a second image in the same column of same row. I'm not even sure if this is possible.. But I have to do it somehow so I'm open to any ideas.
EDIT :
Okay I found the solution to my first problem. I was not using the UpdateObject/UpdateObjects method. I marked all my items with proper images they should show and use this method and everything worked. Now all I need is to find a way to show 2 images at the same time in 1 cell.
EDIT 2 :
About my second problem I found this class --> ImagesRenderer
http://objectlistview.sourceforge.net/cs/ownerDraw.html#imagesrenderer
But I could not found any working solution so far and I don't have any clue on how this is working?
Now all I need is to find a way to show 2 images at the same time in 1 cell.
You can work around the requirement to show 2 images by showing 1 image that contains both. The example below does that. One note is that you should probably build a cache up front of all the combinations so you can return them at high speed rather than building them in the ImageGetter delegate
olv.ShowGroups = false;
//make 2 images
var img1 = new Bitmap(10, 10);
var g = Graphics.FromImage(img1);
g.FillRectangle(new SolidBrush(Color.Pink),2,2,8,8 );
var img2 = new Bitmap(10, 10);
var g2 = Graphics.FromImage(img2);
g2.FillRectangle(new SolidBrush(Color.Blue),2,2,8,8 );
var col1 = new OLVColumn();
col1.AspectName = "ToString";
col1.ImageGetter += delegate(object rowObject)
{
if(rowObject == "1")
return img1;
if(rowObject == "2")
return img2;
if (rowObject == "3")
{
var comboImg = new Bitmap(img1.Width + img2.Width, img1.Height + img2.Height);
using (var g3 = Graphics.FromImage(comboImg))
{
g3.DrawImage(img1,new Point(0,0));
g3.DrawImage(img2,new Point(img1.Width,0));
}
return comboImg;
}
return null;
};
olv.Columns.Add(col1);
olv.AddObject("1");
olv.AddObject("2");
olv.AddObject("3");
}
The solution I found thanks to #Thomas N was this --->
In my objectlistview initializer I did this :
item.Renderer = new ImageRenderer();
item.AspectGetter = delegate (object RowObj)
{
return ((RowObjectModel)RowObj).ImagesToShow;
};
item represent here column 0. I'm iterating all my columns to apply HeaderFormatStyle. I figure I could do this here too.
and before I do this initializer I also set my images
ImageList IList = new ImageList();
IList.Images.Add("NULL", Properties.Resources.Null);
IList.Images.Add("MyIcon", Properties.Resources.MyIcon);
IList.Images.Add("MyIcon2", Properties.Resources.MyIcon2);
IList.Images.Add("MyIcon3", Properties.Resources.MyIcon3);
IList.Images.Add("MyIcon4", Properties.Resources.MyIcon4);
IList.Images.Add("MyIcon5", Properties.Resources.MyIcon5);
objListview.SmallImageList = IList;
and all I have to do is calling my method I wrote inside my model class
public void AddThumbnailImage(ThumbnailImages tImage)
{
if (tImage == ThumbnailImages.NULL)
{
ImagesToShow.Clear();
}
else
{
if (!ImagesToShow.Contains((int)tImage))
ImagesToShow.Add((int)tImage);
}
}
I know my solution is very similar to yours Thomas but I just wanted to show how I did it :)
I have exported an excel document. In that, I have to apply the background color of the entire row based on some conditions. For example, If I have 1000 lines in the excel, I want to apply the background in 100 rows only.
I tried to set the color with range values. I am able to apply a color based on that. But I couldn't apply particular rows alone.
objSHT.Range["A1: A11"].Interior.Color = ColorTranslator.ToOle((System.Drawing.Color)colorConverter.ConvertFromString("#97C2EC"));
Could you please provide me the solution to apply the color in an entire row (particular row - based on column value condition)?
Works perfectly :)
for (int z=1;z<30;z++)
{
Microsoft.Office.Interop.Excel.Range row = xlWorkSheet.Rows[z];
row.EntireRow.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Azure); ;
}
I was looking for that some time ago, now i figured out how to do it :D
var col2 = xlWorkSheet.UsedRange.Columns["I:I", Type.Missing];
foreach (Microsoft.Office.Interop.Excel.Range item in col2.Cells)
{
if (Convert.ToString(item.Value) == null)
{
//item.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Cyan);
item.EntireRow.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Cyan);
}
if (Convert.ToString(item.Value) != null)
{
if (Convert.ToString(item.Value) == "Afterbuild")
{
item.Value = "Afterbuild";
}
else
{
if (Convert.ToInt32(item.Value) < 0)
{
//item.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Yellow);
item.EntireRow.Interior.Color = System.Drawing.ColorTranslator.ToOle(System.Drawing.Color.Yellow);
}
}
}
}
So, based on conditions (values) in column "I" i do change color
i have some PictureBoxes in my program with different colors and I want to count, how much boxes there are for one color. So I created a function to count it:
private void cmdCount(object sender, EventArgs e)
{
int counterWhite, counterRed, counterGreen, counterYellow, counterBlue, counterOrange = 0;
if (alreadyAdded == false)
{
lstBox.Items.Add(picA1);
lstBox.Items.Add(picA2);
lstBox.Items.Add(picA3);
//...
alreadyAdded = true;
}
//Log
String value = Convert.ToString(lstBox.Items.Count);
lblLog.Text = "Objects in array: " + value;
for(int i = 0; i < lstBox.Items.Count; i++)
{
if(lstBox.Items[i].BackColor == Color.White)
{
counterWhite += 1;
}
else if...
}
}
I know, that my for-loop will not work that way, but it's the basic idea how I want to do it.
I have put all my PictureBoxes into my list and in the for-loop I want to count them. First it should play as long as the list is long, then every time it goes to the next box and should check the color of it and then add a one to the seperate counters. The problem is that I get errors every time and I have no idea how to read out the values of the BackColors in my list for each item.
Thank you for maybe helping me out :)
You're getting an error because the ListBox.Items collection is an ObjectCollection... it has to be, since it allows you store any object you want in it.
You'll have to cast the object back to a PictureBox before accessing properties on it:
if (((PictureBox)lstBox.Items[i]).BackColor == Color.White)
{
counterWhite += 1;
}
Or you could switch to a foreach loop and cast them all at once (using LINQ):
foreach (var pBox in new lstBox.Items.Cast<PictureBox>())
{
if (pBox.BackColor == Color.White)
{
counterWhite += 1;
}
...
}
Don't use a ListBox control as temporary storage for referencing your PictureBox controls though. You could create a List<PictureBox> to store references in, and then you won't have to cast when you iterate through the collection.
Or better yet (and the one I'd choose), just query the controls on your Form and count the number of controls of type "PictureBox" that have the BackColor you're looking for (using LINQ again).
var counterWhite = Controls.OfType<PictureBox>()
.Count(p => p.BackColor == Color.White);
var counterGreen = Controls.OfType<PictureBox>()
.Count(p => p.BackColor == Color.Green);
I have a LineSeries chart. By series.IsSelectionEnabled = true; when I move the mouse over the points, I can select that node. But how can I do it when the mouse is not exactly over the point but when it's near it (above or under)? Thanks.
PS:
One more thing. How can I change the color of the column when the mouse is over it so the user can tell which one of the columns he/she is going to select.
I have created the example of the chart with the single LineSeries. You can click anywhere at the plot and the nearest point will be selected.
XAML (Change the ItemsSource property and other properties to yours):
<Charting:Chart MouseLeftButtonDown="Chart_MouseLeftButtonDown">
<Charting:Chart.Series>
<Charting:LineSeries IsSelectionEnabled="True" ItemsSource="..." ... />
</Charting:Chart.Series>
</Charting:Chart>
Code-behind:
private void Chart_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var chart = sender as Chart;
//In my example the line series is the first item of the chart series
var line = (LineSeries)chart.Series[0];
//Find the nearest point on the LineSeries
var newPoint = e.GetPosition(line);
var selectIndex = this.FindNearestPointIndex(line.Points, newPoint);
if (selectIndex != null)
{
//Select a real item from the items source
var source = line.ItemsSource as IList;
line.SelectedItem = source[selectIndex.Value];
}
}
private int? FindNearestPointIndex(PointCollection points, Point newPoint)
{
if (points == null || !points.Any())
return null;
//c^2 = a^2+b^2
Func<Point, Point, double> getLength = (p1, p2) => Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
//Create the collection of points with more information
var items = points.Select((p,i) => new { Point = p, Length = getLength(p, newPoint), Index = i });
var minLength = items.Min(item => item.Length);
//Uncomment if it is necessary to have some kind of sensitive area
//if (minLength > 50)
// return null;
//The index of the point with min distance to the new point
return items.First(item => item.Length == minLength).Index;
}
As I said this chart will select the nearest point even if you click at a great distance away from any chart point. If it isn't intended behavior, you can uncomment these lines and set any number in pixels:
//Uncomment if it is necessary to have some kind of sensitive area
if (minLength > 50)
return null;
I have written comments, but if something isn't clear you can ask and I will explain.