My question is basically, does implementing DrawItem for my ComboBox in WinForms, change my Text property, why and I can I stop it?
Because my OwnerDraw event works perfectly except the Text property "also" gets set to the same logic as all the items in Items[] (ie implemented in DrawItem event below)
For context, I show URL's in the list, but some are so long I basically chop them and put the text "..." at the end - to make it more readable. I have DataSource set so that it renders one property of my class "DisplayUrl" but uses another "Url" for the actual value. (MyUrl below)
At the end of some code, I explicitly set cmbUrl.Text = "THE FULL TEXT"
But somehow the DrawItem event is also effecting the "Text" property because even after running this code, once the DrawItem event is finished my Text property is set to the same as Item[0]. ie With the text chopped off - as in "THE FULL T..."
void cmbUrl_DrawItem(object sender, DrawItemEventArgs e)
{
var text = ((MyUrl)((ComboBox)sender).Items[e.Index]).DisplayUrl;
var brush = text.Contains("bla) ? Brushes.DarkGreen : Brushes.Black;
// Fill in the background
e.Graphics.FillRectangle(new SolidBrush(e.BackColor), e.Bounds);
if (e.Index < 0) return;
// Work out where every thing goes
int nX = e.Bounds.Left;
int nY = e.Bounds.Top;
const int nMarg = 2;
int nH = e.Bounds.Height - (2 * nMarg);
// Draw the Colour Gymph
var penFore = new Pen(e.ForeColor);
var rectGymph = new Rectangle(nX + nMarg, nY + nMarg, nH, nH);
e.Graphics.FillRectangle(brush, rectGymph);
e.Graphics.DrawRectangle(penFore, rectGymph);
var fullWidth = nX + nH + (2 * nMarg);
e.Graphics.DrawString(text, e.Font, brush, fullWidth, e.Bounds.Top);
}
I think you want to show your the full Text in your combobox and just want to show the short text in Items drop-down list, so the solution may be this:
private void cmbUrl_DropDown(object sender, EventArgs e){
cmbUrl.DisplayMember = "DisplayUrl";
}
private void cmbUrl_DropDownClosed(object sender, EventArgs e){
cmbUrl.DisplayMember = "Url";
}
Related
I have 2 comboboxes for the font and the fontsize. When I click them it changes the font size or the font in my richtextbox. Now I want it to work like in word. If the line you just moved to is in a different font or size. It should detect that and change the comboxes to match the font and size of the current line. Somoeone else asked this same question and got a result which didn't work for me. It was as follows
private void richTextBox1_SelectionChanged(object sender, EventArgs e)
{
MessageBox.Show("we got here"); // this is my added part to let me know if the code is even getting executed. It is not.
richTextBox1.SelectionStart = 1;
richTextBox1.SelectionLength = 1;
comboBox1.Text = richTextBox1.SelectionFont.ToString();
comboBox2.Text = null;
comboBox2.Text = richTextBox1.SelectionFont.Size.ToString();
}
I held out hope that it was my answer but I could not see how SelectionFont would make any difference when nothing was selected. Also the richTextBox1_SelectionChanged event seems to not be being called when I move through the document with the up/down arrows. The problem is not with the comboboxes, the problem is that as I arrow through my document I need to be able to know what font and size it is at the caret position so it can fire an event to change the combo boxes to match.
The code that you are using will always make the selection from character at index 1 and are of the length 1. instead for that you need to use which will give you the the following code without specifying the selection(so it will take the selection from the ritchTextBox).
string fontName = richTextBox1.SelectionFont.Name;
float fontsize = richTextBox1.SelectionFont.Size;
You should save the values for the new comboBox position temporarily in variables, otherwise if you do it directly
comboBox1.SelectedIndex = comboBox1.FindStringExact(richTextBox1.SelectionFont.Name);
the comboBox1_SelectedIndexChanged event will be immediately called and could affect the results.
So just try:
private void richTextBox1_SelectionChanged(object sender, EventArgs e)
{
int comboBox1Index = comboBox1.FindStringExact(richTextBox1.SelectionFont.Name);
int comboBox2Index = comboBox2.FindStringExact(richTextBox1.SelectionFont.Size.ToString());
comboBox1.SelectedIndex = comboBox1Index;
comboBox2.SelectedIndex = comboBox2Index;
}
I adapted Sujith's solution and half of Markus's solution and came up with the following which works just fine for me:
Private Sub Description_SelectionChanged(sender As Object, e As EventArgs) Handles Description.SelectionChanged
Dim fontName As String = Description.SelectionFont.Name
Dim fontSize As Single = Description.SelectionFont.Size
tbSelectFont.Text = fontName
tbSelectSize.Text = fontSize
End Sub
I'm sure this is something very easy to figure out but I cannot do it. I have a winform with 3 Label inside a Panel. When the form loads, the first Label has a Paint event that draws a rectangle on it. I would like a backgroundWorker to go through each one, wait 5 seconds, restore the Label to normal (redrawing I'm guessing) and then draw a rectangle on the following Label.
public List<Label> GetLabelList()
{
return new List<List>()
{
label1,
label2,
label3,
label4
};
}
private void bgBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var getList = GetLabelList();
for (int i = 0; i < getList.Count; i++)
{
if ((bgBackgroundWorker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
Thread.Sleep(5000);
getList [i].Paint += RemoveLabelHighlight;
getList [i].Invalidate();
if (i < 2)
{
getList [i + 1].Paint += AddLabelHighlight;
getList [i + 1].Invalidate();
}
bgBackgroundWorker.ReportProgress((i * 10));
}
}
}
private void AddLabelHighlight(object sender, PaintEventArgs e)
{
var label = sender as Label;
e.Graphics.DrawRectangle(new Pen(Color.DeepPink, 8), label.ClientRectangle);
}
private void RemoveLabelHighlight(object sender, PaintEventArgs e)
{
var label = sender as Label;
e.Graphics.DrawRectangle(new Pen(Color.Green, 8), label.ClientRectangle); // This should return the Label back to original state
}
This works but when the rectangle is drawn, the label is cut off all the way around. Any suggestions?
Also, I'm sure there is a much better and more efficient way to achieve this, maybe by an EventHandler or something. I'd like some suggestions, if possible.
This is actually being caused by your use of the pen width of 8 pixels, I believe. Try a different size and see if that changes the size of the rectangle not being drawn.
To fill the rectangle instead, use:
e.Graphics.FillRectangle(new SolidBrush(Color.DeepPink), e.ClipRectangle);
EDIT Since you're now completely responsible for drawing the control, the text can be redrawn with a DrawString call:
e.Graphics.DrawString(label.Text, label.Font, SystemBrushes.ControlText, new PointF(0,0));
EDIT Here's how to nest a panel and a label to achieve what you're looking for:
Add a new panel, set the padding to 8,8,8,8, and BackColor to whatever you like
Add a new label to this panel, set it's AutoSize property to false, Dock property to Fill, and TextAlign property to MiddleCenter
While I have always loved doing owner-drawn stuff, sometimes it's just easier to use what's there! For fun though, I would wrap this into a new Panel-derived control to make it easy to reuse.
I'm writing a program where the user should be able to write text in a TextBox. I'd like the TextBox to resize itself, so it fits to the content.
I've tried the following:
private void textBoxTitle_TextChanged(object sender, TextChangedEventArgs e)
{
System.Drawing.Font myFont = new System.Drawing.Font("Verdana", 8);
System.Drawing.SizeF mySize = e.Graphics.MeasureString("This is a test", myFont);
this.textBoxTitle.Width = (int)Math.Round(mySize.Width, 0);
}
I get an error saying that Graphics doesn't work for TextChangedEventArgs. Is there another way I can resize the TextBox?
You should try a code something like below. It has worked for me well.
private void textBox1_TextChanged(object sender, EventArgs e)
{
Size size = TextRenderer.MeasureText(textBox1.Text, textBox1.Font);
textBox1.Width = size.Width;
textBox1.Height = size.Height;
}
For more information refer to TextRenderer.MeasureText()
I am adding this answer as I do not see the fixed width aspect of a textbox being discussed in any of the other. If you have a fixed width for your textbox, and you want to adjust only its height you can do something like the following:
Something like this gives the height of the text as how it is drawn in the multiline wordwrapped textbox itself:
SizeF MessageSize = MyTextBoxControl.CreateGraphics()
.MeasureString(MyTextBoxControl.Text,
MyTextBoxControl.Font,
MyTextBoxControl.Width,
new StringFormat(0));
I am not sure what StringFormat should be but the values StringFormatFlags do not seem to apply to a default TextBox make up.
Now with MessageSize.Height you know the height of the text in the textbox.
I had the same problem and I solved it in a simpler way.
I used the AutoSize property of a Label control.. I added an invisible label to my form, set its AutoSize property True. When the I need to change the size of my TextBox I use this code:
MyLabel.Text = MyTextBox.Text;
MyTextBox.Size = MyLabel.Size;
I set the Maximum and Minimum Size of the label for better results.
Have Fun
Your binding to the wrong event, and you cannot use the graphics object in the TextChangedEventArgs object.
Try using the TextChanged event. The following snippet is working:
public Form1()
{
InitializeComponent();
this.textBox1.TextChanged += new EventHandler(textBox1_TextChanged);
}
void textBox1_TextChanged(object sender, EventArgs e)
{
System.Drawing.SizeF mySize = new System.Drawing.SizeF();
// Use the textbox font
System.Drawing.Font myFont = textBox1.Font;
using (Graphics g = this.CreateGraphics())
{
// Get the size given the string and the font
mySize = g.MeasureString(textBox1.Text, myFont);
}
// Resize the textbox
this.textBox1.Width = (int)Math.Round(mySize.Width, 0);
}
}
first, create method to Make the TextBox fit its contents.
private void AutoSizeTextBox(TextBox txt)
{
const int x_margin = 0;
const int y_margin = 2;
Size size = TextRenderer.MeasureText(txt.Text, txt.Font);
txt.ClientSize =
new Size(size.Width + x_margin, size.Height + y_margin);
}
then with the TextChanged event handler calls AutoSizeTextBox() function to make the TextBox fit its text when the text changes.
private void txtContents_TextChanged(object sender, EventArgs e)
{
AutoSizeTextBox(sender as TextBox);
}
That’s all, for more info:
resize-a-textbox-to-fit-its-text
You will need to use the CreateGraphics() method of the form to create the Graphics instance to measure the string on.
The TextChangedEventArgs class does not have a Graphics property, that is a property of the PaintEventArgs class passed in to the Paint event handler
Try this:
using System.Drawing;
...
private void textBoxTitle_TextChanged(object sender, TextChangedEventArgs e)
{
// Determine the correct size for the text box based on its text length
// get the current text box safely
TextBox tb = sender as TextBox;
if (tb == null) return;
SizeF stringSize;
// create a graphics object for this form
using(Graphics gfx = this.CreateGraphics())
{
// Get the size given the string and the font
stringSize = gfx.MeasureString(tb.Text, tb.Font);
}
// Resize the textbox
tb.Width = (int)Math.Round(stringSize.Width, 0);
}
Essentially you create your own Graphics object for the form, then measure it based on the text and font of the TextBox. The using will properly dispose the Graphics object - your previous code would have leaked horribly!
Whatever the aim is.
If the size of the textbox should be dynamically set up based on the string, which should be the text inside this box, there is no nice option.
Reasons : MeasureString uses usual string formatters as delimiters for its own width and height.
Means, carriage return and line feed are parsed, too. Resulting in a sizeF.Width and sizeF.Height.
Depending on the string( and its font and number of lines ) these both variables can carry values, which are sometimes useless to be used as width/height values of a textbox ( because they can be bigger than the parentform's values and this would resize the textbox to a size, with left and bottom borders beyond those of the parent form).
Some solutions are still available, depending on the aim, one would like to achieve.
One idea would be :
Create a textbox in designer, size = 100 X 100. enable word-wrapping.
In the OnTextChanged event handler of the textbox, we just resize the textbox's width to a value, defined by ourself (e.g. parentform.Width or another hard value ).
This would cause the word wrap to recalculate the string in the textbox and this would rearrange all the characters inside the textbox, because word wrap is enabled.
The height of the textbox could can be set hard to parentform.Height, for example.
BUT,
better : set the height dynamically,based on the Y value of the ReturnValue (Point) of the method texbox.GetPositionFromCharIndex(textbox.TextLength -1 ).
Then, with Math.Min() determine, which is smaller ( either parentform.Height or Point.Y ) , and reset the textbox size to new Size(previousDeterminedWidth, nowDeterminedHeight).
Please keep in mind ( if scrollbars are enabled ) to add about 17 pixs to Your width calculation.
Best regards
Did you try to set yourTextBox.AutoSize = true;?
This property may be hidden in the GUI designer, but you can set it in the form constructor right after InitializeComponent(); call.
Graphics.Measure string you can do o PaintEventArgs, not on TextChangedEventArgs
What I think you want is this
System.Drawing.Font myFont = new System.Drawing.Font("Verdana", 8);
Graphics graphics = this.CreateGraphics();
SizeF textSize = graphics.MeasureString("This is a test", myFont);
The problem is that you just cannot create a Graphics object by simply allocating it since it has no public constructor, so you should better go and use TextRenderer.MeasureText, as done in http://msdn.microsoft.com/en-us/library/y4xdbe66.aspx
TextRenderer is less accurate because it uses GDI and Graphics uses GDI+, so maybe you should leave a little margin on the value you get from the Width property.
Hope this helps
I have a ComboBox on a form, and its default height is 21. How do I change it?
ComboBox auto-sizes to fit the font. Turning that off is not an option. If you want it bigger then give it a bigger font.
Set the DrawMode to OwnerDrawVariable. However customization of the ComboBox leads to other issues. See this link for a tutorial on how to do this completely:
http://www.csharphelp.com/2006/09/listbox-control-in-c/
OwnerDrawVariable sample code here:
https://msdn.microsoft.com/en-us/library/system.windows.forms.combobox.drawitem%28v=vs.110%29.aspx
Once that's done, you need to set the ItemHeight property of the combobox to set the effective height of the combobox.
Just as another option, if you'd like to increase the height of the ComboBox without increasing the font size or having to worry about drawing everything yourself, you can use a simple Win32 API call to increase the height like this:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Win32ComboBoxHeightExample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, Int32 wParam, Int32 lParam);
private const Int32 CB_SETITEMHEIGHT = 0x153;
private void SetComboBoxHeight(IntPtr comboBoxHandle, Int32 comboBoxDesiredHeight)
{
SendMessage(comboBoxHandle, CB_SETITEMHEIGHT, -1, comboBoxDesiredHeight);
}
private void button1_Click(object sender, EventArgs e)
{
SetComboBoxHeight(comboBox1.Handle, 150);
comboBox1.Refresh();
}
}
}
Result:
To do this, you need to set the DrawMode to OwnerDrawVariable or OwnerDrawFixed and manually draw your items. This can be done with a pretty simple class.
This example will allow you to use the ItemHeight property of the ComboBox regardless of font size. I threw in an bonus property TextAlign which will also allow you to center the items.
One thing worth mentioning is, you must set DropDownStyle to DropDownList for the selected item to respect our customizations.
// The standard combo box height is determined by the font. This means, if you want a large text box, you must use a large font.
// In our class, ItemHeight will now determine the height of the combobox with no respect to the combobox font.
// TextAlign can be used to align the text in the ComboBox
class UKComboBox : ComboBox
{
private StringAlignment _textAlign = StringAlignment.Center;
[Description("String Alignment")]
[Category("CustomFonts")]
[DefaultValue(typeof(StringAlignment))]
public StringAlignment TextAlign
{
get { return _textAlign; }
set
{
_textAlign = value;
}
}
private int _textYOffset = 0;
[Description("When using a non-centered TextAlign, you may want to use TextYOffset to manually center the Item text.")]
[Category("CustomFonts")]
[DefaultValue(typeof(int))]
public int TextYOffset
{
get { return _textYOffset; }
set
{
_textYOffset = value;
}
}
public UKComboBox()
{
// Set OwnerDrawVariable to indicate we will manually draw all elements.
this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;
// DropDownList style required for selected item to respect our DrawItem customizations.
this.DropDownStyle = ComboBoxStyle.DropDownList;
// Hook into our DrawItem & MeasureItem events
this.DrawItem +=
new DrawItemEventHandler(ComboBox_DrawItem);
this.MeasureItem +=
new MeasureItemEventHandler(ComboBox_MeasureItem);
}
// Allow Combo Box to center aligned and manually draw our items
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
// Draw the background
e.DrawBackground();
// Draw the items
if (e.Index >= 0)
{
// Set the string format to our desired format (Center, Near, Far)
StringFormat sf = new StringFormat();
sf.LineAlignment = _textAlign;
sf.Alignment = _textAlign;
// Set the brush the same as our ForeColour
Brush brush = new SolidBrush(this.ForeColor);
// If this item is selected, draw the highlight
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
brush = SystemBrushes.HighlightText;
// Draw our string including our offset.
e.Graphics.DrawString(this.Items[e.Index].ToString(), this.Font, brush,
new RectangleF(e.Bounds.X, e.Bounds.Y + _textYOffset, e.Bounds.Width, e.Bounds.Height), sf);
}
}
// If you set the Draw property to DrawMode.OwnerDrawVariable,
// you must handle the MeasureItem event. This event handler
// will set the height and width of each item before it is drawn.
private void ComboBox_MeasureItem(object sender,System.Windows.Forms.MeasureItemEventArgs e)
{
// Custom heights per item index can be done here.
}
}
Now we have full control over our font and height of the ComboBox separately. We no longer need to make a large font to size our ComboBox.
If you are wanting to adjust to the number of items in the ComboBox you can change the value of the DropDownHeight as follows, given a List of items. I'm using 24 here as a "per item amount"; this is by no means fixed.
comboBox1.DropDownHeight = SomeList.Count * 24;
ComboBox has a property 'DropDownHeight' which can be changed either via the properties window of the combobox or programmatically. i.e.
public partial class EventTestForm : Form
{
public EventTestForm()
{
InitializeComponent();
cmbOwners.DropDownHeight = 100;
}
In code, a.Height should work. In the designer, go into the properties and look in Size->Height.
Alternatively, you can change the Font Size and the combo box will grow bigger to accomodate it, but I don't think that's what you want.
A ToolStripComboBox is placed after a ToolStripButton and is folowed by another one, which is right-aligned. How do I best set up the ToolStripComboBox to always adjust its length to fill all the space available between the preceeding and the folowing ToolStripButtons?
In past I used to handle a parent resize event, calculate the new length to set based on neighboring elements coordinates and setting the new size. But now, as I am developing a new application, I wonder if there is no better way.
I use the following with great success:
private void toolStrip1_Layout(System.Object sender, System.Windows.Forms.LayoutEventArgs e)
{
int width = toolStrip1.DisplayRectangle.Width;
foreach (ToolStripItem tsi in toolStrip1.Items) {
if (!(tsi == toolStripComboBox1)) {
width -= tsi.Width;
width -= tsi.Margin.Horizontal;
}
}
toolStripComboBox1.Width = Math.Max(0, width - toolStripComboBox1.Margin.Horizontal);
}
The above code does not suffer from the disapearing control problem.
There's no automatic layout option for this. But you can easily do it by implementing the ToolStrip.Resize event. This worked well:
private void toolStrip1_Resize(object sender, EventArgs e) {
toolStripComboBox1.Width = toolStripComboBox2.Bounds.Left - toolStripButton1.Bounds.Right - 4;
}
protected override void OnLoad(EventArgs e) {
toolStrip1_Resize(this, e);
}
Be sure to set the TSCB's AutoResize property to False or it won't work.
ToolStrip ts = new ToolStrip();
ToolStripComboBox comboBox = new TooLStripComboBox();
comboBox.Dock = DockStyle.Fill;
ts.LayoutStyle = ToolStripLayoutStyle.Table;
((TableLayoutSettings)ts.LayoutSettings).ColumnCount = 1;
((TableLayoutSettings)ts.LayoutSettings).RowCount = 1;
((TableLayoutSettings)ts.LayoutSettings).SetColumnSpan(comboBox,1);
ts.Items.Add(comboBox);
Now the combobox will dock fill correctly. Set Column or Row span accordingly.