I'm writing a Winform application that processes a grid of products printed on a sheet. To more effectively communicate with my end user I've been tasked with creating a visual representation of the grid (as opposed to just giving them "Row x Col y in text" ) to depict which items on the sheet could not be processed.
My first thought was to use a grid of images (green check mark/red X) aligned in a grid to match the sheet that the app will actually process. The problem with that approach is that eventually we'll have different jobs that use different sheet alignments. One might be a 3x10 grid, another might be a 1x8 etc.
Is there something I can use to define an area of my form as reserved for images, and also a way to insert copies of my image file that have been resized to fit in that area?
Something like:
container.add(
new Image("myFileLocation", imgHeight, imgWidth),
(container.height / numRows),
(container.width/numCols)
);
?
Sorry if this is a stupid question. I'm comfortable in c# but have approximately zero experience designing GUIs for these things.
#ASh that's exactly what I wanted, thanks.
In case it helps anyone else out there here are some references that I found useful to learn how to use TableLayoutPanels:
Generate rows/columns at runtime.
TableLayoutPanel rows & columns at runtime
Add image file to picturebox
Set image source to picturebox which is added dynamically to Table Layout Panel
Resize image inside picturebox (stretch to fill)
Fit Image into PictureBox
Center images inside cells
https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-align-and-stretch-a-control-in-a-tablelayoutpanel-control
My code:
int rows = 7; //Will come from database
int cols = 3; //Will come from database
int colWidth;
int rowHeight;
PictureBox pbox;
Random rnd = new Random();
colWidth = 100 / cols;
if (100 % cols != 0)
colWidth--;
rowHeight = 100 / rows;
if (100 % rows != 0)
rowHeight--;
tabLP.Controls.Clear();
tabLP.ColumnStyles.Clear();
tabLP.RowStyles.Clear();
tabLP.ColumnCount = cols;
for (int i = 0; i < rows; i++)
{
tabLP.RowStyles.Add(new RowStyle(SizeType.Percent, rowHeight));
for (int j = 0; j < cols; j++)
{
tabLP.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, colWidth));
if (rnd.NextDouble() > 0.5 )
{
pbox = new PictureBox() { Image = Properties.Resources.red_X};
}
else
{
pbox = new PictureBox() { Image = Properties.Resources.checkbox_green };
}
pbox.Dock = DockStyle.Fill;
pbox.SizeMode = PictureBoxSizeMode.StretchImage;
tabLP.Controls.Add(pbox, j, i);
}
}
Related
I have a chart with multiple chart areas. When I press a button a new chart area is being created etc.
My problem is that after adding some chart areas I get the following result :
I want to have each chart area in only one column, one after the other like this :
is this possible ?
EDIT: Adding chart areas dynamically
on the left it is the chart with 3 chart areas added and the right is the chart with 4 areas.
Use property ChartArea.AlignWithChartArea
Through the use of the AlignWithChartArea, AlignmentOrientation and AlignmentStyle properties, it is possible to align or synchronize two or more chart areas horizontally, vertically or both.
First, set the AlignWithChartArea property to the name of a ChartArea object. This chart area will then be aligned, based on the AlignmentStyle setting, which defines the alignment to use, and the AlignmentOrientation setting, which defines the elements of the chart area that should be used to set the alignment.
So to put ChartArea2 below ChartArea1:
ChartArea2.AlignWithChartArea1;
ChartArea2.AlignmentStyle = AreaAlignmentStyles.Position;
ChartArea2.AlignmentOrientation = AreaAlignmentOrientation.Vertical;
Here is how you can achive what you need, also referring for your question here: https://stackoverflow.com/questions/67124754/dynamically-add-chart-areas-in-vertical-alignment
Use the Position property and please read comments inside the code:
private void button1_Click(object sender, EventArgs e)
{
List<ChartArea> Areas = new List<ChartArea>();
int numberOfAreas = 5;
chart1.Legends[0].Enabled = false;
for (int k = 1; k <= numberOfAreas; k++) // YOU WANT 5 and not 4 AREAS - changed k< to k<=
{
var S1 = new Series();
chart1.Series.Add(S1);
S1.Name = k.ToString();
for (int j = 0; j < 100; j += 10) S1.Points.AddXY(j, j / 10);
S1.Color = Color.Transparent;
Areas.Add(new ChartArea(k.ToString()));
chart1.ChartAreas.Add(Areas[k - 1]); // IT IS k-1 and not k - we start counting from 0
S1.ChartArea = k.ToString();
chart1.ChartAreas[k - 1].AlignWithChartArea = chart1.ChartAreas[k - 1].Name;
chart1.ChartAreas[k - 1].AlignmentStyle = AreaAlignmentStyles.Position;
chart1.ChartAreas[k - 1].AlignmentOrientation = AreaAlignmentOrientations.Vertical;
}
// NOW THE IMPORTANT PART
float currentHeight = 0;
foreach (var itm in Areas)
{
itm.Position.Height = 100 / numberOfAreas; // Note: the valus are in percenteges and not absolute pixels
itm.Position.Y = currentHeight;
itm.Position.X = 5;
itm.Position.Width = 95;
currentHeight += 100 / numberOfAreas;
}
}
OUTPUT:
I am adding 8 panels to a "flowLayoutPanel1". It works fine.
The problem is that it comes 4 panels on the first"row" and then 4 panels on next "row".
The thing is that I have made the size of the "flowLayoutPanel1" to visually show 3 panels on each "row", - so in this case, half of the 4th panel on each row are not seen.
But if I add them in the designer manually, it do come 3 panels on each "row" which I want.
I wonder why this is happening when I add them dynamically with this code?
flowLayoutPanel1.Controls.Clear(); int count = 0;
for (int n = 0; n < 4; n++)
{
for (int i = 0; i < latestImageLIST.Count; i++)
{
//Now add all images as panels
String imagefile = latestImageLIST[i];
if (File.Exists(imagefile))
{
Panel panel = new Panel(); count++;
panel.Name = "thepanel" + count;
panel.Size = new Size(284, 160);
panel.Margin = new Padding(3);
Image image = Image.FromFile(imagefile);
panel.BackgroundImage = image;
panel.BackgroundImageLayout = ImageLayout.Stretch;
panel.Tag = "thepanel" + count;
panel.Click += new System.EventHandler(this.panel216_Click);
flowLayoutPanel1.Controls.Add(panel);
}
}
}
I recommend using TableLayoutPanel, which is an alternative to FlowLayoutPanel. You can first determine the row and column based on the number of images you need to load, then dynamically create a Panel through a loop and add it to the specified row and column.
public void AddImages(int row, int col)
{
TableLayoutPanel tlp = new TableLayoutPanel();
int prow = 100 / row, pcol = 100 / col;
for(var i=0;i<row;i++)
{
tlp.RowStyles.Add(new RowStyle(SizeType.Percent, prow));
}
for(var i=0;i<col;i++)
{
tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, pcol));
}
//modify the add logic according to your requirement.
tlp.Controls.Add(new Panel() { Dock = DockStyle.Fill }, 1, 1);
//todo...
this.Controls.Add(tlp);
}
I have chart with multiple chart areas and every area contain a bunch of series that need to be updated with scrolling, all chart areas working well except the one below , it takes alot of resources from the memory i think and not scrolling smoothly.
I want to achieve this chart area effectively with smooth scrolling
i achieved same results using stacked bar graph, i added the points from data table, here is how the data table looks like
And as i know displaying only the portion that is needed is certainly the option the takes least resources and time,
so here is the code i use to add my points from data table
private void GraphDemo_Load(object sender, EventArgs e)
{
System.Drawing.Rectangle workingRectangle =
System.Windows.Forms.Screen.PrimaryScreen.WorkingArea;
this.chart.Size = new System.Drawing.Size(workingRectangle.Width - 50, 1000);
scroller = new VScrollBar();
scroller.Dock = DockStyle.Left;
croller.Maximum = 6000 - windowSize; //windowSize = 180;
scroller.LargeChange = 180;
scroller.Scroll += scroller_Scroll;
}
void scroller_Scroll(object sender, ScrollEventArgs e)
{
for (int s = 0; s < chart.Series.Count; s++)
{
LoadCore.chart.Series[s].Points.Clear();
}
for (float ii = 0; ii < dt.Rows.Count; ii++)
{
row = dt.Rows[(int)ii];
ts11 = Convert.ToDouble(row.ItemArray[0]) + 1; //top.value column + 1
ts12 = Convert.ToDouble(row.ItemArray[1]); //base.value column
for (double t = LoadCore.ts11; t <= LoadCore.ts12; t++)
{
//k is the index no. of each series column
for (int k = 2; k <= dt.Columns.Count - 1; k++)
{
if (scroller.Value < t)
{
if (scroller.Value + windowSize > t) //windowSize = 180;
{
//tempindex1 is the index of specific row value at specific base.value columnn
chart.Series[Convert.ToString(dt.Columns[k].ColumnName)].Points.AddXY(t, dt.Rows[tempIndex1][k]);
chart.Series[Convert.ToString(dt.Columns[k].ColumnName)]["PointWidth"] = "1";
}
}
}
}
}
}
With the above code i still can not scroll smoothly and i think it's because of the huge number of points needed to be added with the portion of 180 (window size), ...
so my question is there an alternative effective way to achieve the first image with smooth scrolling?.
should i use any other types of graph instead of stacked bar graph ? what is it?
How to get picture box image at right side in TableLayoutPanel in c#? At present i am getting two images besides but i don't need. i need only one image to their at extreme right side and i need to remove space between two rows
for (int i = 0, r = 0; i < dt.Rows.Count; i++, r++)
{
PictureBox pb = new PictureBox();
pb.ImageLocation = ../imagesDT/answered.gif
tableLayoutPanel1.Controls.Add(pb);
}
Below is my attached image i am getting issue as described above.
You need to set the Column and the Row those PictureBoxes shall sit in:
for (int i = 0, r = 0; i < dt.Rows.Count; i++, r++)
{
int maxCol = tableLayoutPanel1.ColumnCount - 1;
PictureBox pb = new PictureBox();
pb.ImageLocation = ../imagesDT/answered.gif
tableLayoutPanel1.Controls.Add(pb);
tableLayoutPanel1.SetRow(pb, i);
tableLayoutPanel1.SetColumn(pb, maxCol );
}
You can get the columns' Widths and the rows' Heights like this:
var widths = tableLayoutPanel1.GetColumnWidths();
var heights = tableLayoutPanel1.GetRowHeights();
You can fine-tune the position by setting Margins, e.g.:
pb.Margin = New Padding(50, 20, 0, 0);
To change the Heights of the Rows you can either use the Designer or you'll can set its Height in the RowStyle of each Row:
tableLayoutPanel1.RowStyles[row].SizeType = SizeType.Absolute;
tableLayoutPanel1.RowStyles[row].Height = newHeight;
To make your table layout visible during your tests you may want to insert code like this in the CellPaint event:
private void tableLayoutPanel1_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
{
Random R = new Random(1 + e.Row * 3 + e.Column * 7);
using (SolidBrush brush = new SolidBrush(
Color.FromArgb(99, Color.FromArgb( R.Next(123456789)))))
e.Graphics.FillRectangle(brush, e.CellBounds);
}
I have a datagridview in winform and would like to do two things. Resize the datagrid so that all columns are showen (no scrolls) based on the datagrid size resize the width of the winform.
tried the code below but it doesn't work*
int width = 0;
foreach (DataGridViewColumn col in information.Columns)
{
width += col.Width;
}
width += information.RowHeadersWidth;
information.ClientSize = new Size(width + 100,height);
Simple order of operations:
Set the AutoSizeColumnMode property of the DataGridView to AllCells.
Add the Width property of all the columns plus some slack for extra width from borders on the control, etc. (Maybe plus 2)
Set the Width property of the DataGridView to the width you calculated.
Set the form's Width to the Width of the DataGridView.
Up to you to actually code it.
EDIT: I am in front of a compiler now so I put this together:
Go into Visual Studio. Start a new Project. Don't put anything onto the form in the designer. Simply use this code in the initializer.
public Form1()
{
InitializeComponent();
// Create a DataGridView with 5 Columns
// Each column is going to sized at 100 pixels wide which is default
// Once filled, we will resize the form to fit the control
DataGridView dataGridView1 = new DataGridView();
for (int i = 0; i < 5; i++)
{
DataGridViewTextBoxColumn col = new DataGridViewTextBoxColumn();
dataGridView1.Columns.Add(col);
}
dataGridView1.Location = new Point(0, 0);
// Add the DataGridView to the form
this.Controls.Add(dataGridView1);
// Step 2:
// Figure out the width of the DataGridView columns
int width = 0;
foreach (DataGridViewColumn col in dataGridView1.Columns)
width += col.Width;
width += dataGridView1.RowHeadersWidth;
// Step 3:
// Change the width of the DataGridView to match the column widths
// I add 2 pixels to account for the control's borders
dataGridView1.Width = width + 2;
// Step 4:
// Now make the form's width equal to the conbtrol's width
// I add 16 to account for the form's boarders
this.Width = dataGridView1.Width + 16;
}
This code creates a DataGridView with five columns and then sizes the control and the form exactly as you requested. I followed the exact steps I outlined above (except for step 1 because I don't have any data in my columns).
This code works. So if yours is NOT working, there must be something else silly that you have going on and I can't help you.
Hello I found out how to get this working using the following code below. information is the datagrid and this is the form.
int width = 0;
this.information.RowHeadersVisible = false;
for (int i = 0; i < information.Columns.Count; i++)
width += information.Columns[i].GetPreferredWidth(DataGridViewAutoSizeColumnMode.AllCells, true);
int rows = 0;
this.information.RowHeadersVisible = false;
for (int i = 0; i < information.Rows.Count; i++)
rows += information.Rows[i].GetPreferredHeight(i, DataGridViewAutoSizeRowMode.AllCells, true);
information.Size = new Size(width +20, rows+50);
this.Width = width + 50;