I want a box of a fixed size to show some text and have a link in it that is clickable in the bottom right corner for editing. Clicking this edit link shows a set of fields to fill in.
I tried LinkLabel, which does the trick, but, when I change the text, the size of the box changes. I set autosize to false and longer text forces the link outside the box. Short text puts the link to far up.
I could get fancy and calculate the position of the link and insert it at the appropriate place (adding new lines if needed), but I'm wondering if there isn't an easier way to do this.
Is there a better control for doing this or another way of doing this?
EDIT:
The boxes that are filled in are concatenated and replace the text in the linklabel. The Edit link is currently appended to this and a LinkArea (of the last 4 characters) is set.
You need to build a composite layout, for instance using a Panel with Label/TextBox (Dock = Fill) and LinkLabel (Dock = Bottom, TextAlign = MiddleRight) inside, like this
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Samples
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var text = "I want a box of a fixed size to show some text and have a link in it that is clickable in the bottom right corner for editing.";
int textSize = 50;
var form = new Form { Padding = new Padding(8) };
var panel = new Panel { Parent = form, BorderStyle = BorderStyle.FixedSingle, Padding = new Padding(4) };
var label = new Label { Dock = DockStyle.Fill, Parent = panel, AutoSize = false, Text = text, Height = textSize };
var link = new LinkLabel { Dock = DockStyle.Bottom, Parent = panel, AutoSize = false, TextAlign = ContentAlignment.MiddleRight, Text = "Edit" };
panel.Location = form.DisplayRectangle.Location;
panel.Width = form.DisplayRectangle.Width;
panel.Height = panel.Padding.Vertical + link.Height + label.Height;
Application.Run(form);
}
}
}
Result:
I found another way.
I use a text box and position a linklabel in its corner.
textbox is readonly and disabled so it acts like a label and the backcolor is set to white to over-ride the disabled/readonly colour.
The edit link now stays put
Related
first time poster, sorry if something isn't as it should be.
I'm new to Winforms and am trying to build a simple application that will display multiple features of an item (like Size, Composition, etc.). Each Characteristic has a Name, can have a Descritpion, and some can have sub-characteristics (having also a name and sometimes a descritpion).
I want to display them one under the other, with the Name of the feature on a blue background that span the whole width of the container, with the description underneath. The name will be (or have) a button (or similar) that when clicked collapse or expand the description. This must be created at run time because I don't know how many feature an object has until the user generate it.
The issues I'm running in are that either I can't span the blue background the whole width of the container (if using a FlowLayoutPanel), or I have some issue with the Description text being not the right size (either it wraps but is too big, or it doesn't wrap and then I can't see the whole text).
Some things are fixed, mainly the number of main sections (like Size, Composition, Weather, etc.), so I can prepare the skeleton before runtime.
The closest i've been to making it work gives this. This issue here is that the height of the Panel which the description label is embded in is fixed, and if I put in in Autosize, the text don't show (probably because the label is in Fill dock style). Just as information, this is what it looks like when collapsed (this is indeed what I'm looking for)
I know some library exists with collapsible panels, but I'd rather try to make it work without external libraries. Thanks in advance for any help !
This is the code that produces the results in the screenshots :
Panel SizeDescrPanel = new Panel();
SizeDescrPanel.Dock = DockStyle.Top;
//SizeDescrPanel.AutoSize = true;
SizeDescrPanel.AutoSizeMode = AutoSizeMode.GrowAndShrink;
SizeDescrPanel.BackColor = Color.Bisque;
SizePanel.Controls.Add(SizeDescrPanel);
Label SizeDescrLbl = new Label();
SizeDescrLbl.Text = Lorem;
SizeDescrLbl.AutoSize = false;
SizeDescrLbl.Dock = DockStyle.Fill;
SizeDescrLbl.BackColor = Color.BurlyWood;
SizeDescrPanel.Controls.Add(SizeDescrLbl);
/*using(Graphics g = CreateGraphics())
{
SizeF size = g.MeasureString(SizeDescrLbl.Text, SizeDescrLbl.Font, SizePanel.Width);
SizeDescrPanel.Height = (int) Math.Ceiling(size.Height);
}*/
Panel SizeNamePanel = new Panel();
SizeNamePanel.Dock = DockStyle.Top;
SizeNamePanel.BackColor = Color.Cyan;
SizeNamePanel.AutoSize = true;
SizePanel.Controls.Add(SizeNamePanel);
Button SizeNameBtn = new Button();
SizeNameBtn.Text = "<Size Name> ..." + SizeDescrLbl.Height;
SizeNameBtn.TextAlign = ContentAlignment.MiddleLeft;
SizeNameBtn.FlatStyle = FlatStyle.Flat;
SizeNameBtn.AutoSize = true;
SizeNamePanel.Controls.Add(SizeNameBtn);
SizeNameBtn.Click += delegate { HideShowPanel(SizeDescrPanel); };
It,s a test project, so later I'll put that in different methods. What isn't shown here :
I have a main panel set to Fill containing everything.
The text "SIZE" is a label set to Top
Under it is another Panel (SizePanel) that is set to Top and Autosize is at True. This is the Panel inside which I'm puttin my size name and my size description. If I had a subfeature, it would be included (ideally) inside descritpion with the same configuration (button expanding/collapsing the descritpion of the SubFeature)
Whenever I set a TextBox's border style to "None" the bottom of text in the box is invisible. That means I can't see underscores and descenders of letters like y and j.
Here is the textbox with border
Here is the textbox without border
Any suggestions as to how this is solved would be appreciated.
This seemed to work for me:
public Form1()
{
InitializeComponent();
textBox1.Multiline = true;
textBox1.MinimumSize = new Size(0, 30);
textBox1.Size = new Size(textBox1.Size.Width, 30);
textBox1.Multiline = false;
}
Setting box to multi line to adjust size of the box then when its changed back keeps the size.
I have an application that has a tool strip with related controls set apart by ToolStripSeparaters. It looks something like this:
However, when the window size shrinks, some of the controls get moved to a little drop-down section. Unfortunately, this can split related controls, for example in the screenshot below, the "Filter by ID" label, the associated textbox for the ID, and "Clear Filter" button are no longer shown together.
If controls have to be moved to the drop-down, I'd prefer to have related controls move together. Is there a way to group related controls together on a ToolStrip? Or perhaps a better way of dealing with this sort of scenario?
I tried using the LayoutCompleted event move all of the controls to the overflow area if any of them are in the overflow.
private void toolStrip1_LayoutCompleted(object sender, EventArgs e)
{
var filterGroup = new List<ToolStripItem> { lblFilter, txtFilter, btnClearFilter };
if (filterGroup.Any(x => x.IsOnOverflow))
{
filterGroup.ForEach(x => x.Overflow = ToolStripItemOverflow.Always);
}
}
This seems to work fine, but I haven't found a good way to show them again when the window size is increased. I tried both the Resize and Layout events of the ToolStrip with the following code:
var filterGroup = new List<ToolStripItem> { lblFilter, txtFilter, btnClearFilter };
filterGroup.ForEach(x => x.Overflow = ToolStripItemOverflow.AsNeeded);
You could use a ToolStripControlHost to group a winforms TextBox and Label. E.g.
public class ToolStripLabelTextBox : ToolStripControlHost {
public Label Label { get; private set; }
public TextBox TextBox { get; private set; }
public ToolStripLabelTextBox(String labelText) : base(new FlowLayoutPanel { FlowDirection = FlowDirection.LeftToRight, WrapContents = false, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, Padding = Padding.Empty, Margin = Padding.Empty }) {
Label = new Label { Text = labelText, AutoSize = true, Anchor = AnchorStyles.Top | AnchorStyles.Bottom, TextAlign = System.Drawing.ContentAlignment.MiddleCenter };
TextBox = new TextBox();
FlowLayoutPanel panel = (FlowLayoutPanel) Control;
panel.Controls.Add(Label);
panel.Controls.Add(TextBox);
}
}
Two other options are:
Implement a LayoutEngine that does the grouping you want.
Implement a composite ToolStripItem that displays a label and text box. You can use the ToolStripRadioButtonMenuItem as an example: https://msdn.microsoft.com/en-us/library/vstudio/ms404318%28v=vs.100%29.aspx
I'm currently playing around with layouts and made a test project where I construct a Form which displays a Panel which contains a TableLayoutPanel with three rows:
a text box
a button
a placeholder label which is supposed to take up the remaining vertical space.
This test works properly, but if I set the Minimum Size of the Text Box to e.g. (400, 200), I can no longer see the button. Shouldn't the first row in the table layout AutoSize to its content? Note that
setting RowStyles explicitly to SizeType.AutoSize doesn't change anything.
No minimum size set:
Minimum size set:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace LayoutTest
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var sampleForm = new Form();
var samplePanel = new Panel() { Dock = DockStyle.Fill };
var sampleTextBox = new TextBox() { Dock = DockStyle.Fill };
// This line breaks the layout
//sampleTextBox.MinimumSize = new Size(400, 200);
var sampleButton = new Button() { Dock = DockStyle.Fill };
var panelLayout = new TableLayoutPanel() { Dock = DockStyle.Fill };
panelLayout.Controls.Add(sampleTextBox, 0, 0);
panelLayout.Controls.Add(sampleButton, 0, 1);
// Add a placeholder label to take up the remaining space
panelLayout.Controls.Add(new Label() { Text = String.Empty, Dock = DockStyle.Fill });
samplePanel.Controls.Add(panelLayout);
sampleForm.Controls.Add(samplePanel);
Application.Run(sampleForm);
}
}
}
The button is underneath the textbox. You need to set the multiline property to true.
E.g.
sampleTextBox.Multiline = true;
The source of this behavior is either the TableLayoutPanel or the TextBox. It would be strange for the TableLayoutPanel to explicitly test if a Control is a TextBox and if Multiline property is set to true before deciding to adhere to the the MinimumSize constraint. However, in my testing, it appears that the Multiline property must be set before adding it to the TableLayoutPanel and if Multiline is unset, then the control goes back underneath the text box and never goes back even if Multiline is set to true again.
E.g.
sampleButton.Click += delegate {
Size s1 = sampleTextBox.MinimumSize; // always returns the set MinSize
sampleTextBox.Multiline = !sampleTextBox.Multiline;
Size s2 = sampleTextBox.MinimumSize; // always returns the set MinSize
panelLayout.Invalidate(true);
panelLayout.PerformLayout();
};
Let me begin by saying that I have not done a lot of Windows Forms development -- if there is an obvious mistake that I may be making, please don't hesitate to mention it.
Steps to reproduce my issue:
Create a new C# Windows Forms Project using VS 2010 or VS 2012
Using the VS Form Designer, add three FlowLayoutPanel components to the form
Set each FlowLayoutPanel to have the same height as the form and approximately 1/3 the width of the form
Position each FlowLayoutPanel so that they do not overlap each other horizontally and collectively consume approximately the entire area of the Form.
The leftmost FlowLayoutPanel is configured to have an Anchor of Top, Bottom, Left
The middle FlowLayoutPanel is configured to have an Anchor of Top, Bottom
The rightmost FlowLayoutPanel is configured to have an Anchor of Top, Bottom, Right
Add an event for Form_Shown:
private void Form1_Shown(object sender, EventArgs e)
{
Panel p = new Panel();
p.BorderStyle = BorderStyle.FixedSingle;
p.Width = 200;
p.Height = 100;
Label label1 = new Label();
label1.BorderStyle = BorderStyle.FixedSingle;
label1.Text = "Hello";
label1.Anchor = AnchorStyles.Top;
Label label2 = new Label();
label2.BorderStyle = BorderStyle.FixedSingle;
label2.Text = "World!";
label2.Anchor = AnchorStyles.Bottom;
p.Controls.Add(label1);
p.Controls.Add(label2);
middleFlow.Controls.Add(p); // add to the center most FlowLayoutPanel on Form1
}
The result seems to be that label1 is placed on top of label2, despite label2 being added second. Moreover, the anchor values seem to be ignored (as label1 is covering label2 when I intend for them to be anchored to the top and bottom of the Panel component, respectively)
If I use the Dock property instead of the Anchor property, the behavior is as desired. Why does the Anchor property not work in this situation?
Also, is there a way to anchor components to other components? I notice as I increase the size of my Form at runtime, horizontal "gaps" between panels appear. Ideally, I would like the panels to grow together, preventing any gaps/whitespace between them horizontally?
Thanks in advance for any suggestions or tips.
I'm still starting to learn c# and winforms, so the following may not be optimal but it does what you required.
Handled the labels with Dock=Top. Note that the labels are switched so that label1 is on top of label2, i.e., registering label1 last pushes down the already registered label2.
The positioning of the three panels is done without anchors and docks with an event handler for resize. Setting the size of the form after that raises a resize event. Colored to see the components.
using System;
using System.Drawing;
using System.Windows.Forms;
public class ThreePanel : Form {
FlowLayoutPanel leftFlow;
FlowLayoutPanel middleFlow;
FlowLayoutPanel rightFlow;
public ThreePanel(){
leftFlow = new FlowLayoutPanel() {
BackColor = Color.Yellow
};
middleFlow = new FlowLayoutPanel() {
BackColor = Color.LightGreen
};
rightFlow = new FlowLayoutPanel() {
BackColor = Color.LightBlue
};
this.Controls.Add(rightFlow);
this.Controls.Add(middleFlow);
this.Controls.Add(leftFlow);
this.Load += (s,e)=>Form1_Shown(s,e);
this.Resize += (s,e)=>{
int w=this.Width/3;
leftFlow.Width=middleFlow.Width
=rightFlow.Width=w;
leftFlow.Height=middleFlow.Height
=rightFlow.Height=this.Height;
leftFlow.Location=new Point(0,0);
middleFlow.Location=new Point(w,0);
rightFlow.Location=new Point(2*w,0);
};
this.Size = new Size(750,450);
}
private void Form1_Shown(object sender, EventArgs e)
{
Panel p = new Panel() {
BorderStyle = BorderStyle.FixedSingle,
Width = 200,
Height = 100,
BackColor = Color.Fuchsia,
};
Label label1 = new Label() {
BorderStyle = BorderStyle.FixedSingle,
Text = "Hello",
Dock = DockStyle.Top
};
Label label2 = new Label() {
BorderStyle = BorderStyle.FixedSingle,
Text = "World!",
Dock = DockStyle.Top
};
p.Controls.Add(label2);
p.Controls.Add(label1);
// add to the center most FlowLayoutPanel on Form1
middleFlow.Controls.Add(p);
}
public static void Main()
{
Application.Run(new ThreePanel());
}
}
I would expect exactly the behaviour that you mentioned.
The Anchor property only tells the parent container that the label should be sticked
to the parent. In your case AnchorStyles.Top means stick the label to the top and leave it there if the parent moves or resizes.
You did not specify dimensions or positions for the labels, so both overlapp.
The z-order of the controls is created implicitly from the order when added to middleFlow.Controls. You can check this using VS forms designer. Select "Bring to Front" or "Send to Back" and watch how the x.designer.cs changes.
Why it is in reverse order is one of the little .net secrets. The workaround is to change the order. Sometimes it is easier to do it manually than in the designer.