Dynamically sized children of WrapPanel - c#

I have a list of texts placed in WrapPanel, representing nested text blocks... The texts are clickable so I wanted to emphasize it, so when user enters mouse over them, I make them bigger (basically I'm just increasing the font by one). The problem is that this changes size of the framework element which affects layout in WrapPanel, meaning the panel can move last item into next row. Normally there is enough space, or even if not, it's not a problem when hovering over some text in the middle. But if user want's to click last element when it's near the edge of the panel, he basically can't. Because as soon as the element grows, it moves to next row and the cursor is not longer over it, so it shrinks again and panel moves it back to first row, where it is under cursor again, and so on...
Here you can see this scenario:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
var wp = new WrapPanel() {
Background = Brushes.Blue,
Orientation = Orientation.Horizontal,
VerticalAlignment = VerticalAlignment.Top,
Children = {
createChild(),
createChild(),
createChild(),
createChild(),
}
};
Content = new Grid() {
Background = Brushes.Gray,
Children = { wp }
};
}
private UIElement createChild() {
var g = new Grid() {
Width = 100,
Height = 20,
Margin = new Thickness(5),
Background = Brushes.Red,
};
g.MouseEnter += (s, e) => { g.Width *= 2; g.Background = Brushes.White; };
g.MouseLeave += (s, e) => { g.Width *= .5; g.Background = Brushes.Red; };
return g;
}
}
How can I avoid it?
I was thinking of some "margin" property I could specify in WrapPanel so it will move the child to next row earlier when it's close to the edge whiting that margin (even if it could actually fit), but I didn't found such thing.

Related

How do I make a border around the windows form

How do I create a border made of picture boxes, I'm trying to make a snake game and I want to first make a border for the playing area, using picture boxes, so far I have used a while loop to make the top row but I feel there would be a better way to make the whole thing, I heard a helper function may help but I'm not sure how to go about that either
int i = 1;
public Snake()
{
InitializeComponent();
}
private void Snake_Load(object sender, EventArgs e)
{
while (i < 404)
{
var picture = new PictureBox
{
Name = "pictureBox",
Size = new Size(20, 20),
Location = new Point(i, 0),
BackColor = Color.FromArgb(0,0,0),
};
this.Controls.Add(picture);
i += 20;
}
}

Center a Panel within a Panel, which is in a TableLayoutPanel, in C#

So basically I've got a game board going, represented by a TableLayoutPanel. Each cell of the TableLayoutPanel represents a space on the board and contains a Panel. Each Panel has a Label in it to display what is currently in that space (e.g. a character or building) and its BackColor represents what kind of terrain that space is (e.g. Land, Water, etc.). When the player attempts to move a character, each possible space that that character can move will become "highlighted." The player will then double click one of the highlighted spaces to move the character there.
In order to highlight a space, I add a Panel to the existing Panel (the one that already has a Label in it).
So to recap, TableLayoutPanel --> Panel --> Label, Panel.
The main problem I'm having is that I can't get the "highlight-Panel" to center within its parent Panel; it's always Top-Left Centered. I want to be able to see part of the BackColor of the parent Panel in order to make it easier for the player to decide where to go. Thus, setting Dock to Fill is not an option. The highlight-Panel is slightly smaller than the parent Panel, thereby allowing for the edges of the parent Panel's BackColor to be visible. Yes, Anchor is set to None.
The secondary problem is that I cannot get any of the Labels' text colors to change. I've tried changing it at initialization or more directly with a specific location using "tableLayoutPanel.GetControlFromPosition(16, 4).Controls[0].ForeColor = Color.White;" but nothing seems to work.
To be honest, I'm not interested in super complicated hack fixes. But if there's something simple or something I missed, I'd really appreciate some input. Thanks.
This is the code that creates the TableLayoutPanel, the Panels within that, and the Labels within each Panel:
// Create TableLayoutPanel to hold Panels
tableLayoutPanel = new TableLayoutPanel()
{
RowCount = 1,
ColumnCount = 1,
AutoSize = true,
AutoSizeMode = AutoSizeMode.GrowAndShrink,
Location = new Point(12, 12),
Dock = DockStyle.Fill,
AutoScroll = true,
};
// Add tableLayoutPanel to the form
this.Controls.Add(tableLayoutPanel);
// Reset and add rows/columns + styles
tableLayoutPanel.RowStyles.Clear();
tableLayoutPanel.ColumnStyles.Clear();
for (int i = 0; i < rows; i++)
{
tableLayoutPanel.RowCount++;
tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 50F));
}
tableLayoutPanel.RowCount--;
for (int j = 0; j < cols; j++)
{
tableLayoutPanel.ColumnCount++;
tableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50F));
}
tableLayoutPanel.ColumnCount--;
// Add Panels to TableLayoutPanel
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
// Create new Panel
Panel space = new Panel()
{
BackColor = SystemColors.ActiveCaption,
BorderStyle = BorderStyle.FixedSingle,
Margin = new Padding(0),
Size = new Size(45, 45)
};
space.MouseClick += new MouseEventHandler(clickOnSpace);
// Create new Label
Label info = new Label()
{
Size = new Size(93, 93),
Text = "Info",
Dock = DockStyle.Fill,
TextAlign = ContentAlignment.MiddleCenter,
Enabled = false,
Font = new Font("Microsoft Sans Serif", 6),
ForeColor = Color.White
};
// Add Label to Panel
space.Controls.Add(info);
// Add Panel to TableLayoutPanel
tableLayoutPanel.Controls.Add(space, j, i);
}
}
And the code that creates the highlight-Panel:
// Highlight potential positions using possibleMoves
private void Highlight(List<Tuple<int, int>> possibleMoves, int remaining)
{
int r = 0;
int c = 0;
foreach (Tuple<int, int> pair in possibleMoves)
{
r = pair.Item1;
c = pair.Item2;
// If highlight-Panel doesn't already exist
if (tableLayoutPanel.GetControlFromPosition(c, r).Controls.Count == 1)
{
// Create a Panel to highlight the space
Panel highlight = new Panel()
{
Name = "highlight",
Size = new Size(30, 30),
BackColor = Color.Yellow,
Anchor = AnchorStyles.None
};
highlight.MouseDoubleClick += new MouseEventHandler(doubleClickOnSpace);
// Add highlight Panel to space Panel
tableLayoutPanel.GetControlFromPosition(c, r).Controls.Add(highlight);
// Bring highlight Panel to front
tableLayoutPanel.GetControlFromPosition(c, r).Controls[1].BringToFront();
}
}
}
... Thus, setting Dock to Fill is not an option.
In fact I believe setting Dock to Fill is really a good option!
Take a look at this image. It's a TableLayoutPanel having 2 columns and 2 rows. And each cell contains a Pannel which contains a Panel which contains a Label. Despite what you expect Dock property of all controls has been set to Fill!
I tell you settings for the first cell which you can see Red, Black and White colors in it.
The cell contains:
Red area, a Panel having:
○ BackColor property set to Red
○ Margin property set to all 0
○ Padding property set to all 5
○ Dock property set to Fill
Black area, a Panel having:
○ BackColor property set to Black
○ Margin property set to all 0
○ Padding property set to all 5
○ Dock property set to Fill
White label, a Label having:
○ BackColor property set to White
○ Margin property set to all 0
○ Padding property set to all 0
○ Dock property set to Fill
The structure of other cells are exactly the same, but the BackColor of Label and its parent (like the black one), to Transparent, so you see the BackColor of the most inner Panel (like the red one).
Note
The answer is completely based on setting Dock property to Fill and setting suitable Padding value for docked controls. But there is another elegant solution for keeping a control at center of a container automatically which is based on using a TableLayoutPanel having 1 row and 1 column instead of panel, and then setting Anchor property of child to None. This way the child will be centered horizontally and vertically in the TableLayoutPanel. You will find this post about it useful:
How to center a Label inside a Panel without setting Dock to Fill
So I actually reached a less complicated method. Instead of having a Panel within a Panel, I opted for keeping my original Panel, and creating a custom Label, which has an outside border whose thickness and color can be changed. This way I don't have to keep track of too many controls, and the CustomLabel displays everything needed.
Code for CustomLabel:
public class CustomLabel : Label
{
// Constructors
// Default Constructor
public CustomLabel() : base() { }
public CustomLabel(bool drawBorder, int borderThickness, Color borderColor, Color textColor) : base()
{
if (drawBorder)
{
BorderThickness = borderThickness;
BorderColor = borderColor;
}
Size = new Size(36, 36);
Text = "Info";
Anchor = (AnchorStyles.Left | AnchorStyles.Right);
AutoSize = false;
TextAlign = ContentAlignment.MiddleCenter;
Enabled = false;
Font = new Font("Microsoft Sans Serif", 6);
ForeColor = TextColor;
BorderStyle = BorderStyle.FixedSingle;
Dock = DockStyle.Fill;
}
// Creates a border of specified thickness and color
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (BorderStyle == BorderStyle.FixedSingle)
{
int halfThickness = BorderThickness / 2;
using (Pen p = new Pen(BorderColor, BorderThickness))
{
e.Graphics.DrawRectangle(p, new Rectangle(halfThickness,
halfThickness,
ClientSize.Width - BorderThickness, ClientSize.Height - BorderThickness));
}
}
}
public int BorderThickness { get; set; }
public Color BorderColor { get; set; }
}
Code for adding CustomLabel to Panels:
// Add Panels to TableLayoutPanel
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
// Create new Panel
Panel space = new Panel()
{
Size = new Size(45, 45),
Dock = DockStyle.Fill,
Margin = new Padding(0),
ForeColor = Color.Red
};
space.MouseClick += new MouseEventHandler(MouseDownOnSpace);
CustomLabel info = new CustomLabel(false, 0, Color.Empty, Color.Red); // Create new CustomLabel
space.Controls.Add(info); // Add CustomLabel to Panel
tlp.Controls.Add(space, j, i); // Add Panel to TableLayoutPanel
}
}
Code for adjusting the CustomLabel:
((CustomLabel)tlp.GetControlFromPosition(col, row).Controls[0]).BorderThickness = 6;
((CustomLabel)tlp.GetControlFromPosition(col, row).Controls[0]).BorderColor = Color.Yellow;
tlp.GetControlFromPosition(col, row).Controls[0].Text = tlp.GetControlFromPosition(col, row).Controls[0].Text; // Transfer space information
tlp.GetControlFromPosition(col, row).Refresh(); // Refresh Panel to show changes
This is the end result:
(As you can see, the ForeColor still doesn't change.)

Drag from treeview to picture box and connect elements

I'm stuck with this problem. I have in one frame pictureBox and treeview. I want to drag and drop from treeview to picture box and in that picture box I paint another picture (first picture box is map in treeview i have 'Sings')
private void pictureBox1_DragDrop(object sender, DragEventArgs e)
{
Type testTip = new TreeNode().GetType();
TreeNode dragedNode;
_mouseDownSelectionWindow= Rectangle.Empty;
if (e.Data.GetDataPresent(testTip))
{
dragedNode = (TreeNode)e.Data.GetData(testTip);
PictureBox picOneFaceUpA = new PictureBox();
picOneFaceUpA.Parent = MapView;
picOneFaceUpA.Tag = dragedNode;
TreeNode tr = (TreeNode)picOneFaceUpA.Tag;
Sing vr = (Sing)tr.Tag;
picOneFaceUpA.Name = vr.idSing;
vr.onMap = true;
int xCord = e.X;
int yCord = e.Y;
picOneFaceUpA.Location = MapView.PointToClient(new Point (xCord - 20, yCord - 20));
picOneFaceUpA.BackgroundImage = vr.image;
vr.location = picOneFaceUpA.Location;
dictionary.Add(picOneFaceUpA.Location, vr);
picOneFaceUpA.Size = new Size(40, 40);
picOneFaceUpA.BackgroundImageLayout = ImageLayout.Stretch;
picOneFaceUpA.BringToFront();
picOneFaceUpA.Focus();
}
}
And that works, but how I can connect so when I click on Sing on map it gets some kind of focus(frame or something) and it selects treenode in treview that I used to make that picture box.
You already have created a connection when you have stored the dragged node in the PictureBox's Tag.
To make it work you need to code a few events for the created PictureBoxes.
Here is the minimum:
When clicked I set a Border, select the Treenode you have already stored in the Tag and explicitly set the focus on the PictureBox. (PBs don't usually get focus when clicked, so we need to do it in code, so we can catch the LostFocus event..)
picOneFaceUpA.Click += (ss,ee) => {
picOneFaceUpA.BorderStyle = BorderStyle.FixedSingle;
treeView1.SelectedNode = dragedNode;
picOneFaceUpA.Focus();
};
And in the LostFocus I simply remove the Border:
picOneFaceUpA.LostFocus += (ss, ee) =>
{
picOneFaceUpA.BorderStyle = BorderStyle.None;
};
Don't forget to set the treeview HideSelection = false; so you can see the selection while the focus still is on the PB!
Now, depending on the images you show, the border may be a bit weak. For a stronger effect you would have to code the Paint event to draw something more flashy on the top of the control..:
picOneFaceUpA.Paint += (ss, ee) =>
{
if (picOneFaceUpA.BorderStyle == BorderStyle.FixedSingle)
ee.Graphics.DrawRectangle(Pens.Orange, 0, 0,
picOneFaceUpA.ClientSize.Width - 1,
picOneFaceUpA.ClientSize.Height - 1);
};
In your code you use a picOneFaceUpA.Focus();call. This seems to clash with setting the LostFocus event, so will have to remove it!

Forcing the inner part of a form to stay in the same position while changing border style

I have a winform that starts out with a sizable border around it. On the form is a button which, when pressed, changes the form to border style none.
The problem is that then the inner part of the form moves up and to the left slightly. I want to make it that, no matter what border is used, the "inner" part of the form will always stay in the same spot (note: but I do still want the form to be moved around when it has a movable border selected)
Thanks.
The Borderless form moves up and slightly left because thats the location that the form currently has,you need to count for the border.To achieve the result you are after you need to reassign the location property and to do that you need to account for the client size and the whole size(with border),the code i think is simple and it will be self-explanatory i believe:
private void button1_Click(object sender, EventArgs e)
{
var X = (this.Size.Width - this.ClientRectangle.Width) / 2;
var Y = (this.Size.Height - this.ClientRectangle.Height) - X;
Point p = new Point(Location.X + X, Location.Y + Y);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Location = p;
}
Another option is to set the padding on the parent that contains the panel with the border. E.g.
public class Form10 : Form {
Button btn = new Button { Text = "Button", Location = new Point(100, 100) };
Panel border = new Panel { Dock = DockStyle.Fill, BorderStyle = BorderStyle.Fixed3D };
public Form10() {
Controls.Add(border);
border.Controls.Add(btn);
btn.Click += delegate {
if (border.BorderStyle == BorderStyle.Fixed3D) {
border.BorderStyle = BorderStyle.None;
border.Parent.Padding = new Padding(2);
}
else {
border.BorderStyle = BorderStyle.Fixed3D;
border.Parent.Padding = new Padding(0);
}
};
}
}

Centered and scrolled PictureBox in WinForms

I'm developing a WinForms application and can't figure out how to resolve an issue.
I need to show an image in a Form. Because the image can be arbitrarily large, I need scrollbars on the picturebox containing the image so the user can see it entirely.
Googling around I found out the best way to achieve this is to add the PictureBox as a Child Control of a Panel, and making the Panel autosizable and autoscrollable.
I did that programatically since using the designer I was unable to insert the picturebox as a child control of the panel.
The problem I'm now facing is that I can't seem to be able to center and scroll the picturebox at the same time.
If I put the anchor of the picturebox to top,left,bottom,right, the scrollbars are not shown and the image displayed is strange, if I put back the anchor to only top-left, the image is not centered.
Is there any way to do both at the same time?
Here's the code for my Panel and Picturebox:
this.panelCapturedImage = new System.Windows.Forms.Panel();
this.panelCapturedImage.SuspendLayout();
this.panelCapturedImage.AutoScroll = true;
this.panelCapturedImage.AutoSize = true;
this.panelCapturedImage.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.panelCapturedImage.Controls.Add(this.pictureBoxCapturedImage);
this.panelCapturedImage.Location = new System.Drawing.Point(0, 49);
this.panelCapturedImage.Name = "panelCapturedImage";
this.panelCapturedImage.Size = new System.Drawing.Size(3, 3);
this.panelCapturedImage.TabIndex = 4;
this.pictureBoxCapturedImage.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pictureBoxCapturedImage.Location = new System.Drawing.Point(0, 0);
this.pictureBoxCapturedImage.Name = "pictureBoxCapturedImage";
this.pictureBoxCapturedImage.Size = new System.Drawing.Size(0, 0);
this.pictureBoxCapturedImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
this.pictureBoxCapturedImage.TabIndex = 0;
this.pictureBoxCapturedImage.TabStop = false;
this.panelCapturedImage.Controls.Add(this.pictureBoxCapturedImage);
And here's where I set the image:
public Image CapturedImage
{
set
{
pictureBoxCapturedImage.Image = value;
pictureBoxCapturedImage.Size = value.Size;
}
}
For the PictureBox, set SizeMode = AutoSize, Anchor it Top, Left, and set its Location to 0, 0.
Set Panel.AutSize to False and Panel.AutoScroll to True.
When you set the PictureBox.Image property, it will auto-size to the size of the image. You can then use that size to set the panel's AutoScrollPosition property:
public Image CapturedImage
{
set
{
pictureBoxCapturedImage.Image = value;
panelCapturedImage.AutoScrollPosition =
new Point {
X = (pictureBoxCapturedImage.Width - panelCapturedImage.Width) / 2,
Y = (pictureBoxCapturedImage.Height - panelCapturedImage.Height) / 2
};
}
}
If the image is smaller then then panel's size, it will remain in the upper left corner. If you want it centered within the panel, you'll have to add logic to set its Location appropriately.
Based on earlier answers I was able to create this full example:
private void testShowPictureBox()
{
/* format form */
Form frmShowPic = new Form();
frmShowPic.Width = 234;
frmShowPic.Height = 332;
frmShowPic.MinimizeBox = false;
frmShowPic.MaximizeBox = false;
frmShowPic.ShowIcon = false;
frmShowPic.StartPosition = FormStartPosition.CenterScreen;
frmShowPic.Text = "Show Picture";
/* add panel */
Panel panPic = new Panel();
panPic.AutoSize = false;
panPic.AutoScroll = true;
panPic.Dock = DockStyle.Fill;
/* add picture box */
PictureBox pbPic = new PictureBox();
pbPic.SizeMode = PictureBoxSizeMode.AutoSize;
pbPic.Location = new Point(0, 0);
panPic.Controls.Add(pbPic);
frmShowPic.Controls.Add(panPic);
/* define image */
pbPic.ImageLocation = #"c:\temp\pic.png";
frmShowPic.ShowDialog();
}
The picturebox has to be set to autosize. anchored at the center (or a border).
You could manage all of this in the designer, don't undertand your problem with that.
The panel has to be set to autoscroll to true.

Categories

Resources