How to resize cell content (font size) if it doesn't fit. I don't want word-wrap or change cell's size since the form has to fit on single page. The text might have various lengths. It might contain spaces but doesn't have to.
#edit
I've made a mistake. The control I meant is not a XtraGrid but XRTable.
I suggest you to look at the Appearances at first. There are multiple ways to customize appearances of individual rows and cells.
If these options are not helpful, you can manually draw cell content as you needed using the Custom Drawing feature. For example, you can use the GridView.CustomDrawCell event to check whether or not the cell's content is exceed the cell's bounds and update the font of this cell accordingly.
Related example: How to: Custom Draw Cells Depending Upon Cell Values
You can change the font of the cell where the text flows as follows
private void gvView_CustomDrawCell(object sender, DevExpress.XtraGrid.Views.Base.RowCellCustomDrawEventArgs e)
{
if (e.Column != null && e.Column.Name == bgcStav.Name)
{
float minFontSize = 6;
string text = "teeeeeeeeeeeeeext";
int minWidth = gvView.CalcColumnBestWidth(bgcStav);
SizeF s = e.Appearance.CalcTextSize(e.Graphics, text, minWidth);
if (s.Width >= minWidth)
{
e.Appearance.Font = new Font(e.Appearance.Font.FontFamily, minFontSize);
}
}
}
but it is much better to trim the text if it overflows (you do not know how long the text can be), when you do not want to use wordwrap
private void gvView_CustomDrawCell(object sender, DevExpress.XtraGrid.Views.Base.RowCellCustomDrawEventArgs e)
{
if (e.Column != null && e.Column.Name == bgcStav.Name)
{
string text = e.DisplayText;
string newText = "";
int maxWidth = e.Bounds.Width - 20;
SizeF textSize =e.Graphics.MeasureString(text, e.Appearance.Font);
if (textSize.Width >= maxWidth)
{
string textPom = "";
for (int i = 0; i < text.Length; i++)
{
textPom = text.Substring(0, i) + "...";
textSize = e.Graphics.MeasureString(textPom, e.Appearance.Font);
if (textSize.Width >= maxWidth)
{
newText = text.Substring(0, i - 1) + "...";
break;
}
}
e.DisplayText = newText;
}
}
}
Advantage of this solution is that the crop only what is displaied, but in the datatable text remains in its original form
Related
I use TreeListView component from ObjectListView library. I make cell values editable and when i double click on them TextBox will appear with odd offset.
I want to remove this offset, but how?
before start editing
after start editing
As you can see first row, second column ("My new device"), TextBox is appeared with offset.
P.S. Editing work as expected. Only offset is annoying me
P.P.S. As you can see the offset depends of first column offset. How i can change it to zero?
After long searches i found solution!
Source code of OLV, ObjectListView.cs
public virtual void StartCellEdit(OLVListItem item, int subItemIndex) {
OLVColumn column = this.GetColumn(subItemIndex);
Control c = this.GetCellEditor(item, subItemIndex);
Rectangle cellBounds = this.CalculateCellBounds(item, subItemIndex);
c.Bounds = this.CalculateCellEditorBounds(item, subItemIndex, c.PreferredSize);
// Try to align the control as the column is aligned. Not all controls support this property
Munger.PutProperty(c, "TextAlign", column.TextAlign);
// Give the control the value from the model
this.SetControlValue(c, column.GetValue(item.RowObject), column.GetStringValue(item.RowObject));
// Give the outside world the chance to munge with the process
this.CellEditEventArgs = new CellEditEventArgs(column, c, cellBounds, item, subItemIndex);
this.OnCellEditStarting(this.CellEditEventArgs);
if (this.CellEditEventArgs.Cancel)
return;
// The event handler may have completely changed the control, so we need to remember it
this.cellEditor = this.CellEditEventArgs.Control;
this.Invalidate();
this.Controls.Add(this.cellEditor);
this.ConfigureControl();
this.PauseAnimations(true);
}
I saw CellEditEventArgs contains Control, that drawed in area named Bounds.
This function in the source file append offset to control's bounds:
protected Rectangle CalculateCellEditorBoundsStandard(OLVListItem item, int subItemIndex, Rectangle cellBounds, Size preferredSize) {
if (this.View == View.Tile)
return cellBounds;
// Center the editor vertically
if (cellBounds.Height != preferredSize.Height)
cellBounds.Y += (cellBounds.Height - preferredSize.Height) / 2;
// Only Details view needs more processing
if (this.View != View.Details)
return cellBounds;
// Allow for image (if there is one).
int offset = 0;
object imageSelector = null;
if (subItemIndex == 0)
imageSelector = item.ImageSelector;
else {
// We only check for subitem images if we are owner drawn or showing subitem images
if (this.OwnerDraw || this.ShowImagesOnSubItems)
imageSelector = item.GetSubItem(subItemIndex).ImageSelector;
}
if (this.GetActualImageIndex(imageSelector) != -1) {
offset += this.SmallImageSize.Width + 2;
}
// Allow for checkbox
if (this.CheckBoxes && this.StateImageList != null && subItemIndex == 0) {
offset += this.StateImageList.ImageSize.Width + 2;
}
// Allow for indent (first column only)
if (subItemIndex == 0 && item.IndentCount > 0) {
offset += (this.SmallImageSize.Width * item.IndentCount);
}
// Do the adjustment
if (offset > 0) {
cellBounds.X += offset;
cellBounds.Width -= offset;
}
return cellBounds;
}
We can see, that offset appending to every cell (not only first). Also we can se, that CellEditEventArgs contains CellBounds.
So we can just reset Control.Bounds to CellBounds value:
//...
dataTreeView.CellEditStarting += DisableInputValueForCollections;
//...
private static void DisableInputValueForCollections(object sender, CellEditEventArgs e)
{
RemoveExtraOffsetForNotFirstColumnInputControl(e);
var node = e.RowObject as DataTreeNode;
if (node != null && e.Column.AspectName == "Value")
{
if (node.IsContainer()) e.Cancel = true;
}
}
//...
private static void RemoveExtraOffsetForNotFirstColumnInputControl(CellEditEventArgs e)
{
if (e.Column.AspectName != "Name")
{
e.Control.Bounds = e.CellBounds;
}
}
//...
The issue still exists in OLV v2.9.1. I worked around it slightly different than muzagursiy did.
olv.CellEditStarting += (sender, args) =>
{
// Left align the edit control
args.Control.Location = args.CellBounds.Location;
// Readjust the size of the control to fill the whole cell if CellEditUseWholeCellEffective is enabled
if (args.Column.CellEditUseWholeCellEffective)
{
args.Control.Size = args.CellBounds.Size;
}
};
I want to insert a string in my RichTextbox, at a specific position and with a specific color. So I tried to add an extension for the method AppendText() of the RichTextbox class.
public static void AppendText(this RichTextBox Box, string Text, Color col, int SelectionStart)
{
Box.SelectionStart = SelectionStart;
Box.SelectionLength = 0;
Box.SelectionColor = col;
Box.SelectionBackColor = col;
Box.Text = Box.Text.Insert(SelectionStart, Text);
Box.SelectionColor = Box.ForeColor;
}
I tried to use this in a class called RichTextBoxExtension. The result is not as per my expectation. The string is inserted but not with the color selected.
Is there any better way to do this functionality?
EDIT: I think it could be interesting to inform you why I need this functionality. Actually, when user write a closing parenthesis, I would like to highligh (or color) the associative opening parenthesis.
so for example if the user write (Mytext), the first parenthesis will be in color when user tapped ")" and keep the selection on this parenthesis.
You have to use the SelectedText property of the RichTextBox control. Also make sure to keep track of the values of the current selection before you change anything.
Your code should look like this (I give away what Hans was hinting at) :
public static void AppendText(this RichTextBox Box,
string Text,
Color col,
int SelectionStart)
{
// keep all values that will change
var oldStart = Box.SelectionStart;
var oldLen = Box.SelectionLength;
//
Box.SelectionStart = SelectionStart;
Box.SelectionLength = 0;
Box.SelectionColor = col;
// Or do you want to "hide" the text? White on White?
// Box.SelectionBackColor = col;
// set the selection to the text to be inserted
Box.SelectedText = Text;
// restore the values
// make sure to correct the start if the text
// is inserted before the oldStart
Box.SelectionStart = oldStart < SelectionStart ? oldStart : oldStart + Text.Length;
// overlap?
var oldEnd = oldStart + oldLen;
var selEnd = SelectionStart + Text.Length;
Box.SelectionLength = (oldStart < SelectionStart && oldEnd > selEnd) ? oldLen + Text.Length : oldLen;
}
I want to create this view. But I don't know how to make this kind of button, is it feasible with C#? If so, Please help me.
For a ListView you can get something that looks like your image by
splitting the data into the Text and the Tags of each Item/Subitem
owner-drawing the ListView
storing the two images (including headroom) in two Bitmaps.
Coding the MouseDown event to bring the 'buttons' to live
Here is the code for owner-drawing.
private void listView3_DrawItem(object sender, DrawListViewItemEventArgs e)
{
Rectangle textBounds = e.Bounds; textBounds.Height /= 2;
e.Graphics.DrawImage(e.Item.Text == "True" ? bmpOn : bmpOff, e.Bounds.Location);
TextRenderer.DrawText(e.Graphics, e.Item.Tag.ToString(),
Font, textBounds, Color.Black, TFFcenter);
}
private void listView3_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
Rectangle textBounds = e.Bounds; textBounds.Height /= 2;
e.Graphics.DrawImage(e.SubItem.Text == "True" ? bmpOn : bmpOff, e.Bounds.Location);
TextRenderer.DrawText(e.Graphics, e.SubItem.Tag.ToString(),
Font, textBounds, Color.Black, TFFcenter);
}
private void listView3_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
e.DrawDefault = true;
}
The code to process clicking on a ListView is simple but not obvious:
private void listView3_MouseDown(object sender, MouseEventArgs e)
{
ListViewHitTestInfo HI = listView3.HitTest(e.Location);
if (HI.SubItem != null) HI.SubItem.Text = HI.SubItem.Text == "True" ? "False" : "True";
else if (HI.Item != null) HI.Item.Text = HI.Item.Text == "True" ? "False" : "True";
}
The DrawText call uses TextFormatFlags to center the text in the upper half:
TextFormatFlags TFFcenter =
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter;
The trick is in preparing the whole ListView.
For my test I used this routine:
ImageList il = new ImageList();
il.ImageSize = new Size(1, bmpOff.Height);
listView3.SmallImageList = il;
for (int c = 0; c < listView3.Columns.Count; c++)
listView3.Columns[c].Width = bmpOff.Width;
for (int i = 0; i < listView3.Columns.Count; i++)
{
ListViewItem lvi = new ListViewItem( (i % 2 == 0).ToString() );
for (int c = 0; c < 5; c++)
{
lvi.SubItems.Add( ( (c+i) % 2 == 0).ToString());
lvi.SubItems[c].Tag = "3/7";
}
lvi.Tag = "3/7";
listView3.Items.Add(lvi);
}
listView3.Width = listView3.Columns.Count * bmpOff.Width + 4;
Note how I use a dummy ImageList to enforce the Item heights..but while you're at it, it is probably a better idea to add the ImageList in the Designer and store the Bitmaps in it..
When you want to access/change the text displayed above the buttons you need to use the Tag of each Item/Subitem..!
It is also possible to use a DataGridView and get the same look by cell painting it.
Note that none of the solutions lends itself well to databinding as you have too many data to bind per item/cell!
In Windows Forms. (That it looks like what you are using.) You can drop a DataGridView on a form and change the ColumnType in the GridView designer to DataGridViewCheckBoxColumn. This will give you the Functionality that you are looking for.
In other words, if you dont mind the look, (But need the same functionality) you could use a Checkbox in your grid.
When you have the correct functionality : You can use the above answer from Gnqz, to get the Look of the button.
when the selection area is combined with 2 areas that has different fonts, it returns null. The code is below:
private Font myGetSelectionFont()
{
Font t = this.workspace.SelectionFont;
if (t == null)
return defaultFont;
else return t;
}
private void toolStripComboBox_size_SelectedIndexChanged(object sender, EventArgs e)
{
string sizeString = ((ToolStripComboBox)sender).SelectedItem.ToString();
float curSize = float.Parse(sizeString);
Font oldFont = myGetSelectionFont();
Font newFont = this.getFont(oldFont.FontFamily.Name, curSize, oldFont.Style);
this.setFontIcons(newFont);
this.workspace.SelectionFont = newFont;
this.workspace.Focus();
}
There is no problem to set a different font for the selected area, but i don't know the font size and other properties for the selected area. what should i do?
I think I can choose to set the font as the font before the selected area, or behind the selected area, but i don't know how to do it.
A possible solution would be to loop
private void toolStripComboBox_size_SelectedIndexChanged(object sender, EventArgs e)
{
float size = float.Parse(((ToolStripComboBox)sender).SelectedItem.ToString());
SetFontSize(richTextBox1, size);
}
private void SetFontSize(RichTextBox rtb, float size)
{
int selectionStart = rtb.SelectionStart;
int selectionLength = rtb.SelectionLength;
int selectionEnd = selectionStart + selectionLength;
for (int x = selectionStart; x < selectionEnd; ++x)
{
// Set temporary selection
rtb.Select(x, 1);
// Toggle font style of the selection
rtb.SelectionFont = new Font(rtb.SelectionFont.FontFamily, size, rtb.SelectionFont.Style);
}
// Restore the original selection
rtb.Select(selectionStart, selectionLength);
}
I am trying to highlight List of strings in my dataGrid view. For this purpose, I used the existing code that highlights a keyword in dataGridView. but resultant code only higlights last occurrence of the list (i.e. last record fetched).
Here is What I've tried
private void dvg_ClauseSent_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
foreach (string sw in HighlightStrings) // HighlightStringsis the list of string containing all strings I need to highlight in DataGrid view
{
int strt = 0;
int cnt = -1;
int idx = -1;
TextFormatFlags flags = TextFormatFlags.Default | TextFormatFlags.NoPrefix;
if ((((e.RowIndex >= 0) && (e.ColumnIndex >= 0))))
{
e.Handled = true;
e.PaintBackground(e.CellBounds, true);
if (!string.IsNullOrEmpty(sw))
{
string val = e.FormattedValue.ToString();
int sindx = val.IndexOf(sw);
if ((sindx >= 0))
{
while (strt != -1)
{
strt = val.IndexOf(sw, idx + 1);
cnt += 1;
idx = strt;
if (strt != -1)
{
if (strt != 0 && ((strt + sw.Length) != val.Length))
{
Rectangle hl_rect = new Rectangle();
hl_rect.Y = (e.CellBounds.Y + 2);
hl_rect.Height = (e.CellBounds.Height - 5);
// find the size of the text before the search word
// and the size of the search word
// paint the background behind the search word
e.Graphics.FillRectangle(hl_brush, hl_rect);
hl_brush.Dispose();
}
}
}
}
}
}
// paint the content as usual
e.PaintContent(e.CellBounds);
}
}
Attached ScreenShots
Following screenshot shows the strings that should appear as highligted in dataGridView
http://i42.tinypic.com/2dtrea1.png
Part of strings enclosed in ANGLE BRACKET followed/preceeded by |* / *| should be appeared as highlighted but only last entry is being highlighted.
http://i39.tinypic.com/30cbw9l.png
Any help will be appreciated...
Your code has this strange thing:
Rectangle hl_rect = new Rectangle();
hl_rect.Y = (e.CellBounds.Y + 2);
hl_rect.Height = (e.CellBounds.Height - 5);
You don't even initialize the X and the Width (hence they will be empty by default). So how could it be rendered???
I would like to talk this to everyone who will be asking questions, please post your actual code. Don't try simplifying it if you don't understand how wrong it is after being simplified. Except the code above, I don't find any thing which may cause the issue (it's of course not tested, just a quick scan). I've tried writing another code for you, also tested. The problem is we have to draw the string and measure the string together so that the Text Bound can be determined exactly. The TextRenderingHint.AntiAlias should also be used, although sometimes it works without that. The drawn text may look a little blurry however it can be partially eliminated by using a large font, the larger the better. Here is the code for you.
private void dataGridView1_CellPainting(object sender,
DataGridViewCellPaintingEventArgs e) {
if (e.RowIndex > -1 && e.ColumnIndex > -1 && e.Value != null) {
string value = e.Value.ToString();
foreach (var s in HighlightStrings) {
int i = 0;
while (i < value.Length && (i = value.IndexOf(s,i))!=-1) {
if (!e.Handled){
e.Handled = true;
e.PaintBackground(e.ClipBounds, true);
}
StringFormat sf = StringFormat.GenericTypographic;
sf.LineAlignment = StringAlignment.Center;
RectangleF textBounds = GetTextBounds(e.Graphics,
value, i, s.Length,
e.CellBounds,
e.CellStyle.Font, sf);
//highlight it
e.Graphics.FillRectangle(Brushes.Yellow, textBounds);
i += s.Length;
using (Brush brush = new SolidBrush(e.CellStyle.ForeColor)) {
//draw string , don't use PaintContent
e.Graphics.DrawString(value, e.CellStyle.Font, brush,
e.CellBounds, sf);
}
}
}
}
}
public RectangleF GetTextBounds(Graphics g, string text,
int subIndex, int subLength,
RectangleF layout,
Font font, StringFormat sf) {
var charRange = new CharacterRange(0, text.Length);
var subCharRange = new CharacterRange(subIndex, subLength);
sf.SetMeasurableCharacterRanges(new[]{ charRange, subCharRange });
var regions = g.MeasureCharacterRanges(text, font, layout, sf);
return regions.Length < 2 ? RectangleF.Empty : regions[1].GetBounds(g);
}
NOTE: The reason we have to use DrawString is as I said to measure the TextBound exactly. If you have some way to measure it exactly without having to draw the string yourself, you can use PaintContent.