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
Related
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.
I have added splitcontainer in a form for my application. Basically, I have multiple buttons in left panel, each having drop down menu items added dynamically. When an item is clicked, it brings a common form with updated values(in labels) in right panel. It works fine for the first time when a menu item is clicked.But in the next click of another menu item, brings out the old form details which should be giving a form with updated label values.The issue I am having is that the form is not updating label values. I couldn't find much about this issue online.Any help would be much appreciated.
Splitcontainer form;
public int lightIndex
{
get { return _lightindex; }
set { _lightindex = value; }
}
public int groupIndex
{
get { return _groupindex; }
set { _groupindex = value; }
}
Button click event
private void button_Click(object sender, EventArgs e)
{
Button btnSender = (Button)sender;
groupIndex = groupbuttons.IndexOf((Button)sender);
ContextMenu cm = new ContextMenu();
List<UserLight> alllight = Global.g_userlightgroups[_groupindex].getUserlights();
for (int i = 0; i < alllight.Count(); i++)
{
ContextMenu = cm;
MenuItem item = new MenuItem(alllight[i].getlightType().ToString());
item.Click += Item_Click;
cm.MenuItems.Add(item);
}
Point ptLowerLeft = new Point(0, btnSender.Height);
ptLowerLeft = btnSender.PointToScreen(ptLowerLeft);
cm.Show(btnSender, ptLowerLeft);
}
private void Item_Click(object sender, EventArgs e )
{
MenuItem menuItem = (MenuItem)sender;
String lighttype = menuItem.Text;
if (lighttype.Equals("PRIMITIVE"))
{
PrimitiveLight prim = new PrimitiveLight();
prim.TopLevel = false;
prim.Parent = lightcontainer.Panel2;
prim.groupIndex = _groupindex;
prim.lightIndex = menuItem.Index;
prim.Show();
prim.Dock = DockStyle.Fill;
}
}
PrimitiveLight form: This form will be inserted into splitcontainer panel2 when menu item is clicked.
private int _lightindex;
private int _groupindex;
public int lightIndex
{
get { return _lightindex; }
set { _lightindex = value; }
}
public int groupIndex
{
get { return _groupindex; }
set { _groupindex = value; }
}
private void PrimitiveLight_Load(object sender, EventArgs e)
{
lightDefname.Text = Global.g_userlightgroups[_groupindex].getUserlights().ElementAt(_lightindex).getdefName().ToString();
lightCustname.Text = Global.g_userlightgroups[_groupindex].getUserlights().ElementAt(_lightindex).getcustName().ToString();
}
panel2.Refresh() Maybe, dont know your issue but you can refresh the panel.
I have a custom control containing a button with a label underneath. I want to make these controls to be draggable over another one to arrange them as I want in a flowlayoutpanel.
Now is working only if I drag the control from it's background (marked with yellow in the picture bellow) to the other control's yellow marked area, but not if i drag from the button or label area..
How can I make it so I can move the custom control no matter from where I grab and drop it on the other control. Basically to be only one control not like a container for the button and label..
This is my code so far:
private void flowLayoutPanel1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void flowLayoutPanel1_DragDrop(object sender, DragEventArgs e)
{
CustomControl target = sender as CustomControl;
if (target != null)
{
int targetIndex = FindCSTIndex(target);
if (targetIndex != -1)
{
string pictureBoxFormat = typeof(CustomControl).FullName;
if (e.Data.GetDataPresent(pictureBoxFormat))
{
CustomControl source = e.Data.GetData(pictureBoxFormat) as CustomControl;
int sourceIndex = this.FindCSTIndex(source);
if (targetIndex != -1)
this.flowLayoutPanel1.Controls.SetChildIndex(source, targetIndex);
}
}
}
}
private int FindCSTIndex(CustomControl cst_ctr)
{
for (int i = 0; i < this.flowLayoutPanel1.Controls.Count; i++)
{
CustomControl target = this.flowLayoutPanel1.Controls[i] as CustomControl;
if (cst_ctr == target)
return i;
}
return -1;
}
private void OnCstMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
CustomControl cst = sender as CustomControl;
cst.DoDragDrop(cst, DragDropEffects.Move);
}
}
And the custom control class:
public class CustomControl : Control
{
private Button _button;
private Label _label;
public CustomControl(Button button, Label label)
{
_button = button;
_label = label;
button.Width = 50;
button.Height = 50;
label.Width = 65;
button.BackgroundImageLayout = ImageLayout.Stretch;
Height = button.Height + label.Height;
Width = 68;
// Width = Math.Max(button.Width, label.Width);
Controls.Add(_button);
_button.Location = new Point(0, 0);
Controls.Add(_label);
_label.Location = new Point(0, button.Height);
}
}
Use MouseDown instead of MouseMove to initiate drag-and-drop (MSDN). You can initiate drag-and-drop in the control code itself (assuming what all CustomControls will be drag-and-drop-able), otherwise you may want to create public method to sign childs (exposing childs is bad idea, unless you already use them outside).
public class CustomControl : Control
{
...
public CustomControl(Button button, Label label)
{
...
_button.MouseDown += OnMouseDown;
_label.MouseDown += OnMouseDown;
}
override void OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
(sender as Control).DoDragDrop(this, DragDropEffects.Move);
}
}
Untested, but should give you idea.
Managed to solve it:
private void flowLayoutPanel1_DragDrop(object sender, DragEventArgs e)
{
Control target = new Control();
target.Parent = sender as Control;
if (target != null)
{
int targetIndex = FindCSTIndex(target.Parent);
if (targetIndex != -1)
{
string cst_ctrl = typeof(CustomControl).FullName;
if (e.Data.GetDataPresent(cst_ctrl))
{
Button source = new Button();
source.Parent = e.Data.GetData(cst_ctrl) as CustomControl;
if (targetIndex != -1)
this.flowLayoutPanel1.Controls.SetChildIndex(source.Parent, targetIndex);
}
}
}
}
private int FindCSTIndex(Control cst_ctr)
{
for (int i = 0; i < this.flowLayoutPanel1.Controls.Count; i++)
{
CustomControl target = this.flowLayoutPanel1.Controls[i] as CustomControl;
if (cst_ctr.Parent == target)
return i;
}
return -1;
}
private void OnCstMouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Control cst = sender as Control;
cst.DoDragDrop(cst.Parent, DragDropEffects.Move);
}
}
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);
}
}
}
}
I have a winform with a listbox and a treeview.
Once my listbox is filled with items, I want to drag them (multiple or single) from the listbox and drop them in a node in the treeview.
If somebody has a good example in C# that would be great.
It's been a while since I've messed with Drag/Drop so I figured I'll write a quick sample.
Basically, I have a form, with a listbox on the left, and a treeview on the right. Then I put a button on top. When the button is clicked, it just puts the date of the next ten days into the list box. It also populates the TreeView with 2 parents nodes and two child nodes. Then, you just have to handle all the subsequent drag/drop events to make it work.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.treeView1.AllowDrop = true;
this.listBox1.AllowDrop = true;
this.listBox1.MouseDown += new MouseEventHandler(listBox1_MouseDown);
this.listBox1.DragOver += new DragEventHandler(listBox1_DragOver);
this.treeView1.DragEnter += new DragEventHandler(treeView1_DragEnter);
this.treeView1.DragDrop += new DragEventHandler(treeView1_DragDrop);
}
private void button1_Click(object sender, EventArgs e)
{
this.PopulateListBox();
this.PopulateTreeView();
}
private void PopulateListBox()
{
for (int i = 0; i <= 10; i++)
{
this.listBox1.Items.Add(DateTime.Now.AddDays(i));
}
}
private void PopulateTreeView()
{
for (int i = 1; i <= 2; i++)
{
TreeNode node = new TreeNode("Node" + i);
for (int j = 1; j <= 2; j++)
{
node.Nodes.Add("SubNode" + j);
}
this.treeView1.Nodes.Add(node);
}
}
private void treeView1_DragDrop(object sender, DragEventArgs e)
{
TreeNode nodeToDropIn = this.treeView1.GetNodeAt(this.treeView1.PointToClient(new Point(e.X, e.Y)));
if (nodeToDropIn == null) { return; }
if(nodeToDropIn.Level > 0)
{
nodeToDropIn = nodeToDropIn.Parent;
}
object data = e.Data.GetData(typeof(DateTime));
if (data == null) { return; }
nodeToDropIn.Nodes.Add(data.ToString());
this.listBox1.Items.Remove(data);
}
private void listBox1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
this.listBox1.DoDragDrop(this.listBox1.SelectedItem, DragDropEffects.Move);
}
}
You want to use the GetItemAt(Point point) function to translate X,Y location to the listview item.
Here's quite good article about it: Drag and Drop Using C#.
To make the item being dragged visible while dragging, you need to use COM ImageList, which is well described in the following article Custom Drag-Drop Images Using ImageLists.