TableLayoutPanel column won't occupy 100% width - c#

I have an issue with TableLayoutPanel. Despite using a single column with 100% width, the control I add keeps the size assigned to it in layout creator.
TableLayoutPanel panel = someTableLayoutPanel;
panel.RowStyles.Clear();
panel.ColumnStyles.Clear();
panel.Controls.Clear();
ColumnStyle columnStyle = new ColumnStyle(SizeType.Percent, 100F);
panel.ColumnStyles.Add(columnStyle);
int count = capabilityListManager.DeviceCount();
panel.RowCount = deviceCount;
panel.ColumnCount = 1;
for (int index = 0; index < count; index++)
{
RowStyle rowStyle = new RowStyle(SizeType.Absolute, 120F);
panel.RowStyles.Add(rowStyle);
SomeCustomControl cell = new SomeCustomControl();
panel.Controls.Add(cell, 0, index);
}
I assumed that would be enough for the control to always be resized to have fixed 120 pixels height and take 100% of width provided by TableLayoutPanel, however the result looks like this:
My custom control is the one with the border, and the TableLayoutPanel has blueish hue - clearly there's space left for the control to occupy.
SomeCustomControl has its AutoSize property set to false. What am I missing?

Related

Matrix of photos

So I have a code that injects an image into my project through resources Image foodWorld = Resources.orange and I want to make a matrix out of this photo, so it can look like this:
I have this code but I don't know how to draw the matrix. Also, I don't know if this is the right way to draw it or not:
this.Width = 400;
this.Height = 300;
Bitmap b = new Bitmap(this.Width, this.Height);
for(int i = 0; i < this.Height; i++)
{
for(int j = 0; j < this.Width; j ++)
{
//fill the matrix
}
}
I am not too familiar with WinForms, but in WPF, I'd do it this way:
var columns = 15;
var rows = 10;
var imageWidth = 32;
var imageHeight = 32;
var grid = new Grid();
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < columns; j++)
{
//Get the image in your project; I'm not sure how this is done in WinForms
var b = new Bitmap(imageWidth, imageHeight);
//Display it
var pictureBox = new PictureBox();
pictureBox.Image = b;
//Set the position
Grid.SetColumn(j, pictureBox);
Grid.SetRow(i, pictureBox);
//Insert into the "matrix"
grid.Children.Add(pictureBox);
}
}
For moving Pacman, repeat the above, but for only one image. Store a reference to the current position and when certain keys are pressed,
Animate it's margin until it appears to be in an adjacent cell (for instance, if each cell is 16 pixels wide and pacman should be in the center of any given cell, animate the right margin by 16 pixels to get it into the cell on the right and so forth).
Once it has moved to another cell, set the new row and column based on the direction in which it last moved.
If there is a fruit at the new position, get the fruit at that position and remove it from the Grid. You can get it by using myGrid.Children[currentRow * totalColumns + currentColumn] assuming currentRow and currentColumn are both zero-based.
Repeat for each cell it must move to.
This does mean the matrix will have a fixed size, but in WPF, there is a Viewbox, which is convenient for these types of scenarios. Also, set the z-index of pacman to be greater than the fruits so it's always on top.

Tiling Windows Offset using Infragistics Dialog Window

I am using the Infragistics XamDialogWindow to approximate an MDI window in WPF.
Basically I have a user control that I assign to the content of the window and then add the XamDialogWindow as a child of a Grid.
I then have two buttons which display the windows as either "Tile" or "Cascade".
The cascade works really well but I am having problems with the Tile method.
I get the available width and height using the "ActualWidth" and "ActualHeight" properties of the container.
I then calculate the number of rows and columns needed.
Next I iterate through the columns and I calculate how many rows there are in a column, (this means that if there are an odd number of windows such as 5 I will have two rows in column 0 and 3 in column 1).
The width of each window is worked out as "AvailableWidth / numColumns"
The height of each window is worked out as "AvailableHeight / WindowsForThisColumn"
I then iterate through the rows and calculate the top left coordinate of the window we are positioning as follows:
left = columnIndex * width
top = rowIndex * height
The Left, Top, Width and Height properties of the window are then set.
PROBLEM
The Left, Top, Width and Height properties seem to be calculated correctly so for example
AvailableWidth = 1000;
AvailableHeight = 1000;
Window1 = 0, 0, 500, 500
Window2 = 0, 500, 500, 500
Window3 = 500, 0,500, 500
Window4 = 500, 500, 500, 500
This should give a nice grid of 4 windows with no margins filling all available space, however this is not the result.
The windows seem to be offset when displayed (see image below).
If I look at the Left, Top, Width and Height properties they all appear to be correct.
The weird thing is, if I click "Tile" a second time they will all appear correctly.
If I move a window after clicking tile the second time, they will appear correctly.
I have tried using a Canvas and setting the Canvas.Left and Top properties, A grid, stackpanel and wrappanel with the same results every time.
Can anyone help me? I think it may be a problem with how WPF measures sizes and locations at Render Time
var childrenToArrange = Children.Where(a => a.WindowState != Infragistics.Controls.Interactions.WindowState.Minimized).ToList();
var availableWidth = Panel.ActualWidth;
var availableHeight = Panel.ActualHeight;
if (Layout == MDILayout.TILE)
{
//get the number of rows and columns
int rows = (int)Math.Sqrt(childrenToArrange.Count);
int columns = childrenToArrange.Count / rows;
var index = 0;
var width = availableWidth / columns;
//iterate through the columns
for (int x = 0; x < columns; x++)
{
//get the number of windows for this column
var windowsForThisColumn = rows;
if (childrenToArrange.Count % columns > (columns - x - 1))
windowsForThisColumn++;
var height = availableHeight / windowsForThisColumn;
//iterate through the rows
for (int y = 0; y < windowsForThisColumn; y++)
{
//get the X and Y coordinates
var left = x * width;
var top = y * height;
//get the window
var mdiChild = childrenToArrange[index] as XamDialogWindow;
mdiChild.Margin = new Thickness(0);
mdiChild.Left = left;
mdiChild.Top = top;
mdiChild.Width = width;
mdiChild.Height = height;
index++;
}
}
}
I've reproduced the issue with your snippet. Please click the following hyperlink to the following forum post at Infragistics for further instructions.
Michael DiFilippo

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 */

easier way to create grid than creating 900 controls c#.net

I'm trying to write a program that creates a 30x30 grid of 20px x 20px boxes. When you click on a box in the grid it changes the color of the box and stores the RGB value of it and the x,y coordinates (hidden in the box). So basically I'm making a simple paint program. The purpose of it is to assist me in programming LED RGB animations for a 30x30 pixel grid. So once I draw something I export the X,Y,R,G,B of each pixel in the image.
So my question is, is there an easy way to do this. With out creating 900 buttons and putting them together? I've got something sorta working :
Panel BU = new Panel();
BU.AutoSize = false;
BU.Location = new System.Drawing.Point(xpos, ypos);
BU.BackColor = System.Drawing.Color.Transparent;
BU.Font = new System.Drawing.Font("Microsoft Sans Serif", 5, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
BU.Name = row_num + " x " + col_num;
BU.Size = new System.Drawing.Size(20, 20);
BU.MouseDown +=new MouseEventHandler(BU_MouseDown);
BU.MouseEnter +=new EventHandler(BU_MouseEnter);
this.Controls.Add(BU);
xpos = xpos + 20;
px_num++;
col_num++;
if (col_num == 30)
{
col_num = 0;
ypos = ypos + 20;
row_num++;
xpos = 0;
};
But it takes WAAAAAY to long to load.
Yes.
Create one panel, and handle the grid painting and mouse events inside that.
Super simple example, optimized for nothing and flicker happy:
private Color[,] _Colors = new Color[30, 30];
private void panel1_Paint(object sender, PaintEventArgs e) {
int left = 0;
int top = 0;
for (int y = 0; y < 30; y++) {
left = 0;
for (int x = 0; x < 30; x++) {
Rectangle r = new Rectangle(left, top, 20, 20);
using (SolidBrush sb = new SolidBrush(_Colors[x, y]))
e.Graphics.FillRectangle(sb, r);
ControlPaint.DrawBorder3D(e.Graphics, r, Border3DStyle.Raised, Border3DSide.Left | Border3DSide.Top | Border3DSide.Right | Border3DSide.Bottom);
left += 20;
}
top += 20;
}
}
private void panel1_MouseDown(object sender, MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
int left = 0;
int top = 0;
for (int y = 0; y < 30; y++) {
left = 0;
for (int x = 0; x < 30; x++) {
Rectangle r = new Rectangle(left, top, 20, 20);
if (r.Contains(e.Location)) {
_Colors[x, y] = Color.Red;
panel1.Invalidate();
}
left += 20;
}
top += 20;
}
}
}
A custom user control that renders 20x20 boxes would do the trick. To get the location of a mouse click, take the x and y values, divide by 20.
Something very similar here: https://github.com/i-e-b/DBSS/blob/master/DBSS/BigGrid/SheetView.cs
Well, instead of creating 900 controls, no difference if it's made in WindowsForms, or WPF with HD accelaration, it will be slow.
What can do, if you really need always have all controls visible on the screen, so potentially at some points also have 900 contemporary, is just draw rectangles. You can do an
emulation of the button.
Draw Rectangle, where Left and Top lines are darker then Right and Bottom, will give 3D filling to user, make it inverse colors of lines, and it will give to user a filling of pushed button. For sure you need to handle all mouse interactions, like MouseMove, MouseDown, MouseUp on your panel (cause the panel will be the only control present at this point), and figure out on what rectangle event happens and draw that rectangle accordingly.
I typically use a Bitmap object for this and use one of the overrides in Graphics.DrawImage to draw it zoomed in. Then I draw grid lines over top of the zoomed image.
I'm digging up some source code for you now.
Edit
Sorry, no source code handy at the moment. But basically what you want to do is create a new system.drawing.bitmap that is 30x30 pixels. In the paint even of teh control you want to draw it in, use the passed in Graphics Object (usually e.graphics) and call .DrawImage. Use one of the overloads that allows you to specify the output size so that you can scale it out by whatever zoom factor you want. You will also need to set the .PixelOffsetMode on the graphics object to .none, or else things will be offset by 1/2 your zoom. Set .InterpolationMode to .NearestNeighbor so that the output isn't blurry. This should give you a perfectly aligned "pixelated" zoomed image. Then just loop and draw horizontal and vertical grid lines.
To handle the mouse clicks just add a handler to the controls mouse down event, and divide the input position by your zoom factor to get the real x and y coords. then update that pixel of the source image and call .invalidate on the control you are drawing to. That will cause it to repaint with the updated image.

How can I create Rectangles in WPF dynamically?

I am trying to create rectangles and the number of rectangles is depend on data passed from database.
For example, if number = 5, the program will generate 5 rectangles. Also, these rectangles must be able to follow my rectangle property settings, like height, width,color...put them in one line at the end.
Is there a way to do that?
I am using WPF and C#.
Thank you.
To create the rectangle in code dynamically:
int number = 5;
int width = 10;
int height = 10;
int top = 20;
int left = 20;
for (int i = 0; i < number; i++)
{
// Create the rectangle
Rectangle rec = new Rectangle()
{
Width = width,
Height = height,
Fill = Brushes.Green,
Stroke = Brushes.Red,
StrokeThickness = 2,
};
// Add to a canvas for example
canvas.Children.Add(rec);
Canvas.SetTop(rec, top);
Canvas.SetLeft(rec, left);
}

Categories