I'm trying to implement a DataGridView that has smaller cell width than autosize does.
If you look really close at a autosized cell you'll notice that there is still some space that is not used to actually display the cell's content.
That's why I started to measure the content's width by myself via TextRenderer and then manually set the column's width.
The initial problem was that a "A" was displayed as "A..." long before the cell was actually "filled". The reason for that was cell.Style.WrapMode set to "nonSet". I was quite happy that DataGridViewTriState.True did work for that "A"-example.
But now I just noticed that if the String has multiple words ("A, B") the DataGridView tries to display the content to several lines long before the cell is actually "filled".
What I'm now looking for is either a way to delete that "padding" of the cell's content or fully suppress the word wrapping on a certain cell/column (= single line without the String's cut off).
Additionally I should admit that there are no hidden blanks on that Strings so trim has no effect at all.
Edit:
I randomly stumbled upon some colleagues code that seems to do kind of what I'm searching.
StringFormat format = new StringFormat(StringFormatFlags.NoClip);
The documentation says that strings have some rectangle around them that is bigger than the string itself. If the rectangle sticks out of the writeable area the string is wrapped. That code snippet suppresses that (default) behaviour.
The only problem is that this solution only seems to work for drawing strings. I didn't find a possibility to assign a stringformat object to a string.
Try This Code
dataGridView1.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells;
dataGridView1.DefaultCellStyle.WrapMode to DataGridView1TriState.True
Hope it helps you
Can you Try this code. That is working in my condition.
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.Value == null)
return;
var s = e.Graphics.MeasureString(e.Value.ToString(), dataGridView1.Font);
if (s.Width > dataGridView1.Columns[e.ColumnIndex].Width)
{
using (
Brush gridBrush = new SolidBrush(this.dataGridView1.GridColor),
backColorBrush = new SolidBrush(e.CellStyle.BackColor))
{
e.Graphics.FillRectangle(backColorBrush, e.CellBounds);
e.Graphics.DrawString(e.Value.ToString(), dataGridView1.Font, Brushes.Black, e.CellBounds, StringFormat.GenericDefault);
dataGridView1.Rows[e.RowIndex].Height = (int)(s.Height * Math.Ceiling(s.Width / dataGridView1.Columns[e.ColumnIndex].Width));
e.Handled = true;
}
}
}
Related
The goal is to add a column of paragraph numbers to a RichTextBox (the numbers show the index of paragraphs in that richtextbox.Document.Blocks). Currently, I use this block of code in LayoutUpdated event of the RichTextBox:
bool _added=false
void onLayoutUpdated(object sender, EventArgs e)
{
if (!_added)
{
_added= true;
scv = Helper.GetFind.FindChild<ScrollViewer>(this, null);
if (scv != null)
{
FrameworkElement documentView = scv.Content as FrameworkElement;
scv.ClearValue(ScrollViewer.ContentProperty);
Grid grid= new Grid();
... I will talk about what I have added here...
scv.Content = grid;
UpdateLayout();
}
}
}
In the grid, I add two columns, the first one is a StackPanel and the second one is the documentView. For each paragraph I add a TextBlock to the StackPanel.Children and I set the height of each textBlock by using Paragraph.ElementStart.GetCharacterRect(LogicalDirection.Forward) methods and the Top & Bottom Properties of the returned Rect(s).
Everything is fine and when there are less than 500 paragraphs, the numbering updates quickly, But as the text gets larger, it gets slower. How can I make it more efficient? Should I use a Canvas instead of a StackPanel? Or is there any better way of doing this?
Thanks.
ListView GridView. Support virtualiztion. I use textblock for each line in some very large document and works great.
I used the procedure I mensioned in the question and then Dispacher.BeginInvoke(...) method. I set DispatcherPriority to ApplicationIdle. I call it when Width chaneges or new Paragraph is added. Sth like this:
_updateIfWidthChangedDispacherO= _ownerTextBox.Dispatcher.BeginInvoke((Action)(() =>
{
updateIfWidthChanged();
}),
DispatcherPriority.ApplicationIdle);
I am working on a custom C# TreeView and I would like to do some custom draw to highlight the keywords appear in the name of nodes.
I did :
DrawMode = TreeViewDrawMode.OwnerDrawText;
in the constructor of the custom TreeView and override the OnDrawNode:
protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
if (!e.Node.IsVisible) { return; }
if (e.Node.Bounds.IsEmpty) { return; }
e.DrawDefault = false;
...draw calls...
But it worked strangely after I coded like this, the perceived behaviors include:
OnDrawNode being call on the child nodes which is not expanded and invisible
When the content of the TreeView updates, the user would see the old content and new content at the same time overlapping with each other. The old content would disappear not until about half second or longer.
The rendering speed is much slower than the original draw call.
Another modification I did is the code snippet I found here to suppress the flickering happens when the TreeView is updating:
http://dev.nomad-net.info/articles/double-buffered-tree-and-list-views
But it seems not directly related to the problem since I can still see the text overlapping after removing it.
I wonder if anyone have any idea about this issue?
Any thought would be appreciated.
Thank you.
edit:
The content of the OnDrawNode is like:
string pattern = keyword;
if (!string.IsNullOrWhiteSpace(pattern))
{
Regex regularExpressionnew = Regex(pattern);
Match match = regularExpression.Match(e.Node.Text);
while (match.Success)
{
CaptureCollection captureCollection = match.Groups[0].Captures;
foreach (Capture capture in captureCollection)
{
int highlightStartIndex = capture.Index;
int highlightEndIndex = capture.Index + pattern.Length;
e.Graphics.FillRectangle(nodeHightLightColor, GetTextBoundsBetweenIndex(e.Graphics, e.Node.Text, highlightStartIndex, highlightEndIndex, e.Bounds));
}
match = match.NextMatch();
}
Brush drawBrush = new SolidBrush(Color.Black);
e.Graphics.DrawString(e.Node.Text, Font, drawBrush, e.Bounds);
GetTextBoundsBetweenIndex is essentially calculating the square area covering the characters between highlightStartIndex and highlightEndIndex.
But the lag and overlap would happen event the regular expression is commented out and only the text rendering left.
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 column that holds Checkboxes.
My main problem is that when I call this function:
dataGrid.AutoResizeColumn(0, DataGridViewAutoSizeColumnMode.DisplayedCells);
It stretches the checkbox column too, and I want that column to stay in 25 in width.
How can I do that? (only checkbox column not stretched)
Here is some more code, showing what I want to happen:
dataGrid.AutoResizeColumn(0, DataGridViewAutoSizeColumnMode.DisplayedCells);
dataGrid.AutoResizeColumn(1, DataGridViewAutoSizeColumnMode.Fill);
dataGrid.AutoResizeColumn(2, DataGridViewAutoSizeColumnMode.Fill);
dataGrid.AutoResizeColumn(3, DataGridViewAutoSizeColumnMode.DisplayedCells);
dataGrid.AutoResizeColumn(4, DataGridViewAutoSizeColumnMode.Fill);
dataGrid.AutoResizeColumn(5, DataGridViewAutoSizeColumnMode.Fill);
But the fill gives me an error.
What you are asking doesn't quite make sense, since the method you show AutoResizeColumn take as its first parameter the column index to resize - if you call this method with the index of the checkbox column then your are explicitly telling the grid to resize that column. If you don't want the resize, don't do that!
If you set the resize mode the next level up for the grid, you do it like this:
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
Now with this method of setting the sizing mode then yes, the checkbox will change when you might want it to.
The answer to that is to use either the designer or the method you mention above, and set the checkbox columns autosize mode to None
If you absolutely must loop over the columns setting their AutoResize mode, then the only option you have is to check if you have the checkbox column in the loop and apply a different more.
This is in answer to your comment - the error you will be seeing is an ArgumentException being thrown by the AutoResizeColumn method. This is all documented on the MSDN page for the AutoResizeColumn method. You cannot specify a AutoSize mode of None or Fill.
It sounds like what you want to do is something like:
// If column 3 is the checkbox column, we set its resize mode to none:
dataGridView1.Columns[3].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
// Then we set the width:
dataGridView1.Columns[3].Width = 25;
// Finally we set the rest of the grid to fill or what ever resizing you need:
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
you can define in datagridview_cellpainting event
private void gvDocumentList_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.ColumnIndex == gvDocumentList.Columns["checkbox column name"].Index && e.RowIndex >= 0)
{
e.PaintBackground(e.ClipBounds, true);
Rectangle rectRadioButton = new Rectangle();
rectRadioButton.Width = 14;
rectRadioButton.Height = 14;
rectRadioButton.X = e.CellBounds.X + (e.CellBounds.Width - rectRadioButton.Width) / 2;
rectRadioButton.Y = e.CellBounds.Y + (e.CellBounds.Height - rectRadioButton.Height) / 2;
e.Paint(e.ClipBounds, DataGridViewPaintParts.Focus);
e.Handled = true;
}
}
I'm using the following code to make my treenodes bold:
Font font = new Font(tvQuestionSequence.Font, FontStyle.Bold);
foreach (QuestionnaireBuilder_Category cat in categories)
{
TreeNode node = new TreeNode();
node.Text = cat.Description;
node.Name = cat.Id.ToString();
node.NodeFont = font;
tvQuestionSequence.Nodes.Add(node);
}
But the text of the bold nodes is not displayed correctly. The last letter(s) are not shown. How come? And how to solve this problem?
I found this Post when searching through the web because I am facing the exact same problem.
However, appending a white space to the end of the node was not an option, and I found an alternative way that seems to fix the issue.
After setting my node font Bold, all I need to do is reset the node text with the same value.
Here is the Code Sample:
Font boldFont = new Font(treeview.Font, FontStyle.Bold);
node.NodeFont = boldFont;
node.Text = node.Text;
It seems that the node is redrawn after changing the text, which is exactly what I wanted in the first place.
I've found that this is a Windows issue. A workaround for this problem is this:
In the form constructor set the font of the treeview to bold. When adding nodes which must not be bold, change the font to regular:
// Constructor of your form
public Form()
{
InitializeComponent();
Font font = new Font(tvQuestionSequence.Font, FontStyle.Bold);
tvQuestionSequence.Font = font;
}
// Add regular nodes (not bold)
Font font = new Font(tvQuestionSequence.Font, FontStyle.Regular);
TreeNode treeNode = new TreeNode();
treeNode.Text = "Foo";
treeNode.NodeFont = font;
TreeNode parent = tvQuestionSequence.Nodes.Find("parent", true);
parent.Nodes.Add(treeNode);
Simply use treeView.BeginUpdate() before you bold the node then treeView.EndUpdate() after you've bolded the node.
This is a known Windows bug.
The simple solution is just to append an extra space character at the end of your strings. The space character will not be visible, but it will increase the number of pixels needed to draw the string, so the entire string will be visible.
This is all not helping for me.
What DID the trick is making the font a little bigger and bold at DESIGN time.
(In the Properties window)
So make sure you define the treeview with big enough font, then later you can add nodes with smaller font. They will fit.
I do agree with the solution provided. I just want to add to it to shed a little more light on what the problem is.
The treeview has its own font which determines the width of items at the root level. That compensates for the fact that there is only an item height property available and no item width property.
The solution to your problem is to determine what the font of your root node should be, then set the tree to that same font. You can do that at design time also.
Hope that helps someone.
A workaround for this problem is this:
Set the defaul font of treeview to bold in the properties.
And chnage to not bold when you need.
I do the following, I set the DrawNode Event to call, it sets the node to bold and removes the highlighted colour.
You can set any colour you like using the first parameter of the e.Graphics.FillRectangle function.
private void SetNodeBoldWhenSelected(object sender, DrawTreeNodeEventArgs e)
{
if (e.Node == null) return;
var font = e.Node.NodeFont ?? e.Node.TreeView.Font;
if (e.Node.IsSelected)
{
font = new Font(font, FontStyle.Bold);
}
var bounds = new Rectangle( e.Bounds.X,e.Bounds.Y,e.Bounds.Width+20,e.Bounds.Height);
e.Graphics.FillRectangle(SystemBrushes.ControlDarkDark, bounds);
TextRenderer.DrawText(e.Graphics, e.Node.Text, font, bounds, SystemColors.HighlightText, TextFormatFlags.GlyphOverhangPadding);
}
Now when I select a node I get 20 pixels more space, for my font, this works well, one can calculate the "real" size needed but there is no specification stating it needs to do this but you can use Graphics.MeasureString if you feel you need to do that.
Very easy and works fine
treeView1.SelectedNode.NodeFont = new System.Drawing.Font(treeView1.SelectedNode.TreeView.Font, treeView1.SelectedNode.TreeView.Font.Style | FontStyle.Bold);
this.treeView1.SelectedNode.Text += string.Empty;
I realize this is an old thread and it may have been answered. I just ran across this problem as I'm learning to use TreeViews. What worked for me was changing the font size for the entire TreeView to the same size, or bigger than the font of the level you want to bold. The default font size is 8.something. I changed mine to 10, which was the size I wanted my nodes, and the truncating was gone.
What worked for me: Hooking into the load event in the Control's constructor and tweaking the node as explained in BlunT's answer.
public MyControl()
{
InitializeComponent();
_head = new TreeNode();
this.Load += (s, e) =>
{
trvParts.Nodes.Clear();
_head.NodeFont = new Font(trvParts.Font, FontStyle.Bold);
trvParts.Nodes.Add(_head);
_head.Text = "Node Name";
};
}
It's in vb.Net however the solution to re-enter the value of the TEXT field gets around this nicely. As in:
With myNode
Dim myText As String = .Text 'capture the text
.NodeFont = New Font(<name of your treeview>.Font, FontStyle.Bold)
.Text = myText 'reset the text to the original value
End With
Based on MSDN Library, try change your code to:
Font font = new Font(tvQuestionSequence.Font, FontStyle.Bold);
foreach (QuestionnaireBuilder_Category cat in categories)
{
TreeNode node = new TreeNode();
node.Text = cat.Description;
node.Name = cat.Id.ToString();
node.NodeFont = font;
tvQuestionSequence.BeginUpdate(); //added newline here <--
tvQuestionSequence.Nodes.Add(node);
tvQuestionSequence.EndUpdate(); //added newline here <--
}
It work for me