C# Unable to dynamicly auto-size columns evenly in TableLayoutPanel - c#

I have a WinForm that has a TableLayoutPanel control. My code will detect the number of attached monitors on screen, create a column per monitor, and then add a button for each display within each individual column in the TableLayoutControl so I can ensure that no matter how many monitors are attached, the buttons will appear "centered" across the form. One/two monitors renders just fine, however three monitors results in end columns not being evenly distributed.
Here is my code:
int count = Screen.AllScreens.Count();
this.monitorLayoutPanel.ColumnCount = count;
ColumnStyle cs = new ColumnStyle(SizeType.Percent, 100 / count);
this.monitorLayoutPanel.ColumnStyles.Add(cs);
this.monitorLayoutPanel.AutoSize = true;
var buttonSize = new Size(95, 75);
int z = 0;
foreach (var screen in Screen.AllScreens.OrderBy(i => i.Bounds.X))
{
Button monitor = new Button
{
Name = "Monitor" + screen,
AutoSize = true,
Size = buttonSize,
BackgroundImageLayout = ImageLayout.Stretch,
BackgroundImage = Properties.Resources.display_enabled,
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font("Segoe UI", 10, FontStyle.Bold),
ForeColor = Color.White,
BackColor = Color.Transparent,
Text = screen.Bounds.Width + "x" + screen.Bounds.Height,
Anchor = System.Windows.Forms.AnchorStyles.None
};
this.monitorLayoutPanel.Controls.Add(monitor, z, 0);
z++;
monitor.MouseClick += new MouseEventHandler(monitor_Click);
}
I've tried making the buttons smaller, and increased the form size but the last column is always smaller than the first two. I can't understand it!

Clear ColumnStyles first.
this.monitorLayoutPanel.ColumnStyles.Clear();
then:
int count = Screen.AllScreens.Count();
for (int i = 0; i < count; i++)
{
ColumnStyle cs = new ColumnStyle(SizeType.Percent, (float)100 / count);
this.monitorLayoutPanel.ColumnStyles.Add(cs);
}
this.monitorLayoutPanel.AutoSize = true;
...

Reza Aghaei pointed me to this thread How to create a magic square using Windows Forms? which pointed me in the right direction. Updated (and working) code below. :)
int screens = Screen.AllScreens.Count();
this.monitorLayoutPanel.ColumnStyles.Clear();
this.monitorLayoutPanel.ColumnCount = screens;
this.monitorLayoutPanel.AutoSize = true;
int z = 0;
foreach (var screen in Screen.AllScreens.OrderBy(i => i.Bounds.X))
{
var percent = 100f / screens;
this.monitorLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, percent));
Button monitor = new Button
{
Name = "Monitor" + screen,
Size = new Size(95, 75),
BackgroundImageLayout = ImageLayout.Stretch,
BackgroundImage = Properties.Resources.display_enabled,
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font("Segoe UI", 10, FontStyle.Bold),
ForeColor = Color.White,
BackColor = Color.Transparent,
Text = screen.Bounds.Width + "x" + screen.Bounds.Height,
Anchor = System.Windows.Forms.AnchorStyles.None
};
this.monitorLayoutPanel.Controls.Add(monitor, z, 0);
z++;
monitor.MouseClick += new MouseEventHandler(monitor_Click);

Related

C# How to nextline in e.Drawing when printing images

I'm generating a barcode depending on how many inputs that the user set in the numericUpDown control. The problem is when generating a lot of barcodes, the other barcodes cannot be seen in the printpreviewdialog because it I cannot apply a nextline or \n every 4-5 Images.
int x = 0, y = 10;
for (int i = 1; i <= int.Parse(txtCount.Text); i++)
{
idcount++;
connection.Close();
Zen.Barcode.Code128BarcodeDraw barcode = Zen.Barcode.BarcodeDrawFactory.Code128WithChecksum;
Random random = new Random();
string randomtext = "MLQ-";
int j;
for (j = 1; j <= 6; j++)
{
randomtext += random.Next(0, 9).ToString();
Image barcodeimg = barcode.Draw(randomtext, 50);
resultimage = new Bitmap(barcodeimg.Width, barcodeimg.Height + 20);
using (var graphics = Graphics.FromImage(resultimage))
using (var font = new Font("Arial", 11)) // Any font you want
using (var brush = new SolidBrush(Color.Black))
using (var format = new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Far}) // Also, horizontally centered text, as in your example of the expected output
{
graphics.Clear(Color.White);
graphics.DrawImage(barcodeimg, 0, 0);
graphics.DrawString(randomtext, font, brush, resultimage.Width / 2, resultimage.Height, format);
}
x += 25;
}
e.Graphics.DrawImage(resultimage, x, y);
}
There's no "new lines" in rasterized graphics. There's pixels. You've got the right idea, every n number of images, add a new line. But since you're working with pixels, let's say every 4 images you're going to need to add a vertical offset by modifying the y coordinate of all your graphics draw calls. This offset, combined with a row height in pixels could look something like this:
var rowHeight = 250; // pixels
var maxColumns = 4;
var verticalOffset = (i % maxColums) * rowHeight;
Then, when you can supply a y coordinate, starting at or near 0, add the vertical offset to it.

Windows Forms C# draw whole scrollable panel

I have a problem with my scrollable panel in Windows Forms C#.
My Situation
I have a Form with a tabControl.
In 1 of the tabs i have a panel that fills the whole tab and is scrollable.
In runtime i fill this panel with about 60 other panels and 6 pictures in each of the added panels. This leads to 1 big panel that i can scroll down.
My Problem
Now drawing 1 of these sub panels takes about 0.2 seconds on my bad pc which is fine and reaosnable but i have the problem that it is only drawing the things that are currently visible in the main panel.
Even if i let the main panel load for 1 minute he still only draws the first couple of sub panels and if i scroll down he has to draw the rest.
If i scroll up again everything is smoot, so it looks like it is storing everything that was drawn once.
I would like abehaviour where he basically draws the whole main panel in the beginning and afterwards the scrolling is smooth.
PS: I am not sure if "drawing" is the right word for what the form is doing.
PPS: I know that this code is not perfect, but I am not complaining about performance issues but I just want to understand how this loading works
Some code:
private void Form1_Load(object sender, EventArgs e) {
pnEverything.Controls.Clear();
int yPosition = 20;
for (int i = 1; i <= 48; i++) {
Panel panel = new Panel();
Label label = new Label();
if (i % 2 != 0) {
panel.Location = new Point(10, yPosition);
}
else {
panel.Location = new Point(this.Size.Width / 2 + 10, yPosition);
yPosition += this.Size.Width / 10;
}
panel.Size = new Size(this.Size.Width / 2 - 80, ((this.Size.Width / 2 - 80) - 90) / 6 + 40);
panel.Tag = i;
panel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
label.AutoSize = true;
label.Font = new Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
label.ForeColor = Color.Black;
label.Name = "lbLade" + i;
label.Location = new Point(panel.Width / 2 - label.Size.Width / 2, panel.Size.Height - 30);
label.Text = i.ToString();
panel.Controls.Add(label);
int xPosition = 30;
for (int j = 1; j <= 6; j++) {
MyPictureBox pb = new MyPictureBox();
pb.Location = new Point(xPosition, 10);
pb.Margin = new Padding(2);
pb.Name = "pbLade" + i + "Nummer" + j;
pb.Size = new Size((panel.Size.Width - 90) / 6, (panel.Size.Width - 90) / 6);
pb.SizeMode = PictureBoxSizeMode.StretchImage;
pb.Cursor = System.Windows.Forms.Cursors.Hand;
pb.Position.Lade = i;
pb.Position.Nummer = j;
xPosition += pb.Size.Width + 10;
pb.ImageLocation = #"Bilder\plus.png";
pb.Click += new EventHandler(pbAddCar_Click);
panel.Controls.Add(pb);
}
pnEverything.Controls.Add(panel);
}
}
After Activate the form, Load one Panel and remaining you can load in the Background worker and Invoke.

mschart horizontal scroll causing unwanted chart area width change

When I scroll horizontally a zoomed mschart I see an ugly chart area flicker (right border), caused by unwanted change of its width (this is due to variable scaling of the chart during horizontal scrolling).
Any ideas how to improve that?
Code example:
DateTime zeroTime = new DateTime(1, 1, 1, 0, 0, 0);
int k = 0;
chart1.Series[0].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
chart1.Dock = DockStyle.Fill;
chart1.BackColor = Color.Salmon;
this.chart1.ChartAreas["ChartArea1"].CursorX.IsUserEnabled = true;
this.chart1.ChartAreas["ChartArea1"].CursorY.IsUserEnabled = true;
this.chart1.ChartAreas["ChartArea1"].CursorX.IsUserSelectionEnabled = true;
this.chart1.ChartAreas["ChartArea1"].CursorY.IsUserSelectionEnabled = true;
this.chart1.ChartAreas["ChartArea1"].AxisX.ScaleView.Zoomable = true;
this.chart1.ChartAreas["ChartArea1"].AxisY.ScaleView.Zoomable = true;
this.chart1.ChartAreas["ChartArea1"].AxisX.ScrollBar.IsPositionedInside = true;
this.chart1.ChartAreas["ChartArea1"].AxisY.ScrollBar.IsPositionedInside = true;
this.chart1.ChartAreas["ChartArea1"].AxisX.ScaleView.SmallScrollMinSizeType =DateTimeIntervalType.Seconds;
this.chart1.ChartAreas["ChartArea1"].AxisX.ScaleView.SmallScrollSizeType = DateTimeIntervalType.Seconds;
chart1.ChartAreas["ChartArea1"].CursorY.Interval = 0.1;
chart1.ChartAreas["ChartArea1"].CursorX.Interval = 5.0;
chart1.ChartAreas["ChartArea1"].AxisX.LabelStyle.Format = "HH:mm:ss";
chart1.ChartAreas["ChartArea1"].CursorX.IntervalType = System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType.Seconds;
for (int i = 0; i < 600; i++)
{if (i < 200 ){k=i/10;} else if(i<400){k=20;}else{k=(600-i)/10;}; chart1.Series[0].Points.AddXY(zeroTime.AddSeconds(i * 5), 0 - k);}
The resizing of the chart can be influenced by the InnerPlotPosition property of the chart area. The default here is InnerPlotPosition.Auto set to true and this leads to the width change.
The chart width change can be stopped by switching to manual mode:
this.chart1.ChartAreas["ChartArea1"].InnerPlotPosition.Auto = false;
// optional:
this.chart1.ChartAreas["ChartArea1"].InnerPlotPosition.X = 0; // all values are percentage
this.chart1.ChartAreas["ChartArea1"].InnerPlotPosition.Y = 0;
this.chart1.ChartAreas["ChartArea1"].InnerPlotPosition.Height = 100;
this.chart1.ChartAreas["ChartArea1"].InnerPlotPosition.Width = 100;

get actual column width

I am building a DataGridView table programmatically and placing it within a GroupBox on a form.
I insert the data, then resize the columns automatically to fit the data. I then want to resize the groupbox to the size of the DataGridView. For each column and row, I get their respective width and height, which technically should be accurate, and update the panel and overall DataGridView sizes.
The problem is that Column.Width always returns 100 pixels regardless of its actual size (see screenshot: actual column width is around 30px, not 100). If I manually enter width = 90 pixels, the resizing is quite accurate!
matrix = new DataGridView();
//modify behaviour
matrix.ColumnHeadersVisible = false;
matrix.AllowUserToResizeColumns = false;
matrix.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
matrix.RowHeadersVisible = false;
matrix.AllowUserToResizeRows = false;
//modify positioning
matrix.Location = new Point(10, 20);
//matrix.Anchor = (AnchorStyles)(AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right);
matrix.Dock = DockStyle.Fill;
//set the size of the matrix
matrix.ColumnCount = col;
matrix.RowCount = row;
//Data now inserted...
matrix.AutoResizeColumns(); //correctly resizes the columns
int height = 0;
foreach (DataGridViewRow row in matrix.Rows)
{
height += row.Height;
}
height += matrix.ColumnHeadersHeight;
int width = 0;
foreach (DataGridViewColumn col in matrix.Columns)
{
width += col.Width;
//PROBLEM: Width always = 100 pixels.
}
width += matrix.RowHeadersWidth;
//width = 90; //override width manually
matrix.Size = new Size(width + 2, height + 2);
panel.Size = new Size(matrix.Width, matrix.Height);
the panel is so large because width isn't 90px but 357 roughly, which is wrong!
EDIT: PARTIAL FIX
I have found a way to get the correct width of a cell:
DataGridView.Rows[0].Cells[0].ContentBounds.Width
//ContentBounds = a rectangle with the exact dimensions of that cell
I can now set the DataGridView the correct size, but only if it's not Docked to Fill. Setting matrix.Dock = DockStyle.Fill prevents the resizing to occur correctly.
I guess that is because you're autosizing your columns in row:
matrix.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
try to change it to:
matrix.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
I have figured out what the problem is:
DataGridView.Dock = DockStyle.Fill prevented the DataGridView.AutoResizeColumns() method from doing anything.
Rectangle rect = matrix.Rows[0].Cells[0].ContentBounds getting the dimensions of the rectangle containing the cell values didn't update after AutoResizeColumns()
I tried setting the columns manually, but as heq suggested, the AutoSizeColumnsMode must be set to None in order to be able to modify the width.
I adjusted the width of each column manually, to be 10px for each character inside the cell
Final Solution
/* Create the DataGridView */
matrix = new DataGridView();
//modify behaviour
matrix.ColumnHeadersVisible = false;
// matrix.columnhea
matrix.AllowUserToResizeColumns = false;
matrix.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None;
matrix.RowHeadersVisible = false;
matrix.AllowUserToResizeRows = false;
matrix.ReadOnly = true;
//modify positioning
matrix.Location = new Point(10, 20);
//matrix.Dock = DockStyle.Fill;
matrix.Anchor = (AnchorStyles)(AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right);
//set the size of the matrix
matrix.ColumnCount = cols;
matrix.RowCount = rows;
panel.Controls.Add(matrix);
/* Set the data contents (not shown) */
...
/* Adjust the width of columns */
int width = 0;
int height = 0;
for(int c = 0; c < cols; c++)
{
int largest = 0; //largest number of characters in cell
for(int r = 0; r < rows; r++)
{
int len = matrix.Rows[r].Cells[c].Value.ToString().Length;
if (len > largest)
{
largest = len;
}
}
matrix.Columns[c].Width = largest * 10;
width += largest * 10;
}
/* Get height of table */
foreach (DataGridViewRow row in matrix.Rows)
{
height += row.Height;
}
/* Set the DataGridView Size and Parent Panel size */
//Note: first set the parent panel width, the table is anchored to all of its edges
// as such table wont resize correctly if the panel isn't up to size first
panel.Size = new Size(width + 20, height + 30);
matrix.Size = new Size(width + 3, height + 3); //is it 1px per row and col?
/* Parent Panel settings kept to default, no modifications */

C# Chart use CustomLabels with scrollbar

I don't understand something. If i don't use the customlabels, the chart will use the default label. And then if I move the scrollbar , the chart size won't adjust. The Chart view maintain the original size.
But if I use this code to change the label at row 0. (other rows don't have this problem)
chart1.ChartAreas[0].AxisY2.CustomLabels.Add((i) ,
(i+1), (ntemp * 10).ToString(), 0, LabelMarkStyle.SideMark);
And Move the scrollbar, the chart View will be a little different for size. The chart will flicker, and I don't want it.
Thanks in advance.
Here is example
Random rand = new Random();
chart1.Series.Clear();
var series = chart1.Series.Add("My Series");
series.ChartType = SeriesChartType.RangeBar;
series.Color = Color.Black;
series.YAxisType = AxisType.Secondary;
for (int i = 10; i > 2; i--)
series.Points.AddXY(i, (rand.Next(3600, 7200)), (rand.Next(30000, 80000)));
var chartArea = chart1.ChartAreas[series.ChartArea];
chartArea.BorderDashStyle = ChartDashStyle.Solid; //最外圍的框框
chartArea.BorderWidth = 10;
chartArea.AxisY.Enabled = AxisEnabled.False;
chartArea.AxisY2.Enabled = AxisEnabled.True;
chartArea.AxisY2.LabelStyle.IntervalType = DateTimeIntervalType.Number;
chartArea.AxisY2.Interval = 3600;
chartArea.AxisY2.Minimum = 0;
chartArea.AxisY2.Maximum = 86400;
chartArea.AxisY2.ScaleView.Zoom(0, 3600 * 4);
for (int i = 0; i <= 24 * 6; i++)
{
int ntemp = i % 6;
if (ntemp != 0)
{
/*Problem Here !!*/
//chart1.ChartAreas[0].AxisY2.CustomLabels.Add((i) * 600, (i + 1) * 600, (ntemp * 10).ToString(), 0, LabelMarkStyle.Box);
}
}
chartArea.CursorY.AutoScroll = true;
chartArea.AxisY2.ScaleView.Zoomable = true;
chartArea.AxisY2.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
chartArea.AxisY2.ScrollBar.IsPositionedInside = false;
}
Well, I was intrigued about how and if this can be achieved with OxyPlot, and I think it can ...
Here's the code I've used, and here's a screenshot:
var model = new PlotModel("IntervalBarSeries") { LegendPlacement = LegendPlacement.Outside };
var temp_serie = new IntervalBarSeries
{
Title = "IntervalBarSeries 1",
FillColor = OxyColors.Black
};
var categoryAxis = new CategoryAxis
{
Position = AxisPosition.Left,
IsZoomEnabled = false, // No zoom on this axis
IsPanEnabled = false, // Right mouse move won't affect this axis
MajorGridlineStyle = LineStyle.Solid
,StartPosition = 1, EndPosition = 0 // This will reverse the order
};
var valueAxis = new LinearAxis(AxisPosition.Top)
{
MinimumPadding = 0.1, MaximumPadding = 0.1,
IsZoomEnabled = true,
MajorGridlineStyle = LineStyle.Solid,
MajorStep = 3600,
AbsoluteMinimum = 0
};
for (int i = 10; i > 2; i--)
{
temp_serie.Items.Add(new IntervalBarItem {
Start = rand.Next(3600, 7200),
End = rand.Next(30000, 80000)
});
categoryAxis.Labels.Add("Activity "+i);
}
model.Series.Add(temp_serie);
model.Axes.Add(categoryAxis);
model.Axes.Add(valueAxis);
MyPlotModel = model;
Now, I'm using MVVM and just binding to the plot model from my View with:
<oxy:Plot Model="{Binding MyPlotModel}"/>
But you can figure out how to do the same with WinForms once (if?) you decide to use OxyPlot and import it.
I'm assuming you're doing some work that is related to times, but your code obviously doesn't say so ... you could play around with the top header, and maybe set how to show the numbers (ATM, with no zoom, they overlap each other a bit. zooming with scroller solves that, but that's just because i've set the tick size to 3600 ... )

Categories