I have a dynamically created N panels, the distance between them in height 15px
panel.Location = new Point (x, y);
y = panel.Bottom + 15;
I can make the width of the smaller, and so I need distance in height between the panels was always 15px
I have a method with different checks for resize, and I try changes distance, but it always works differently...
public void checkResize(string msg_out, object panel_sender, object text_msg_sender, int panHei, int numbs)
{
Panel pan_item = (Panel)panel_sender;
Label lab_item = (Label)text_msg_sender;
char[] msg_arr = msg_out.ToCharArray();
int panWidRaz = 308 - pan_item.Width;
int panWidw = pan_item.Width;
if (int.Parse(pan_item.Name) != numbs - 1)
{
if (panWidw < buff)
{
/* if (panWidRaz % 15 == 0)
{
for (int i = int.Parse(pan_item.Name); i >= 0; i--)
{
panel1.Controls[i.ToString()].Location = new Point(panel1.Controls[i.ToString()].Location.X, panel1.Controls[i.ToString()].Location.Y + 1);
}
}*/
//width control becomes smaller panels are becoming more in height, it is necessary that the distance between the panels remained 15px
}
if (panWidw > buff)
{
/*if (panWidRaz % 15 == 0)
{
for (int i = int.Parse(pan_item.Name); i >= 0; i--)
{
panel1.Controls[i.ToString()].Location = new Point(panel1.Controls[i.ToString()].Location.X, panel1.Controls[i.ToString()].Location.Y - 1);
}
}*/
//width control becomes bigger panels are becoming less in height, it is necessary that the distance between the panels remained 15px
}
buffCountPan++;
if (buffCountPan == panel1.Controls.Count - 1)
{
buff = panWidw;
buffCountPan = 0;
}
if (msg_arr.Length > 26)
{
int panWid = (308 - pan_item.Width) / 5;
int panWidLab = 308 - pan_item.Width;
pan_item.Height = panHei + panWid;
lab_item.MaximumSize = new System.Drawing.Size(300 - panWidLab, 100);
lab_item.MinimumSize = new System.Drawing.Size(300 - panWidLab, 14);
}
}
}
I can't post image here... reputation... i make scrin of work my panel
http://pixs.ru/showimage/Bezimeni1p_9639414_8969341.png
If you want to make it simple, when you add those panel which i guess is done dynamically. You can set the panel.tag = "[Order]". so an order number assign so you know which one is first on top and the second and go on...
Const int MinimumStartLocation = 0;
Const int DistanceBetweenPanel = 15;
private void setPanelLocation(Panel pnl)
{
// retreive the order of this panel
int iPanelOrder = Convert.ToInt32(pnl.Tag);
// the first panel must show on top and have specific location
if(iPanelOrder == 0)
{
pnl.Top = MinimumStartLocation;
}
else
{
// set the top of the panel to the bottom value of the panel just before him in the order plus the constant
pnl.Top = this.Controls.OfType<Panel>().ToList().Find(pan => Convert.ToInt32(pan.Tag) == iPanelOrder -1).Bottom + DistanceBetweenPanel;
}
}
now use this LINQ command it will call the method above for each panel in the proper order.
this.Controls.OfType<Panel>().OrderBy(x => Convert.ToInt32(x.Tag)).ToList().ForEach(pan => setPanelLocation(pan));
here a sample project quickly done download here
Edit: updated link
It might be worth taking a look at the Flow Layout Panel. When you add a control to a flow layout panel it will automatically be positioned either to the left, right, top or bottom of any existing controls within that flow layout panel (depending on the layout direction property that you specify).
Similarly, if you remove or resize a control contained within a flow layout panel the positions of the controls will be automatically updated for you.
In your case it should be as simple as adding a new flow layout panel to your form then, for each panel that you add to the flow layout panel, setting the top margin to 15px and the bottom margin to 0px; the flow layout panel will take care of the rest. When panels are added, removed or resized the flow layout panel will ensure that they are still displayed correctly with a margin of 15px between them.
Related
I'm using simple code to generate a grid (gridSize * gridSize fields with lines dividing them in column and row, basically a TicTacToe grid).
As I'm creating the panels dynamically during Form_Load, I need to also adjust the size of the form. However, setting it to gridSize * tileSize, gridSize * tileSize is not big enough - I found by experimentation that I need to add ~15 to width and ~40 to height for gridSize = 3 and tileSize = 120. Why is this?
Code below:
private void Form1_Load(object sender, EventArgs e)
{
const int tileSize = 120;
const int gridSize = 3;
/* Here: When setting size, I need to add 15 and 40? */
this.Size = new System.Drawing.Size(tileSize * gridSize + 15, tileSize * gridSize + 40);
// initialize the "board"
tictactoeFields = new Panel[gridSize, gridSize]; // column, row
// double for loop to handle all rows and columns
for (var n = 0; n < gridSize; n++)
{
for (var m = 0; m < gridSize; m++)
{
// create new Panel control which will be one
// tic tac toe field
var newPanel = new Panel
{
Size = new Size(tileSize, tileSize),
Location = new Point(tileSize * n, tileSize * m)
};
// add to our 2d array of panels for future use
tictactoeFields[n, m] = newPanel;
newPanel.BackColor = Color.White;
if(n != 0)
{
// Draw a line in front (to the left) of this panel
Panel leftSeparator = new Panel
{
Size = new Size(1, tileSize),
Location = newPanel.Location,
BackColor = Color.Black
};
Controls.Add(leftSeparator);
}
if(m != 0)
{
// Draw a line on top (above) this panel
Panel topSeparator = new Panel
{
Size = new Size(tileSize, 1),
Location = newPanel.Location,
BackColor = Color.Black
};
Controls.Add(topSeparator);
}
}
}
foreach(Panel pan in tictactoeFields)
{
// add to Form's Controls so that they show up
Controls.Add(pan);
}
}
The Size property is just a shorthand for setting the size of the Bounds property which includes nonclient elements such as scroll bars, borders, title bars, and menus.
What you should do is to set the size of the ClientRectangle property, or use the ClientSize shorthand.
There's also a DisplayRectangle property which includes padding, but in this case use the ClientRectangle property.
this.ClientSize = new Size((tileSize * gridSize), (tileSize * gridSize));
I'm trying to set the Height of the form while I'm resizing it, if a condition is met. I have it set to only allow the width of the form to be altered manually using the code provided by in this answer.
I have a FlowLayoutPanel displaying a collection of PictureBox controls, each with a fixed Height of 50 pixels. Initially, the form's Height is 38 (Size.Height - ClientSize.Height) + 50 + 6 (Margin.Top + Margin.Bottom of an image) = 94.
If the controls overflow, by default the FlowLayoutPanel pushes them down onto a new line. What I want to do is resize the form when this happens, or when the form width is manually changed which might cause the controls to jump to the next line.
The following code works, and is called whenever a new control is added to the FlowLayoutPanel (itemPanel):
private void ResizeForm()
{
if (itemPanel.Controls.Count < 1) return;
var lastElement = itemPanel.Controls[itemPanel.Controls.Count - 1];
// The Form is the correct size, no need to resize it:
if (lastElement.Bottom + lastElement.Margin.Bottom == itemPanel.Height) return;
Height = 38 + lastElement.Bottom + lastElement.Margin.Bottom;
}
However, when called within my SizeChange event, this method causes the Form to "flash" between the initial Height and the new Height:
private void MainForm_SizeChanged(object sender, EventArgs e)
{
ResizeForm();
}
I'm guessing the reason is because setting Height will fire the SizeChange event again, but I don't know how to resolve this issue. When I print out the values of lastElement.Bottom + lastElement.Margin.Bottom and itemPanel.Height after setting the Height, they are identical, but the code is still somehow reaching that point.
In a nutshell, I want only the form Width to be manually altered, but the Height of the form to change when items are added or the Width is changed, so that all controls inside the FlowLayoutPanel can be viewed.
However, when called within my SizeChange event, this method causes
the Form to "flash" between the initial Height and the new Height
Basically any of the stock "resize" events for your Form will occur too late for you to change the size without it being noticeable.
You'll want to trap the WM_SIZING message:
Sent to a window that the user is resizing. By processing this
message, an application can monitor the size and position of the drag
rectangle and, if needed, change its size or position.
This will allow you to change the size of the Form before it has actually been updated on the screen.
It would look something like this:
public partial class Form1 : Form
{
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
enum HitTest
{
Caption = 2,
Transparent = -1,
Nowhere = 0,
Client = 1,
Left = 10,
Right = 11,
Top = 12,
TopLeft = 13,
TopRight = 14,
Bottom = 15,
BottomLeft = 16,
BottomRight = 17,
Border = 18
}
private const int WM_SIZING = 0x214;
private const int WM_NCHITTEST = 0x84;
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WM_NCHITTEST:
var result = (HitTest)m.Result.ToInt32();
if (result == HitTest.Top || result == HitTest.Bottom)
m.Result = new IntPtr((int)HitTest.Caption);
if (result == HitTest.TopLeft || result == HitTest.BottomLeft)
m.Result = new IntPtr((int)HitTest.Left);
if (result == HitTest.TopRight || result == HitTest.BottomRight)
m.Result = new IntPtr((int)HitTest.Right);
break;
case WM_SIZING:
// Retrieve the "proposed" size of the Form in "rc":
RECT rc = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
// ... do something with "rc" ...
// this is your code (slightly modified):
if (itemPanel.Controls.Count > 0)
{
var lastElement = itemPanel.Controls[itemPanel.Controls.Count - 1];
if (lastElement.Bottom + lastElement.Margin.Bottom != itemPanel.Height)
{
int Height = 38 + lastElement.Bottom + lastElement.Margin.Bottom;
rc.Bottom = rc.Top + Height; // <--- use "Height" to update the "rc" struct
}
}
// Put the updated "rc" back into message structure:
Marshal.StructureToPtr(rc, m.LParam, true);
break;
}
}
}
Give this a try:
private void ResizeForm()
{
this.SuspendLayout(); // Suspends the layout logic until ResumeLayout() is called (below)
if (itemPanel.Controls.Count < 1) return;
var lastElement = itemPanel.Controls[itemPanel.Controls.Count - 1];
// The Form is the correct size, no need to resize it:
if (lastElement.Bottom + lastElement.Margin.Bottom == itemPanel.Height) return;
Height = 38 + lastElement.Bottom + lastElement.Margin.Bottom;
this.ResumeLayout(); // ADD THIS AS WELL
}
Im having a problem with setting custom border of Control. This border can be done with DrawRectangle, DrawBorder or anything else as long as I get this behaviour
Obviously, the darkest border is where previouse border was. Other borders are around it trying to mimic fade out(or whatever). Now, the most challenging thing is I cannot override OnPaint or extend any other Control. This HAS to work on all Controls!
This is part of my extender provider whith which I set these borders when control has focus(like Google Chrome).
So far I have come up with this...
When adding controls in extender provider dictionary I hook up on Enter and Leave events of control. In there I get the parent of the control that is firing the event and on that form I draw these 3 rectangles. That way I sorted painting on non client area. The thing that remains is painting the actual border of control. I have tried and tried but to no avail.
I also hooked up on paint event of that control but ControlPaint.DrawBorder() is not working.
Okay, so this is method that is getting called on Enter and leave.
private void BojajGlow(Graphics gfx, Graphics gfxCtrl, Control parent, Control kontrola, bool novi)
{
Rectangle[] rect = new Rectangle[3];
for (int i = 0; i < 3; i++)
{
int x = kontrola.Location.X - (i + 1);
int y = kontrola.Location.Y - (i + 1);
int w = kontrola.Size.Width + 2 * (i + 1) - 1;
int h = kontrola.Size.Height + 2 * (i + 1) - 1;
rect[i] = new Rectangle(x, y, w, h);
}
if (novi)
{
Color boja = DohvatiOpcije(kontrola).Boja;
for (int i = 0; i < 3; i++)
{
if (i > 0)
boja = Posvjetli(95, ControlPaint.Light(boja));
Pen olovka = new Pen(boja);
olovka.EndCap = olovka.StartCap = LineCap.Round;
olovka.Width = 1;
GraphicsPath gfxPath = new GraphicsPath();
gfxPath.AddRectangle(rect[i]);
gfx.DrawPath(olovka, gfxPath);
}
}
else
{
for (int i = 0; i < 3; i++)
{
Pen olovka = new Pen(parent.BackColor);
olovka.EndCap = olovka.StartCap = LineCap.Round;
olovka.Width = 1;
GraphicsPath gfxPath = new GraphicsPath();
gfxPath.AddRectangle(rect[i]);
gfx.DrawPath(olovka, gfxPath);
}
}
}
From Enter event it is going to be called like this
if (((Control)sender).Parent != null)
BojajGlow(Graphics.FromHwnd(((Control)sender).Parent.Handle), Graphics.FromHwnd(((Control)sender).Handle), ((Control)sender).Parent, (Control)sender, true);
Does anyone have any valuable input on this?
In winforms you are probably going to need to create your own custom control inheriting from the TextBox control. In your control you can implement the OnPaint based on the controls state such as whether or not it has focus.
As for drawing outside of the control, dont. It will only frustrate you. Instead draw three boarders using the forms background color WITHIN your control and change them to the hightlighted color when you need them to glow.
Hope this helps.
So anyone out there knows of sample code or control that perfectly emulates the Windows 8 Start Menu Tile Layout Engine?
It should support mixed Square and Rectangle Tiles, and repacks the square tiles over or under rectangle tiles properly.
Note: WrapPanel works if ALL TILES are Square. But once you mix in tiles that span 2-Squares worth of space, the layout breaks, and is inconsistent with the Windows 8 Start Menu
I am expecting code that extends the WPF Panel.
Disclaimer: Yes I have searched the Internet, the closest thing I've found is the CodeProject example, but that only works if all tiles are same-sized squares.
I've looked around myself and couldn't find anything to do what I/we want. I knew that to get this behavior we'd need some sort of custom panel object, so I set about creating one...
What it boils down to, is the tiles need to be arranged vertically, with double-width tiles taking up a whole row in that column, and normal width tiles to pair up. When it reaches the bottom of the container, it needs to create a new column and follow the same pattern.
Here's my implementation:
public class MetroTilePanel : Panel
{
protected override Size ArrangeOverride(System.Windows.Size finalSize)
{
double x = 0, y = 0, colWidth = 0, rowHeight = 0;
int col = 0;
colWidth = Children.Cast<UIElement>().Select(c => c.DesiredSize.Width).Max();
foreach (UIElement child in Children)
{
rowHeight = Math.Max(rowHeight, child.DesiredSize.Height);
if (x + child.DesiredSize.Width > (colWidth * (col + 1)))
{
// New row
y += rowHeight;
x = (colWidth * (col));
rowHeight = child.DesiredSize.Height;
}
if (y + rowHeight > finalSize.Height)
{
// New column
col++;
x = (colWidth * (col));
y = 0;
}
child.Arrange(new Rect(x, y, child.DesiredSize.Width, child.DesiredSize.Height));
x += child.DesiredSize.Width;
}
return finalSize;
}
protected override Size MeasureOverride(Size availableSize)
{
double x = 0, y = 0, colWidth = 0;
foreach (UIElement child in Children)
{
child.Measure(availableSize);
if (x + child.DesiredSize.Height > availableSize.Height)
{
x += colWidth;
y = 0;
colWidth = 0;
}
y += child.DesiredSize.Height;
if (child.DesiredSize.Width > colWidth)
{
colWidth = child.DesiredSize.Width;
}
}
x += colWidth;
var resultSize = new Size();
resultSize.Width = double.IsPositiveInfinity(availableSize.Width) ? x : availableSize.Width;
resultSize.Height = double.IsPositiveInfinity(availableSize.Height) ? y : availableSize.Height;
return resultSize;
}
}
Screenshot of the control in action:
Disclaimers:
The MeasureOverride only works by chance, and isn't setup correctly.
If you want the nice MetroTile layout, then stick to uniform sizes i.e 100x100 and 200x100
I haven't fully tested it, but I will be implementing it into my fake-Metro app, so if you want to see any future changes, just holler.
If you want the proper GridView tiling behavior, then we'd have to create a brand new control (to support dragging items around etc).
I hope this helps.
These are the two different libraries I evaluated for my project to create a Windows 8 like startpage in WPF:
Telerik RadTileList (paid)
mahapps.metro (open source)
I'm attempting to create an application that looks much like the Windows 8 Metro UI with the tiles.. right now if you click on a tile I have an animation that increases the width and reveals more info on that tile.
I have the tiles laid out in a WrapPanel which is nice because if I resize the tile the other tiles next to it move and keep it's margin perfectly. However I've been researching this for awhile, I would like to if possible limit the number of items in the wrap panel to two wide (Right now it's three wide) as if you select one of the tiles it resizes itself and pushes a tile next to it (or if it's the end tile it will push itself) to the next row, while my animations are smooth it doesn't look the best presentation-wise..
Could someone point me towards how I might specify my wrappanel to only have a width of two items across?
Any help is very much appreciated.
Try making your own wrap panel deriving from standard wrap panel as described in this post in detail. Post addresses the same issue which you are trying to solve.
public class MyWrapPanel : WrapPanel
{
public int MaxRows
{
get { return (int)GetValue(MaxRowsProperty); }
set { SetValue(MaxRowsProperty, value); }
}
public static readonly DependencyProperty MaxRowsProperty =
DependencyProperty.Register("MaxRows", typeof(int), typeof(MyWrapPanel), new UIPropertyMetadata(4));
protected override Size ArrangeOverride(Size finalSize)
{
Point currentPosition = new Point();
double ItemMaxHeight = 0.0;
int RowIndex = 0;
foreach (UIElement child in Children)
{
ItemMaxHeight = ItemMaxHeight > child.DesiredSize.Height ? ItemMaxHeight : child.DesiredSize.Height;
if (currentPosition.X + child.DesiredSize.Width > this.DesiredSize.Width)
{
currentPosition = new Point(0, currentPosition.Y + ItemMaxHeight);
ItemMaxHeight = 0.0;
RowIndex++;
}
if (RowIndex < MaxRows)
{
child.Visibility = System.Windows.Visibility.Visible;
Rect childRect = new Rect(currentPosition, child.DesiredSize);
child.Arrange(childRect);
}
else
{
Rect childRect = new Rect(currentPosition, new Size(0,0));
child.Arrange(childRect);
}
currentPosition.Offset(child.DesiredSize.Width, 0);
}
return finalSize;
}
protected override Size MeasureOverride(Size availableSize)
{
return base.MeasureOverride(availableSize);
}
}