When dragging a node, although I get the Move icon there is no tooltip text shown in my case as there is in the given SimpleExample, using Andrey Gliznetsov's TreeViewAdv.
This doesn't seem to be affected by adding a Tooltip object and using SetToolTip method. How do I get the tooltip to show the text (and icon) of the dragged node when dragging?
]
In TreeViewAdv.Input.cs, you will find this method
private void CreateDragBitmap(IDataObject data)
{
if (UseColumns || !DisplayDraggingNodes)
return;
which prevents a _dragBitmap being created if columns are being used. If you have to use the TreeViewAdv control dll without modification, then the only workaround if you want to see the tooltip is to not use columns (Property UseColumns = False).
But since the source code is provided, you could modify CreateDragBitmap by removing the condition UseColumns from the first if statement in this method and adding a condition inside the foreach NodeControl loop to exit after the first is drawn:
private void CreateDragBitmap(IDataObject data)
{
if (!DisplayDraggingNodes) //removed UseColumns as OR condition here
return;
TreeNodeAdv[] nodes = data.GetData(typeof(TreeNodeAdv[])) as TreeNodeAdv[];
if (nodes != null && nodes.Length > 0)
{
Rectangle rect = DisplayRectangle;
Bitmap bitmap = new Bitmap(rect.Width, rect.Height);
using (Graphics gr = Graphics.FromImage(bitmap))
{
gr.Clear(BackColor);
DrawContext context = new DrawContext();
context.Graphics = gr;
context.Font = Font;
context.Enabled = true;
int y = 0;
int maxWidth = 0;
foreach (TreeNodeAdv node in nodes)
{
if (node.Tree == this)
{
int x = 0;
int height = _rowLayout.GetRowBounds(node.Row).Height;
foreach (NodeControl c in NodeControls)
{
Size s = c.GetActualSize(node, context);
if (!s.IsEmpty)
{
int width = s.Width;
rect = new Rectangle(x, y, width, height);
x += (width + 1);
context.Bounds = rect;
c.Draw(node, context);
if (UseColumns) //only show first column if using columns
break;
}
}
y += height;
maxWidth = Math.Max(maxWidth, x);
}
}
if (maxWidth > 0 && y > 0)
{
_dragBitmap = new Bitmap(maxWidth, y, PixelFormat.Format32bppArgb);
using (Graphics tgr = Graphics.FromImage(_dragBitmap))
tgr.DrawImage(bitmap, Point.Empty);
BitmapHelper.SetAlphaChanelValue(_dragBitmap, 150);
}
else
_dragBitmap = null;
}
}
}
which results in
If you wanted to see all columns when dragging, omit the if (UseColumns) break; change, which looks like this
Related
I searched but didn't find how to set the cell height when printing DataGridView when the cell has long text.
I didn't find one result that concentrate how to calculate the height with long text. And I don't want to use 3rd party DLL file that print it with right cell height.
I use this code to calculate the height but text always cut and short text has lower cell height that on DGV.
var tallestHeight = 0;
foreach (DataGridViewCell cell in GridRow.Cells)
{
if (!cell.Visible) { continue; }
var s = e.Graphics.MeasureString(cell.FormattedValue.ToString(), dataGridView1.Font);
var tempHeight = (int)(s.Height * Math.Ceiling(s.Width / dataGridView1.Columns[cell.ColumnIndex].Width));
if (tempHeight > tallestHeight)
{
tallestHeight = tempHeight;
}
tallestHeight = (tallestHeight < 22) ? 22 : tallestHeight;
}
iCellHeight = tallestHeight;
I want when I print DataGridView to printer to show all the text in all cells without cutting. Long text increase the row height and if no long text row's height stays unchanged.
I have row height = 22
Text wrap for my DataGridView is enabled
Edit1:
Here is my DataGridView properties
Here is how i print my DataGridView: PrintDGV Class that i use
My DGV appears
Print preview
Yellow highlighted text isn't complete Full text is First Middle lastname- some text- 0130011511478- تجربةة 7427/01300/8346584584563846
The text below it complete.
How to show the first row at full?
Apart from the grid settings, you need to use the output sizes to calculate the adequate height of each row to fit their contents. Also, calling a MeasureString method overload that takes StringFormat is necessary to get more accurate result.
I see in the printout image above you are dividing the MarginBounds.Width by the visible cells. Thus, the following:
Calculate
Create a method to calculate and return the proper height of each row.
// +
using System.Linq;
private int GetRowHeight(
Graphics g,
DataGridViewRow row,
Rectangle bounds,
StringFormat sf,
int minHeight = 22)
{
var cells = row.Cells.OfType<DataGridViewTextBoxCell>()
.Where(c => c.Visible);
if (cells == null) return minHeight;
var cell = cells.Aggregate((DataGridViewTextBoxCell)null, (x, y) => x != null &&
x.FormattedValue.ToString().Length > y.FormattedValue.ToString().Length ? x : y);
if (cell == null) return minHeight;
var h = g.MeasureString(cell.FormattedValue.ToString(),
row.DataGridView.Font,
new SizeF(bounds.Width / cells.Count(), bounds.Height),
sf).ToSize().Height;
return Math.Max(h + 6, minHeight); // 6 for top and bottom margins...
}
Call
Caller example and class variables to track the printing...
// +
using System.Drawing.Printing;
private int rowIndex;
private int cellCount;
private int pageNumber;
private readonly PrintDocument printDoc;
// ...
// ctor
public YourForm()
{
InitializeComponent();
printDoc = new PrintDocument();
printDoc.PrintPage += OnPrintPage;
}
// Cleanup
private void YourForm_FormClosed(object sender, FormClosedEventArgs e)
{
printDoc.Dispose();
}
// Preview
private void btnPrintPreview(object sender, EventArgs e) => Print(true);
// Print
private void btnPrint(object sender, EventArgs e) => Print();
// Print Routine
private void Print(bool preview = false)
{
rowIndex = 0;
cellCount = 0;
pageNumber = 0;
var rows = dataGridView1.Rows
.Cast<DataGridViewRow>()
.FirstOrDefault(r => !r.IsNewRow);
if (rows != null)
cellCount = rows.Cells
.OfType<DataGridViewTextBoxCell>()
.Where(c => c.Visible)
.Count();
if (cellCount == 0)
{
MessageBox.Show("Nothing to print...");
return;
}
printDoc.DefaultPageSettings.Landscape = true;
if (preview)
{
using (var pd = new PrintPreviewDialog())
{
pd.Document = printDoc;
pd.ShowDialog();
}
}
else
{
using (var pd = new PrintDialog())
{
pd.Document = printDoc;
if (pd.ShowDialog() == DialogResult.OK)
pd.Document.Print();
}
}
}
Print
PrintDocument.PrintPage event example.
// +
using System.Drawing.Text;
private void OnPrintPage(object sender, PrintPageEventArgs e)
{
var w = e.MarginBounds.Width / cellCount;
var x = e.MarginBounds.X;
var y = e.MarginBounds.Y;
int h;
Rectangle rec;
using (var sf = new StringFormat())
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
// Maybe you need to set this? I see Arabic text in the images.
// sf.FormatFlags = StringFormatFlags.DirectionRightToLeft;
e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
// Uncomment to print the headers in the first page only.
//if (pageNumber == 0)
//{
h = dataGridView1.RowTemplate.Height;
foreach (var col in dataGridView1.Columns
.OfType<DataGridViewTextBoxColumn>()
.Where(c => c.Visible))
{
rec = new Rectangle(x, y, w, h);
e.Graphics.FillRectangle(Brushes.Gainsboro, rec);
e.Graphics.DrawString(
col.HeaderText,
col.DataGridView.Font,
Brushes.Black,
rec,
sf);
e.Graphics.DrawRectangle(Pens.Black, rec);
x += w;
}
x = e.MarginBounds.X;
y += h;
//}
for (var i = rowIndex; i < dataGridView1.RowCount; i++)
{
var row = dataGridView1.Rows[i];
if (row.IsNewRow) break;
h = GetRowHeight(e.Graphics, row, e.MarginBounds, sf);
if (h > e.MarginBounds.Height)
{
MessageBox.Show("Insufficient height.");
e.Cancel = true;
return;
}
foreach (var cell in row.Cells
.OfType<DataGridViewTextBoxCell>()
.Where(c => c.Visible))
{
rec = new Rectangle(x, y, w, h);
if (rec.Bottom > e.MarginBounds.Bottom)
{
pageNumber++;
rowIndex = i;
e.HasMorePages = true;
return;
}
e.Graphics.DrawString(
cell.FormattedValue.ToString(),
dataGridView1.Font,
Brushes.Black,
rec,
sf);
e.Graphics.DrawRectangle(Pens.Black, rec);
x += rec.Width;
}
x = e.MarginBounds.X;
y += h;
}
}
}
Output
Does anybody know a way to set the ComboBox's content's width to autosize
I do not mean the ComboBox itself, just the opened content.
You can't use it directly.
Do a trick
First iterate through all items of your combobox, check for the width of every items by assigning the text to a label. Then, check width every time, if width of current item gets greater than previous items then change the maximum width.
int DropDownWidth(ComboBox myCombo)
{
int maxWidth = 0;
int temp = 0;
Label label1 = new Label();
foreach (var obj in myCombo.Items)
{
label1.Text = obj.ToString();
temp = label1.PreferredWidth;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
label1.Dispose();
return maxWidth;
}
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.DropDownWidth = DropDownWidth(comboBox1);
}
OR
As suggested by stakx, you can use TextRenderer class
int DropDownWidth(ComboBox myCombo)
{
int maxWidth = 0, temp = 0;
foreach (var obj in myCombo.Items)
{
temp = TextRenderer.MeasureText(obj.ToString(), myCombo.Font).Width;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
return maxWidth;
}
obj.ToString() doesn't work for me, I suggest to use myCombo.GetItemText(obj). This works for me:
private int DropDownWidth(ComboBox myCombo)
{
int maxWidth = 0, temp = 0;
foreach (var obj in myCombo.Items)
{
temp = TextRenderer.MeasureText(myCombo.GetItemText(obj), myCombo.Font).Width;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
return maxWidth + SystemInformation.VerticalScrollBarWidth;
}
Here is very elegant solution. Just subscribe your combobox to this event handler:
private void AdjustWidthComboBox_DropDown(object sender, EventArgs e)
{
var senderComboBox = (ComboBox)sender;
int width = senderComboBox.DropDownWidth;
Graphics g = senderComboBox.CreateGraphics();
Font font = senderComboBox.Font;
int vertScrollBarWidth = (senderComboBox.Items.Count > senderComboBox.MaxDropDownItems)
? SystemInformation.VerticalScrollBarWidth : 0;
var itemsList = senderComboBox.Items.Cast<object>().Select(item => item.ToString());
foreach (string s in itemsList)
{
int newWidth = (int)g.MeasureString(s, font).Width + vertScrollBarWidth;
if (width < newWidth)
{
width = newWidth;
}
}
senderComboBox.DropDownWidth = width;
}
This code was taken from the codeproject: Adjust combo box drop down list width to longest string width.
But I have modified it to work with comboboxes filled with any data (not only strings).
Mostly the same code as in Javed Akram's second suggestion, but the width of the vertical scroll bar is added:
int setWidth_comboBox(ComboBox cb)
{
int maxWidth = 0, temp = 0;
foreach (string s in cb.Items)
{
temp = TextRenderer.MeasureText(s, cb.Font).Width;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
return maxWidth + SystemInformation.VerticalScrollBarWidth;
}
Use the code like this (on a form with a combobox with the name myComboBox):
myComboBox.Width = setWidth_comboBox(myComboBox);
Vote for algreat's answer below.
I simply modified algreat's answer with code resize the entire control.
I would have just added it as a comment but couldn't add formatted code on the comment.
private void combo_DropDown(object sender, EventArgs e)
{
//http://www.codeproject.com/Articles/5801/Adjust-combo-box-drop-down-list-width-to-longest-s
ComboBox senderComboBox = (ComboBox)sender;
int width = senderComboBox.DropDownWidth;
Graphics g = senderComboBox.CreateGraphics();
Font font = senderComboBox.Font;
int vertScrollBarWidth =
(senderComboBox.Items.Count > senderComboBox.MaxDropDownItems)
? SystemInformation.VerticalScrollBarWidth : 0;
int newWidth;
foreach (string s in ((ComboBox)sender).Items)
{
newWidth = (int)g.MeasureString(s, font).Width
+ vertScrollBarWidth;
if (width < newWidth)
{
width = newWidth;
}
if (senderComboBox.Width < newWidth)
{
senderComboBox.Width = newWidth+ SystemInformation.VerticalScrollBarWidth;
}
}
senderComboBox.DropDownWidth = width;
}
Old but classic, hope to work fast enough
private int GetDropDownWidth(ComboBox combo)
{
object[] items = new object[combo.Items.Count];
combo.Items.CopyTo(items, 0);
return items.Select(obj => TextRenderer.MeasureText(combo.GetItemText(obj), combo.Font).Width).Max();
}
Please see my solution below:
private int AutoSizeDropDownWidth(ComboBox comboBox)
{
var width = cmboxUnit.DropDownWidth;
var g = cmboxUnit.CreateGraphics();
var font = cmboxUnit.Font;
var verticalScrollBarWidth = cmboxUnit.Items.Count > cmboxUnit.MaxDropDownItems
? SystemInformation.VerticalScrollBarWidth : 0;
var itemsList = cmboxUnit.Items.Cast<object>().Select(item => item);
foreach (DataRowView dr in itemsList)
{
int newWidth = (int)g.MeasureString(dr["Name"].ToString(), font).Width + verticalScrollBarWidth;
if (width < newWidth)
{
width = newWidth;
}
}
return width;
}
This is an old question, but I just ran into it and combined a couple of the answers for my solution. I liked the simplicity of the accepted answer but wanted something that would work with any object type in the combo box. I also wanted to make use of the method through an extension method.
public static int AutoDropDownWidth(this ComboBox myCombo)
{
return AutoDropDownWidth<object>(myCombo, o => o.ToString());
}
public static int AutoDropDownWidth<T>(this ComboBox myCombo, Func<T, string> description)
{
int maxWidth = 1;
int temp = 1;
int vertScrollBarWidth = (myCombo.Items.Count > myCombo.MaxDropDownItems)
? SystemInformation.VerticalScrollBarWidth : 0;
foreach (T obj in myCombo.Items)
{
if (obj is T)
{
T t = (T)obj;
temp = TextRenderer.MeasureText(description(t), myCombo.Font).Width;
if (temp > maxWidth)
{
maxWidth = temp;
}
}
}
return maxWidth + vertScrollBarWidth;
}
This way if my class is:
public class Person
{
public string FullName {get;set;}
}
I could auto adjust the combo box drop down width like this:
cbPeople.DropDownWidth = cbPeople.AutoDropDownWidth<Person>(p => p.FullName);
TComboBox.ItemWidth is the property you seek. It has the behavior you want without any coding. Just set it at design time or programmatically to something bigger than Width, and when the user pulls down the box they will get a wider list of choices.
I have an application that is mostly operated through NotifyIcon's ContextMenuStrip
There are multiple levels of ToolStripMenuItems and the user can go through them.
The problem is, that when the user has two screen, the MenuItems jump to second screen when no space is available. like so:
How can I force them to stay on the same screen? I've tried to search through the web but couldn't find an appropriate answer.
Here is a sample piece of code i'm using to test this senario:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var resources = new ComponentResourceManager(typeof(Form1));
var notifyIcon1 = new NotifyIcon(components);
var contextMenuStrip1 = new ContextMenuStrip(components);
var level1ToolStripMenuItem = new ToolStripMenuItem("level 1 drop down");
var level2ToolStripMenuItem = new ToolStripMenuItem("level 2 drop down");
var level3ToolStripMenuItem = new ToolStripMenuItem("level 3 drop down");
notifyIcon1.ContextMenuStrip = contextMenuStrip1;
notifyIcon1.Icon = ((Icon)(resources.GetObject("notifyIcon1.Icon")));
notifyIcon1.Visible = true;
level2ToolStripMenuItem.DropDownItems.Add(level3ToolStripMenuItem);
level1ToolStripMenuItem.DropDownItems.Add(level2ToolStripMenuItem);
contextMenuStrip1.Items.Add(level1ToolStripMenuItem);
}
}
It is not easy, but you can write code in the DropDownOpening event to look at where the menu is at (its bounds), the current screen, and then set the DropDownDirection of the ToolStripMenuItem:
private void submenu_DropDownOpening(object sender, EventArgs e)
{
ToolStripMenuItem menuItem = sender as ToolStripMenuItem;
if (menuItem.HasDropDownItems == false)
{
return; // not a drop down item
}
// Current bounds of the current monitor
Rectangle Bounds = menuItem.GetCurrentParent().Bounds;
Screen CurrentScreen = Screen.FromPoint(Bounds.Location);
// Look how big our children are:
int MaxWidth = 0;
foreach (ToolStripMenuItem subitem in menuItem.DropDownItems)
{
MaxWidth = Math.Max(subitem.Width, MaxWidth);
}
MaxWidth += 10; // Add a little wiggle room
int FarRight = Bounds.Right + MaxWidth;
int CurrentMonitorRight = CurrentScreen.Bounds.Right;
if (FarRight > CurrentMonitorRight)
{
menuItem.DropDownDirection = ToolStripDropDownDirection.Left;
}
else
{
menuItem.DropDownDirection = ToolStripDropDownDirection.Right;
}
}
Also, make sure you have the DropDownOpening event hooked up (you would really need to add this to every menu item):
level1ToolStripMenuItem += submenu_DropDownOpening;
I have solved it this way:
For the ContextMenuStrip itself to open on a desired screen, I created a ContextMenuStripEx with the following methods:
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
Rectangle dropDownBounds = new Rectangle(x, y, width, height);
dropDownBounds = ConstrainToBounds(Screen.FromPoint(dropDownBounds.Location).Bounds, dropDownBounds);
base.SetBoundsCore(dropDownBounds.X, dropDownBounds.Y, dropDownBounds.Width, dropDownBounds.Height, specified);
}
internal static Rectangle ConstrainToBounds(Rectangle constrainingBounds, Rectangle bounds)
{
if (!constrainingBounds.Contains(bounds))
{
bounds.Size = new Size(Math.Min(constrainingBounds.Width - 2, bounds.Width), Math.Min(constrainingBounds.Height - 2, bounds.Height));
if (bounds.Right > constrainingBounds.Right)
{
bounds.X = constrainingBounds.Right - bounds.Width;
}
else if (bounds.Left < constrainingBounds.Left)
{
bounds.X = constrainingBounds.Left;
}
if (bounds.Bottom > constrainingBounds.Bottom)
{
bounds.Y = constrainingBounds.Bottom - 1 - bounds.Height;
}
else if (bounds.Top < constrainingBounds.Top)
{
bounds.Y = constrainingBounds.Top;
}
}
return bounds;
}
(ConstrainToBounds method is taken from the base class ToolStripDropDown via Reflector)
for the nested MenuItems to open on the same screen as ContextMenuStrip, I created a ToolStripMenuItemEx (which derives from ToolStripMenuItem). In my case it looks like this:
private ToolStripDropDownDirection? originalToolStripDropDownDirection;
protected override void OnDropDownShow(EventArgs e)
{
base.OnDropDownShow(e);
if (!Screen.FromControl(this.Owner).Equals(Screen.FromPoint(this.DropDownLocation)))
{
if (!originalToolStripDropDownDirection.HasValue)
originalToolStripDropDownDirection = this.DropDownDirection;
this.DropDownDirection = originalToolStripDropDownDirection.Value == ToolStripDropDownDirection.Left ? ToolStripDropDownDirection.Right : ToolStripDropDownDirection.Left;
}
}
The code of #David does not fix if the menu is opened in the left side of second screen. I have improved that code to work on all screen corner.
private void subMenu_DropDownOpening(object sender, EventArgs e)
{
ToolStripMenuItem mnuItem = sender as ToolStripMenuItem;
if (mnuItem.HasDropDownItems == false)
{
return; // not a drop down item
}
//get position of current menu item
var pos = new Point(mnuItem.GetCurrentParent().Left, mnuItem.GetCurrentParent().Top);
// Current bounds of the current monitor
Rectangle bounds = Screen.GetWorkingArea(pos);
Screen currentScreen = Screen.FromPoint(pos);
// Find the width of sub-menu
int maxWidth = 0;
foreach (var subItem in mnuItem.DropDownItems)
{
if (subItem.GetType() == typeof(ToolStripMenuItem))
{
var mnu = (ToolStripMenuItem) subItem;
maxWidth = Math.Max(mnu.Width, maxWidth);
}
}
maxWidth += 10; // Add a little wiggle room
int farRight = pos.X + mnuMain.Width + maxWidth;
int farLeft = pos.X - maxWidth;
//get left and right distance to compare
int leftGap = farLeft - currentScreen.Bounds.Left;
int rightGap = currentScreen.Bounds.Right - farRight;
if (leftGap >= rightGap)
{
mnuItem.DropDownDirection = ToolStripDropDownDirection.Left;
}
else
{
mnuItem.DropDownDirection = ToolStripDropDownDirection.Right;
}
}
I did not try the solution by tombam. But since the others didn't seem to work, I came up with this simple solution:
private void MenuDropDownOpening(object sender, EventArgs e)
{
var menuItem = sender as ToolStripDropDownButton;
if (menuItem == null || menuItem.HasDropDownItems == false)
return; // not a drop down item
// Current bounds of the current monitor
var upperRightCornerOfMenuInScreenCoordinates = menuItem.GetCurrentParent().PointToScreen(new Point(menuItem.Bounds.Right, menuItem.Bounds.Top));
var currentScreen = Screen.FromPoint(upperRightCornerOfMenuInScreenCoordinates);
// Get width of widest child item (skip separators!)
var maxWidth = menuItem.DropDownItems.OfType<ToolStripMenuItem>().Select(m => m.Width).Max();
var farRight = upperRightCornerOfMenuInScreenCoordinates.X + maxWidth;
var currentMonitorRight = currentScreen.Bounds.Right;
menuItem.DropDownDirection = farRight > currentMonitorRight ? ToolStripDropDownDirection.Left :
ToolStripDropDownDirection.Right;
}
Note that in my world, I was not concerned about multiple levels of cascading menus (as in the OP), so I did not test my solution in that scenario. But this works correctly for a single ToolStripDropDownButton on a ToolStrip.
I had been experimenting on writing a code to generate images inside a FlowLayoutPanel.
This is what i had done so far, when i click on a button for the first time (by using a checkboxes - read in number of images to open), it will generate the images, when i click on the button on second try, it will not update the flowlayoutpanel.
Even though i tried to remove the controls inside the FlowLayoutPanel, it still doesn't show the second try of the images.
This is the code snippet of the method:
FlowLayoutPanel fwPanel = null;
private void btnOpenFile_Click(object sender, EventArgs e)
{
//if there is content inside the flowpanel, dump it
if (fwPanel != null)
{
listOfFile.Clear();
}
//create a new FLP
fwPanel = new FlowLayoutPanel();
int panelWidth = width * 4 + 50;
int panelHeight = height * 4 + 50;
fwPanel.Size = new Size(panelWidth, panelHeight);
fwPanel.Location = new Point(0, 0);
this.Controls.Add(fwPanel);
//each checked item would be stored into an arraylist
foreach(object itemChecked in clbFile.CheckedItems)
{
listOfFile.Add((clbFile.Items.IndexOf(itemChecked)+1).ToString());
}
int noOfCheckedFile = listOfFile.Count;
PictureBox[] listOfPicture = new PictureBox[noOfCheckedFile];
int positionX = 0, positionY = 0;
int maxPaddingX = (width * MATRIX_SIZE) - 1;
int maxPaddingY = (height * MATRIX_SIZE) - 1;
//dynamically create images.
for (int i = 0; i < noOfCheckedFile; i++)
{
listOfPicture[i] = new PictureBox();
listOfPicture[i].Image = resizeImage((Image)show_picture(Convert.ToInt32(listOfFile[i])), new Size(width, height));
listOfPicture[i].Size = new Size(width, height);
if (positionX > maxPaddingX)
{
positionX = 0;
positionY += height;
}
if (positionY > maxPaddingY)
{
positionY = 0;
}
listOfPicture[i].Location = new Point(positionX,positionY);
listOfPicture[i].Visible = true;
fwPanel.Controls.Add(listOfPicture[i]);
positionX += width;
}
}
show_picture is a method that takes in and integer and returns a bitmap image.
listOfFile is to trace which file to return.
listOfPicture is to store each images.
i tried replacing this lines
//if there is content inside the flowpanel, dump it
if (fwPanel != null)
{
listOfFile.Clear();
}
i have added this line into it, when i do a second click, everything just gone missing, but this is not what i want, because it does not re-populating the FlowLayoutPanel.
if (fwPanel != null)
{
fwPanel.SuspendLayout();
if (fwPanel.Controls.Count > 0)
{
for (int i = (fwPanel.Controls.Count - 1); i >= 0; i--)
{
Control c = fwPanel.Controls[i];
c.Dispose();
}
GC.Collect();
}
fwPanel.ResumeLayout();
listOfFile.Clear();
}
I also tried this, but on second click, nothing will happen.
if (fwPanel != null)
{
List<Control> listControls = fwPanel.Controls.Cast<Control>().ToList();
foreach (Control control in listControls)
{
fwPanel.Controls.Remove(control);
control.Dispose();
}
listOfFile.Clear();
}
I wonder if i miss out anything, can someone enlighten me on what did i miss out ? Or guide me for the best practice of doing this.
as Suggested, i shifted the creation outside (credit to Sinatr for spotting it)
FlowLayoutPanel fwPanel = new FlowLayoutPanel();
private void createFLP()
{
int panelWidth = width * 4 + 50;
int panelHeight = height * 4 + 50;
fwPanel.Size = new Size(panelWidth, panelHeight);
fwPanel.Location = new Point(0, 0);
this.Controls.Add(fwPanel);
}
that solves the nothing happen part. Followed by using this to remove controls
if (fwPanel != null)
{
List<Control> listControls = fwPanel.Controls.Cast<Control>().ToList();
foreach (Control control in listControls)
{
fwPanel.Controls.Remove(control);
control.Dispose();
}
listOfFile.Clear();
}
and everything works like a charm, hope that this answer will be able to help others who are facing the same problem too.
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.