I want to show tooltip on ListBox, so in load form I have:
lstTech.DrawMode = DrawMode.OwnerDrawFixed;
var techListQuery = $"exec getTaskAssignableEmployeeList";
var techList = db.GetTableBySQL(techListQuery);
lstTech.DataSource = techList.ToList();
lstTech.DisplayMember = "Abbreviation";
lstTech.ValueMember = "EmpGuid";
Then in DrawItem method I have:
private void lstTech_DrawItem(object sender, DrawItemEventArgs e)
{
ListBox senderListBox = (ListBox)sender;
if (e.Index < 0)
{
return;
}
var item = ((DataRowView)senderListBox.Items[e.Index]);
var abbr = senderListBox.GetItemText(item["Abbreviation"]);
var name = senderListBox.GetItemText(item["Name"]);
e.DrawBackground();
using (SolidBrush br = new SolidBrush(e.ForeColor))
{
e.Graphics.DrawString(abbr, e.Font, br, e.Bounds);
}
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected )
{
ttTechs.Show(name, senderListBox, e.Bounds.Right, senderListBox.PointToClient(Cursor.Position).Y);
}
e.DrawFocusRectangle();
}
Then I want to disappear Tooltip on mouse leave like:
private void lstTech_MouseLeave(object sender, EventArgs e)
{
ListBox senderListBox = (ListBox)sender;
ttTechs.Hide(senderListBox);
}
So now Tooltip works, it appear when I clic on some item of list, problem is I don't want it on clic, I want it in Hover event. So I try:
private void lstTech_MouseHover(object sender, EventArgs e)
{
ListBox senderListBox = (ListBox)sender;
ttTechs.Show(senderListBox);
}
But it returns me an error:
No overload for method 'Show' takes 1 arguments
What am I doing wrong? what I need to display tooltip on hover event and remove from click event? Regards
Update
As answer below I change code to:
//Set tooltip as Global variable
ToolTip toolTip = new ToolTip();
//Create new method to assign Draw
private void SetToolTipText()
{
var content = string.Empty;
foreach (DataRowView list in lstTech.SelectedItems)
{
var techName = list[1].ToString();
content += techName + Environment.NewLine;
}
toolTip.SetToolTip(lstTech, content);
}
//Draw method
private void lstTech_DrawItem(object sender, DrawItemEventArgs e)
{
SetToolTipText();
}
Problem is list items are invisible, if I click in listbox Tooltip appears correctly but list is not displayed. If I remove lstTech.DrawMode = DrawMode.OwnerDrawVariable; from load_form listbox items appears again but tooltip stop working. What is going on there?
There's a ToolTip control in WinForms which you can make use of.
Let's say your list box contains a bunch of names, and I want to only display the first 3 letters of each name on the tool tip. So I would write a function to go through each item in the list box and filter the names and create a string, then set it to the tool tip.
private void SetToolTipText()
{
var content = string.Empty;
foreach (var item in listBox1.Items)
{
var first3 = item.ToString().Substring(0, 3);
content += first3 + Environment.NewLine;
}
toolTip.SetToolTip(listBox1, content);
}
Note that the toolTip is a global variable in my Form.
public partial class MainForm : Form
{
ToolTip toolTip = new ToolTip();
public MainForm()
{
InitializeComponent();
}
Next I would call that function in my DrawItem() event like so:
private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
SetToolTipText();
}
In your program, instead of taking the first 3 characters of the string, do whatever the filtering you do.
Related
I'm having problems with an owner drawn listbox in a windows forms application.
The listbox is filled with objects containing their own UserControl. The user control of each itemis shown in the listbox.
This all works but when I scroll up or down the UserControls appear shifted a bit.
Once I click them, they jump to the right position.
In the picture you can see the white UserControls shifted a bit to the right and a bit down.
This is how they look before scrolling.
The list is filled with objects of this type:
class Class1
{
public UserControl1 UC;
public string Text;
public Class1(UserControl1 uc, string text)
{
UC = uc;
Text = text;
}
}
This is the class that controls the list:
class ListDrawer
{
public ListBox LB;
public int HeaderHeight = 25;
public ListDrawer(ListBox lb)
{
LB = lb;
LB.DrawMode = DrawMode.OwnerDrawVariable;
LB.DrawItem += LB_DrawItem;
LB.MeasureItem += LB_MeasureItem;
}
private void LB_MeasureItem(object sender, MeasureItemEventArgs e)
{
ListBox lst = sender as ListBox;
Class1 c = (Class1)lst.Items[e.Index];
e.ItemHeight = HeaderHeight;
e.ItemHeight = e.ItemHeight + c.UC.Height;
}
private void LB_DrawItem(object sender, DrawItemEventArgs e)
{
ListBox lst = sender as ListBox;
Class1 c = (Class1)lst.Items[e.Index];
e.DrawBackground();
e.Graphics.FillRectangle(Brushes.DarkSeaGreen, e.Bounds);
e.Graphics.DrawString(c.Text, LB.Font, SystemBrushes.HighlightText, e.Bounds.Left, e.Bounds.Top);
if (!lst.Controls.Contains(c.UC))
{
lst.Controls.Add(c.UC);
}
c.UC.Top = e.Bounds.Top + HeaderHeight;
}
}
The list is filled on a button click:
private void button1_Click(object sender, EventArgs e)
{
UserControl1 uc = new UserControl1();
Class1 c = new Class1(uc, "text 1");
ListDrawer LD = new ListDrawer(listBox1);
listBox1.Items.Add(c);
uc = new UserControl1();
c = new Class1(uc, "text 2");
listBox1.Items.Add(c);
}
Hope this can be fixed....
Cheers,
Robert.
In the user control override the onMove event:
protected override void OnMove( EventArgs e ) {
base.OnMove( e );
this.Parent.Invalidate();
}
It will probably flicker but will solve your problem
I have a form on which are "listBox1" and "button1". I have two function. The second function adds checkboxes to listbox1 and the first function displays message box. But I don´t know how to write the first function.
Here I want to check which checkbox is checked and write a message:
private void button1_click(object sender, EventArgs e)
{
MessageBox.Show("radiobutton: " + rb[i].Text);
}
Here I create checkboxes: //it´s working
internal void loadSurveys()
{
WebClient client2 = new WebClient();
var json = client2.DownloadString("http://www.test.net/api/surveys/?api_key=123");
JObject data = JObject.Parse(json);
var example = JsonConvert.DeserializeObject<Example>(json);
int y = 5;
int i = 0;
RadioButton[] rb = new RadioButton[example.surveys.Length];
String chkBox_name = "";
String chkBox_text = "";
foreach (var survey in data["surveys"].Children())
{
rb[i] = new RadioButton();
rb[i].Location = new Point(5, y);
rb[i].Name = chkBox_name + survey["id"];
rb[i].Text = chkBox_text + survey["title"];
rb[i].AutoSize = true;
this.listBox1.Controls.Add(rb[i]);
y += 20;
i++;
}
}
You can go through listBox1.Controls and pick checked RadioButton
private void button1_click(object sender, EventArgs e)
{
var rb = this.listBox1.Controls.OfType<RadioButton>().SingleOrDefault(n => n.Checked);
if (rb != null)
MessageBox.Show("radiobutton: " + rb.Text);
}
since this is RadioButton there shouldn't be more then one checked
The first step, is to make the radiobutton array a variable on form level:
RadioButton[] rb
which is assigned inside loadSurveys
rb = new RadioButton[example.surveys.Length];
Then the array is accessible inside your button click
var rb = rb.FirstOrDefault(r=>r.Checked);
if(rb==null)
MessageBox.Show("No radiobutton was selected");
else
MessageBox.Show("radiobutton: " + rb[i].Text);
edit Just noticed you add the radiobuttons to a listbox. Is the listbox1 variable an actual listbox? The above will still work, but if the goal is to display a listbox of radiobuttons, you can custom paint the listbox and else use a normal panel instead of a listbox.
Either way, you can also do a firstordefault on the controls on the controls of the listbox1 variable (with OfType), but if you'd use a listbox, and fill its items, you could simply use SelectedIndexChanged
edit 2
Since I already had it, wanted to show a way to make your listbox a radiobutton box. You can make any existing listbox a radiobutton box with the following class:
public class RadioButtonBoxPainter:IDisposable
{
public readonly ListBox ListBox;
public RadioButtonBoxPainter(ListBox ListBox)
{
this.ListBox = ListBox;
ListBox.DrawMode = DrawMode.OwnerDrawFixed;
ListBox.DrawItem += ListBox_DrawItem;
}
void ListBox_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index == -1) return;
Rectangle r = e.Bounds;
r.Width=r.Height;
bool selected= (e.State & DrawItemState.Selected) > 0;
e.DrawBackground();
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
ControlPaint.DrawRadioButton(e.Graphics, r, selected ? ButtonState.Checked : ButtonState.Normal);
r.X = r.Right + 2;
r.Width = e.Bounds.Width - r.X;
string txt;
if (ListBox.Site != null && ListBox.Site.DesignMode && e.Index >= ListBox.Items.Count)
txt = ListBox.Name;
else
txt = ListBox.GetItemText(ListBox.Items[e.Index]);
using (var b = new SolidBrush(e.ForeColor))
e.Graphics.DrawString(txt, e.Font, b, r);
if (selected)
{
r = e.Bounds;
r.Width--; r.Height--;
e.Graphics.DrawRectangle(Pens.DarkBlue, r);
}
}
public void Dispose()
{
ListBox.DrawItem -= ListBox_DrawItem;
}
}
Example of a standard implementation:
public class RadioButtonBox:ListBox
{
public readonly RadioButtonBoxPainter Painter;
public RadioButtonBox()
{
Painter = new RadioButtonBoxPainter(this);
}
[DefaultValue(DrawMode.OwnerDrawFixed)]
public override DrawMode DrawMode
{
get{return base.DrawMode;}
set{base.DrawMode = value;}
}
}
The RadioButtonBox is a control I actually use a lot. Personally I find it a lot quicker in implementing then a load of separate radiobuttons.
In case you want to use it, and want an example how to implement it in your current code, leave a comment and I'll add one.
Problem
I have two ListView's. One has options that are to be dragged into the other. This is the "fields" ListView. The other one is the "builder" ListView. I cannot figure out a way to visually show the user where the item will be inserted. I would like to draw a line in-between the ListViewItem's to visually aid the user.
private void builder_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void fields_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void fields_ItemDrag(object sender, ItemDragEventArgs e)
{
fromBuilder = false;
fields.DoDragDrop(e.Item, DragDropEffects.Move);
}
private void builder_ItemDrag(object sender, ItemDragEventArgs e)
{
fromBuilder = true;
builder.DoDragDrop(e.Item, DragDropEffects.Move);
}
private void builderAndFields_DragDrop(object sender, DragEventArgs e)
{
ListViewItem i = new ListViewItem();
i = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
// Since this function works for both the builder and the fields,
// we have to check to see where we are dropping, the sender
// is the ListView we are dropping onto
if (sender.Equals(builder))
{
ListViewItem c = new ListViewItem();
c = (ListViewItem)i.Clone();
Point cp = builder.PointToClient(new Point(e.X, e.Y));
Console.WriteLine("cp: " + cp);
ListViewItem dragToItem = builder.GetItemAt(cp.X, cp.Y);
Console.WriteLine("dragToItem: " + dragToItem);
int dropIndex = dragToItem.Index;
// Now, we have to check to see if we are reordering or adding
// So, we check the flag to see if the dragDrop was initiated
// on the builder or on the fields ListView
if (fromBuilder)
{
builder.Items.Insert(dropIndex, c);
builder.Items.Remove(i);
}
else
{
Console.WriteLine(dropIndex);
builder.Items.Insert(dropIndex, c);
}
}
// If the sender is the fields listView, the user is trying to remove
// the item from the builder.
else
{
builder.Items.Remove(i);
}
}
Take a look at PreviewDragEnter, PreviewDragOver, and PreviewDragLeave.
You can use that event to add an Adorner to your drop list. IF you search for "WPF DragDropHelper" you will find several detailed examples.
Better ListView supports drop highlighting and insertion marks out of the box:
I think you can also do this in a regular ListView using HitTest() method within DragOver event handler.
I was wondering how could I do this. I know I can use the button component but it has the little gray stuff around it when I give it a image. With image button how could I show another image for the hover effect
You want to create a button with no border but displays different images when the user hovers over it with the mouse? Here's how you can do it:
Add an ImageList control to your form at add two images, one for the button's normal appearance and one for when the mouse is hovering over.
Add your button and set the following properties:
FlatStyle = Flat
FlatAppearance.BorderColor (and maybe MouseOverBackColor & MouseDownBackColor) to your form's background color
ImageList = the ImageList you added to the form
ImageIndex to the index value of your normal image
Code the MouseHover and MouseLeave events for the button like this:
// ImageList index value for the hover image.
private void button1_MouseHover(object sender, EventArgs e) => button1.ImageIndex = 1;
// ImageList index value for the normal image.
private void button1_MouseLeave(object sender, EventArgs e) => button1.ImageIndex = 0;
I believe that will give you the visual effect you're looking for.
Small summary (Border, MouseDownBackColor, MouseOverBackColor)
FlatApperance
BorderColor = Black or what ever you want
BorderSize = can be set to 0
MouseDownBackColor = Transparent
MouseOverBackColor = Transparent
Text = none
For MouseDown:
// ImageList index value for the mouse down image.
private void button1_MouseDown(object sender, MouseEventArgs e) => button1.ImageIndex = 2;
You can assign the BackgroundImage property for the button. You can also use the OnMouseEnter and OnMouseExit events to change the background as per your request.
See BackgroundImage OnMouseEnter OnMouseLeave
I also needed an image button, but I wanted one like the ToolstripMenuButton.
With the correct borders and colors on hover.
So I made a custom control to do just that:
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace LastenBoekInfrastructure.Controls.Controls
{
[DefaultEvent("Click")]
public class ImageButton : UserControl
{
public string ToolTipText
{
get { return _bButton.ToolTipText; }
set { _bButton.ToolTipText = value; }
}
public bool CheckOnClick
{
get { return _bButton.CheckOnClick; }
set { _bButton.CheckOnClick = value; }
}
public bool DoubleClickEnabled
{
get { return _bButton.DoubleClickEnabled; }
set { _bButton.DoubleClickEnabled = value; }
}
public System.Drawing.Image Image
{
get { return _bButton.Image; }
set { _bButton.Image = value; }
}
public new event EventHandler Click;
public new event EventHandler DoubleClick;
private ToolStrip _tsMain;
private ToolStripButton _bButton;
public ImageButton()
{
InitializeComponent();
}
private void InitializeComponent()
{
var resources = new ComponentResourceManager(typeof(ImageButton));
_tsMain = new ToolStrip();
_bButton = new ToolStripButton();
_tsMain.SuspendLayout();
SuspendLayout();
//
// tsMain
//
_tsMain.BackColor = System.Drawing.Color.Transparent;
_tsMain.CanOverflow = false;
_tsMain.Dock = DockStyle.Fill;
_tsMain.GripMargin = new Padding(0);
_tsMain.GripStyle = ToolStripGripStyle.Hidden;
_tsMain.Items.AddRange(new ToolStripItem[] {
_bButton});
_tsMain.Location = new System.Drawing.Point(0, 0);
_tsMain.Name = "_tsMain";
_tsMain.Size = new System.Drawing.Size(25, 25);
_tsMain.TabIndex = 0;
_tsMain.Renderer = new ImageButtonToolStripSystemRenderer();
//
// bButton
//
_bButton.DisplayStyle = ToolStripItemDisplayStyle.Image;
_bButton.Image = ((System.Drawing.Image)(resources.GetObject("_bButton.Image")));
_bButton.ImageTransparentColor = System.Drawing.Color.Magenta;
_bButton.Name = "_bButton";
_bButton.Size = new System.Drawing.Size(23, 22);
_bButton.Click += bButton_Click;
_bButton.DoubleClick += bButton_DoubleClick;
//
// ImageButton
//
Controls.Add(_tsMain);
Name = "ImageButton";
Size = new System.Drawing.Size(25, 25);
_tsMain.ResumeLayout(false);
_tsMain.PerformLayout();
ResumeLayout(false);
PerformLayout();
}
void bButton_Click(object sender, EventArgs e)
{
if (Click != null)
{
Click(this, e);
}
}
void bButton_DoubleClick(object sender, EventArgs e)
{
if(DoubleClick != null)
{
DoubleClick(this, e);
}
}
public class ImageButtonToolStripSystemRenderer : ToolStripSystemRenderer
{
protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
{
//base.OnRenderToolStripBorder(e);
}
}
}
}
How do you edit items and subitems in a listview? Let's say I have a listview with 3 columns,and subitems,
Car Brand | Car Name | Car Year
Ford | Mustang | 2000
Dodge | Charger | 2007
How would I Add items like that to listview and how would I edit let's say the Car Name on which ever row by index[] if I needed to edit at runtime at firing of an event?
If you're looking for "in-place" editing of a ListView's contents (specifically the subitems of a ListView in details view mode), you'll need to implement this yourself, or use a third-party control.
By default, the best you can achieve with a "standard" ListView is to set it's LabelEdit property to true to allow the user to edit the text of the first column of the ListView (assuming you want to allow a free-format text edit).
Some examples (including full source-code) of customized ListView's that allow "in-place" editing of sub-items are:
C# Editable ListView
In-place editing of ListView subitems
I use a hidden textbox to edit all the listview items/subitems. The only problem is that the textbox needs to disappear as soon as any event takes place outside the textbox and the listview doesn't trigger the scroll event so if you scroll the listview the textbox will still be visible.
To bypass this problem I created the Scroll event with this overrided listview.
Here is my code, I constantly reuse it so it might be help for someone:
ListViewItem.ListViewSubItem SelectedLSI;
private void listView2_MouseUp(object sender, MouseEventArgs e)
{
ListViewHitTestInfo i = listView2.HitTest(e.X, e.Y);
SelectedLSI = i.SubItem;
if (SelectedLSI == null)
return;
int border = 0;
switch (listView2.BorderStyle)
{
case BorderStyle.FixedSingle:
border = 1;
break;
case BorderStyle.Fixed3D:
border = 2;
break;
}
int CellWidth = SelectedLSI.Bounds.Width;
int CellHeight = SelectedLSI.Bounds.Height;
int CellLeft = border + listView2.Left + i.SubItem.Bounds.Left;
int CellTop =listView2.Top + i.SubItem.Bounds.Top;
// First Column
if (i.SubItem == i.Item.SubItems[0])
CellWidth = listView2.Columns[0].Width;
TxtEdit.Location = new Point(CellLeft, CellTop);
TxtEdit.Size = new Size(CellWidth, CellHeight);
TxtEdit.Visible = true;
TxtEdit.BringToFront();
TxtEdit.Text = i.SubItem.Text;
TxtEdit.Select();
TxtEdit.SelectAll();
}
private void listView2_MouseDown(object sender, MouseEventArgs e)
{
HideTextEditor();
}
private void listView2_Scroll(object sender, EventArgs e)
{
HideTextEditor();
}
private void TxtEdit_Leave(object sender, EventArgs e)
{
HideTextEditor();
}
private void TxtEdit_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
HideTextEditor();
}
private void HideTextEditor()
{
TxtEdit.Visible = false;
if (SelectedLSI != null)
SelectedLSI.Text = TxtEdit.Text;
SelectedLSI = null;
TxtEdit.Text = "";
}
Click the items in the list view.
Add a button that will edit the selected items.
Add the code
try
{
LSTDEDUCTION.SelectedItems[0].SubItems[1].Text = txtcarName.Text;
LSTDEDUCTION.SelectedItems[0].SubItems[0].Text = txtcarBrand.Text;
LSTDEDUCTION.SelectedItems[0].SubItems[2].Text = txtCarName.Text;
}
catch{}
Sorry, don't have enough rep, or would have commented on CraigTP's answer.
I found the solution from the 1st link - C# Editable ListView, quite easy to use. The general idea is to:
identify the SubItem that was selected and overlay a TextBox with the SubItem's text over the SubItem
give this TextBox focus
change SubItem's text to that of TextBox's when TextBox loses focus
What a workaround for a seemingly simple operation :-|
private void listView1_MouseDown(object sender, MouseEventArgs e)
{
li = listView1.GetItemAt(e.X, e.Y);
X = e.X;
Y = e.Y;
}
private void listView1_MouseUp(object sender, MouseEventArgs e)
{
int nStart = X;
int spos = 0;
int epos = listView1.Columns[1].Width;
for (int i = 0; i < listView1.Columns.Count; i++)
{
if (nStart > spos && nStart < epos)
{
subItemSelected = i;
break;
}
spos = epos;
epos += listView1.Columns[i].Width;
}
li.SubItems[subItemSelected].Text = "9";
}