When I try to draw a shape, like a line, is working only in the first StackPanel
Line myLine = new Line();
myLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
myLine.X1 = 1;
myLine.Y1 = 1;
myLine.X2 = 500;
myLine.Y2 = 50;
// myLine.HorizontalAlignment = HorizontalAlignment.Left;
// myLine.VerticalAlignment = VerticalAlignment.Center;
myLine.StrokeThickness = 2;
gridAreaFight.Children.Add(myLine);
And here is the picture of the grid.
I try to create a Tower Defense game, and I want the towers to attack (with lines/shapes) on the grid, but it seems the draw method is working only in my first stack panel image (see the picture, in the left corner top). Is there something I'm not doing right? Or I should use something else instead drawing method for my towers?
You need to set row and column span for the line in order to draw it in whole available space. If you are drawing something on the grid without specific row and column, it defaults to column and row number 0.
Line myLine = new Line();
myLine.Stroke = System.Windows.Media.Brushes.LightSteelBlue;
Grid.SetRowSpan(myLine, n); // Here you are setting RowSpan. n-number of rows to span
Grid.SetColumnSpan(myLine, n); // Here you are setting ColumnSpan. n-number of columns to span
myLine.X1 = 1;
myLine.Y1 = 1;
myLine.X2 = 500;
myLine.Y2 = 50;
// myLine.HorizontalAlignment = HorizontalAlignment.Left;
// myLine.VerticalAlignment = VerticalAlignment.Center;
myLine.StrokeThickness = 2;
area.Children.Add(myLine);
I am using this code, it is a bad example, but it works to test, but it ends up changing all rows.
I need only change the row selected.
if (e.Cell.Column.Layout.Override.RowSizingAutoMaxLines == 4)
{
e.Cell.Column.Layout.Override.RowSelectorStyle = Infragistics.Win.HeaderStyle.XPThemed;
e.Cell.Column.Layout.Override.RowSizingAutoMaxLines = 20;
}
else
{
e.Cell.Column.Layout.Override.RowSelectorStyle = Infragistics.Win.HeaderStyle.Default;
e.Cell.Column.Layout.Override.RowSizingAutoMaxLines = 4;
}
Setting the RowSizingAutoMaxLines on Override will set this to all the rows. What you can do instead is calculate the necessary row height and set it to the current row, assuming you have set beforehand RowSizing to Free or to AutoFree. You can use Graphics MeasureString to calculate the height of one row and set then each row's height like this:
First setup the grid:
private void UltraGrid1_InitializeLayout(object sender, InitializeLayoutEventArgs e)
{
// I think you need row selectors as you set their style
e.Layout.Override.RowSelectors = Infragistics.Win.DefaultableBoolean.True;
// Set the RowSizing to some Free value to allow each row to has its onw height
e.Layout.Override.RowSizing = RowSizing.AutoFree;
// I think you have multiline text in the cells, so you should set CellMultiLine to true too
e.Layout.Override.CellMultiLine = Infragistics.Win.DefaultableBoolean.True;
}
Then measure one row and set the row's height:
// Calculate the height of one line of text
var oneLineHeight = float.MinValue;
using(Graphics g = this.ultraGrid1.CreateGraphics())
{
oneLineHeight = g.MeasureString("Jj", this.ultraGrid1.Font, int.MaxValue, StringFormat.GenericTypographic).Height;
}
// Set the row selectors' style and the row's height
if(e.Cell.Column.Layout.Override.RowSelectorStyle == Infragistics.Win.HeaderStyle.Default)
{
e.Cell.Column.Layout.Override.RowSelectorStyle = Infragistics.Win.HeaderStyle.XPThemed;
// Add 4 to add some padding
e.Cell.Row.Height = (int)(oneLineHeight * 20 + 4);
}
else
{
e.Cell.Column.Layout.Override.RowSelectorStyle = Infragistics.Win.HeaderStyle.Default;
// Add 4 to add some padding
e.Cell.Row.Height = (int)(oneLineHeight * 4 + 4);
}
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.)
I'm working on a code-editor and want to auto-adjust the width of a label as the number increases. For example, for 1-9 (1 digit) there's a specific width. Then when it gets to 10-99 (2 digits), width of label increases. Then again for then 100-999 (3 digits), etc.
The result should be something like this:
Here is my code:
private void timer_countline_Tick(object sender, EventArgs e)
{
updateNumberLabel();
}
private void updateNumberLabel()
{
// we get index of first visible char and number of first visible line
Point pos = new Point(0, 0);
int firstIndex = rtb.GetCharIndexFromPosition(pos);
int firstLine = rtb.GetLineFromCharIndex(firstIndex);
// now we get index of last visible char and number of last visible line
pos.X = ClientRectangle.Width;
pos.Y = ClientRectangle.Height;
int lastIndex = rtb.GetCharIndexFromPosition(pos);
int lastLine = rtb.GetLineFromCharIndex(lastIndex);
// this is point position of last visible char, we'll use its Y value for calculating numberLabel size
pos = rtb.GetPositionFromCharIndex(lastIndex);
// finally, renumber label
numberLabel.Text = "";
for (int i = firstLine; i <= lastLine + 1; i++)
numberLabel.Text += i + 1 + "\n";
}
You can use TextRenderer for doing what you want. Please check the following code lines (You should add the code lines to TextChanged event of your label):
Please remember that the AutoSize property of your controls must set to False.
This is for changing Width of your control to fit with width of its contents.
yourLabelName.Width = TextRenderer.MeasureText(yourLabelName.Text, yourLabelName.Font).Width;
This is for changing Height of your control to fit with height of its contents.
yourLabelName.Height = TextRenderer.MeasureText(yourLabelName.Text, yourLabelName.Font).Height;
Update 1:
For changing your panel Width for showing all contents in it horizontally, you can use the followng code line:
yourPanelName.Width = yourLabelName.Left + yourLabelName.Width;
For changing your panel Height for showing all contents in it vartically, you can use the followng code line:
yourPanelName.Height = yourLabelName.Top + yourLabelName.Height;
Update 2:
If you are used SplitContainer control, you must change the properties of your SplitContainer as follows:
FixedPanel = none
IsSplitterFixed = False
Then you can use the following lines of code for achieve to what you want:
For changing your SplitContainer panel Width for showing all contents in it horizontally, you can use the followng code line:
int yourLabelNameWidth = TextRenderer.MeasureText(yourLabelName.Text, yourLabelName.Font).Width;
yourSplitContainerName.SplitterDistance = yourLabelName.Left + yourLabelNameWidth;
yourLabelName.Width = yourLabelName.Left + yourLabelNameWidth;
I have a DataGridView that displays a limited number of rows, never more than 5. This DataGridViewis placed on a DataRepeater control so it's usually displayed many times on the screen. What I want to achieve is that all grids are resized to the size of their contents so they don't display scroll bars if 4 or 5 items are in them or take up extra vertical space if only 1 or 2 items are there.
The grids only contain text data. They are data bound controls, so they'll need to resize if the underlying data source changes (I guess the DataBindingComplete event would be suitable).
How may I achieve this? Is counting rows the best option? Thanks in advance.
Since your control is data-bound, I would set the Height property on the DataGridView to the sum of the heights of its rows (plus some margin) in the DataBindingComplete event:
private void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
var height = 40;
foreach (DataGridViewRow dr in dataGridView1.Rows) {
height += dr.Height;
}
dataGridView1.Height = height;
}
I took hmqcnoesy's answer and expanded on it and created a function to also include the width. And to use on any grid.
Note: Set AutoSizeCells = AllCells on the grid.
public static DataGridView SetGridHeightWidth(DataGridView grd, int maxHeight, int maxWidth)
{
var height = 40;
foreach (DataGridViewRow row in grd.Rows)
{
if(row.Visible)
height += row.Height;
}
if (height > maxHeight)
height = maxHeight;
grd.Height = height;
var width = 60;
foreach (DataGridViewColumn col in grd.Columns)
{
if (col.Visible)
width += col.Width;
}
if (width > maxWidth)
width = maxWidth;
grd.Width = width;
return grd;
}
This value can be anything. You should test in your grid to discover what's the best value to set the height.
var height = 40;
Edited:
To find the real value to height, you need add the location of grid and height of header. Something like that.
int height = dgv.Location.Y + dgv.ColumnHeadersHeight;
foreach (DataGridViewRow dr in dgv.Rows) {
height += dr.Height; // Row height.
}
dgv.Height = height;