I'm trying to implement a 'preview' scenario when the user hover a menu item.
For example, lets say a program has a context-menu with 'Set Color' sub-menu.
The sub-menu pop a list of color to choose from.
Now, when the mouse cursor is over a specific color, I want it to change a label of "Selected color".
And when the mouse cursor leave the selected color menu-item, I want to label to restore its original text.
The following code demonstrate changing the label when menu-item selected - mouse is over.
private void Init()
{
var mnuContextMenu = new ContextMenu();
this.ContextMenu = mnuContextMenu;
var smthingElseMenu = new MenuItem("Do something else");
var setColorMenu = new MenuItem("Set Color");
var colorBlue = new MenuItem("Blue");
var colorRed = new MenuItem("Red");
var colorGreen = new MenuItem("Green");
mnuContextMenu.MenuItems.Add(smthingElseMenu);
mnuContextMenu.MenuItems.Add(setColorMenu);
setColorMenu.MenuItems.Add(colorBlue);
setColorMenu.MenuItems.Add(colorRed);
setColorMenu.MenuItems.Add(colorGreen);
colorBlue.Select += ColorSelect;
colorRed.Select += ColorSelect;
colorGreen.Select += ColorSelect;
}
void ColorSelect(object sender, EventArgs e)
{
lblSelectedColor.Text = ((MenuItem) sender).Text;
}
But I couldn't find a way to make the label text restore when the mouse cursor leave the menu-item.
Any ideas how can I implement some kind of 'Unselect'/'MouseLeave' event for MenuItem?
There's no "un-select" event for MenuItems, unfortunately.
I would just catch the Collapse event of your context menu, and reset your label there. This would have the added benefit that if your user hovers over the "Red" option, then hovers off the context menu, the label should stay red until the context menu closes.
mnuContextMenu.Collapse += (s, e) => lblSelectedColor.Text = "None";
If you really need it to reset the label when your mouse leave the context menu, then you could catch the MouseEnter event of the Panel (or whatever) you have that surrounds the ContextMenu.
MyPanel.MouseEnter += (s, e) => lblSelectedColor.Text = "None";
EDIT Do consider using the ContextMenuStrip class instead. The ToolSTripMenuItem class has a MouseLeave event. And a Checked property, probably what you really want.
Can't you just save the old MenuItem ref.
private MenuItem _oldMenuItem;
void ColorSelect(object sender, EventArgs e)
{
if(_oldMenuItem != null) _oldMenuItem.Text = someText;
_oldMenuItem = sender as MenuItem;
lblSelectedColor.Text = ((MenuItem) sender).Text;
}
Use MouseEnter and MouseLeave events to handle everything. First is raised when when the mouse pointer enters the bounds of this element. And second mouse pointer leaves the bounds - at this point you restore default label text.
EDIT like Hans pointed in comment use ContextMenuStrip and ToolStripMenuItems and youll have those events.
Can't you use:
private void colorBlue_MouseEnter(object sender, EventArgs e)
{
// use color blue
}
private void colorBlue_MouseLeave(object sender, EventArgs e)
{
// use old color
}
Related
i'm are trying to mimic the behavior of web browsers in a WinForm application where you can drag and drop tabs in and out of the browser and create another instance of the it when you drop the tab somewhere with no existing tabs.
Currently the WinForm application only has one main TabControl and I was looking at the DoDragDrop() related events, but they seem to only work when you have two TabControls and move TabPages around those two.
Is there a way to make it work with only one TabControl? Meaning, If you Drop a TabPage out of the TabControl then it will create a new TabControl with the TabPage in it?
I can only think of using:
private void TabControl_DragLeave(object sender, EventArgs e)
{
Form newInstance = new Form();
TabControl newTabControl = new TabControl();
newInstance.Controls.Add(newTabControl);
newTabControl.TabPages.Add(sender as TabPage);
newInstance.Show();
}
but that is pretty crud and will create the new tab every time you leave the TabControl.
It seems you are looking for an event which raises at the end of drop, regardless of ending over your control or outside of the control.
You can rely on QueryContinueDrag and check if the action is Drop, then check the mouse position and for example if it's not inside your control, just create another window and add the selected tab into a tab control inside the new window.
private void tabControl1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
tabControl1.DoDragDrop(tabControl1.SelectedTab, DragDropEffects.All);
}
}
private void tabControl1_DragOver(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(TabPage)))
e.Effect = DragDropEffects.Move;
}
private void tabControl1_QueryContinueDrag(object sender, QueryContinueDragEventArgs e)
{
if (e.Action == DragAction.Drop)
{
var tabPage = tabControl1.SelectedTab;
if (!tabControl1.RectangleToScreen(tabControl1.Bounds).Contains(Cursor.Position))
{
var form = new Form();
form.Text = tabPage.Text;
var tabControl = new TabControl();
tabControl.TabPages.Add(tabPage);
tabControl.Dock = DockStyle.Fill;
form.Controls.Add(tabControl);
form.FormBorderStyle = FormBorderStyle.SizableToolWindow;
form.StartPosition = FormStartPosition.Manual;
form.Location = new Point(Cursor.Position.X - form.Width / 2,
Cursor.Position.Y - SystemInformation.CaptionHeight / 2);
form.Show();
e.Action = DragAction.Cancel;
//You can comment tabControl.TabPages.Add
//Then set e.Action = DragAction.Continue
//Then the DragDrop event will raise and add the tab there.
}
}
}
private void tabControl1_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(TabPage)))
{
var tabPage = (TabPage)e.Data.GetData(typeof(TabPage));
tabControl1.TabPages.Remove(tabPage);
tabControl1.TabPages.Add(tabPage);
}
}
For more advanced scenarios and to enhance the code:
When start dragging, you can start dragging just if the mouse dragged at least for a specific points, for example 16 points. It's easy to calculate. Having p1 as mouse down point and p2 as mouse move point, and d as drag threshold. start dragging just in case (p1.X-p2.X)*(p1.X-p2.X) + (p1.Y-p2.Y)*(p1.Y-p2.Y) > d*d.
You can use GiveFeedback event to disable default cursor of the mouse and instead show a more suitable cursor while dragging, easily by e.UseDefaultCursors = false; and setting Cursor.Current = Cursors.SizeAll; for example.
You can encapsulate the logic and put it in a derived TabControl. Then in DragEnter and DragLeave events set a static property for tracking drop target. In case the drop-target has value, it means you are dropping on a derived tab control, otherwise it means you are dropping outside. Then drag and drop will be easily enabled for all your custom tab controls.
You can close the tool form, after a drag and drop, in case the form doesn't contain any other tab.
When adding the tab, you can insert it before/after the selected tab or the tab under cursor in target.
I have created a Table layout in windows forms as shown in figure, i have added a right mouse button click Menu to my table, i want to color the cell when i right click on the perticuler cell, so how can i do it.
When i click add device the cell should paint to green color,
When i click delete device it should show default color,
When i click fire the cell should be painted with red color
so on
The below is my form and table layout
Hi there i dont have what you looking for but,
TableLayoutPanels don't really have 'cells' as such and are really meant to be a container for controls. This means you can't really retrieve individual rows, columns or cells. An alternative would be to use panels and put individual click events on each of these.
However if you're determined that you want to use TableLayoutPanels, you can use the XY coordinate of where the mouse click occurred on the TableLayoutPanel from EventArgs. And determine which block it is, since you've got evenly spread rows/columns.
For example if you have all the cells the same size and the the TableLayoutPanel is docked in the form this will get the selected Cell.:
Point selectedCell = new Point();
private void tableLayoutPanel1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
//show contextMenuStrip
selectedCell = new Point(e.X / (tableLayoutPanel1.Width / tableLayoutPanel1.ColumnCount), e.Y / (tableLayoutPanel1.Height / tableLayoutPanel1.RowCount));
}
}
I got the answer for my question it can be done as shown below
First we need to find the label control(sender) which has sent the event as follows
private void AssignClickEvent()
{
foreach (Control c in tableLayout.Controls.OfType<Label>())
{
c.MouseClick += tablelayout_MouseClick;
addDevice.Click += addDevice_Click;
deleteDevice.Click += deleteDevice_Click;
fire.Click += fire_Click;
fault.Click += fault_Click;
suppress.Click += suppress_Click;
}
}
have a globle varible of pointer control
public Label h = new Label();
then we need to copy the sender to the control
private void tablelayout_MouseClick(object sender, MouseEventArgs e)
{
Label l = (Label)sender;
if (e.Button == MouseButtons.Right)
{
h = l;
num = l.Text;
m.MenuItems.AddRange(new MenuItem[] { addDevice, deleteDevice, fire, fault,suppress });
tableLayout.ContextMenu = m;
m.Show((Control)(sender), e.Location);
}
}
then use global control `h' to set color to the control
public void addDevice_Click(object sender, EventArgs e)
{
try
{
h.BackColor = Color.Green; ;
comport.Write("AB");
comport.Write(num);
comport.Write(" ");
stausLable.Text = "Device "+num+" added";
comport.WriteLine("000000000000");
}
catch (InvalidOperationException )
{
stausLable.Text = "Open communication port";
}
}
There is an easier way without having to implement those events as well as the extra variables. Assuming the contextmenu is named 'DeviceActionsContextMenu' and is linked to the label controls (as per your comment), Implement the toolstrip Item's click event -
private void addDeviceToolStripMenuItem_Click(object sender, EventArgs e)
{
(contextMenuStrip1.SourceControl as Label).BackColor = Color.Green;
}
Or Implement the contextmenu's Item clicked event -
private void DeviceActionsContextMenu_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
if (e.ClickedItem == addDeviceToolStripMenuItem)
{
(contextMenuStrip1.SourceControl as Label).BackColor = Color.Green;
}
}
In my Windows forms application written in C# I have a bunch of buttons. When the user's mouse hovers over a button, I want the button's border to change.
Currently I have multiple instances of the following (a copy for each button):
private void btnStopServer_MouseEnter(object sender, EventArgs e)
{
oldColor = btnStopServer.FlatAppearance.BorderColor;
btnStopServer.FlatAppearance.BorderColor = mouseOverColor;
}
private void btnStopServer_MouseLeave(object sender, EventArgs e)
{
btnStopServer.FlatAppearance.BorderColor = oldColor;
}
Since I have a lot of buttons, the code to change the color of the button's border takes up a lot of space.
Is there any simpler way that I could do this?
You should wire-up a single MouseEnter and MouseLeave to each control that needs this functionality (rather than writing a new version of each method for each control). Assuming you're using Visual Studio, this can be done by changing the target method name for the event, in each Button's property pane. If you write the following code first, then this method will appear in the property's MouseEnter and MouseLeave events' drop-down lists.
The code would then need to check which button from which the event was fired, as follows:
private void btnWithHoverBorder_MouseEnter(object sender, EventArgs e)
{
Button eventButton = (Button) sender;
oldColor = eventButton.FlatAppearance.BorderColor;
eventButton.FlatAppearance.BorderColor = mouseOverColor;
}
private void btnWithHoverBorder_MouseLeave(object sender, EventArgs e)
{
Button eventButton = (Button) sender;
eventButton.FlatAppearance.BorderColor = oldColor;
}
I presume oldColor is a global? This might get out of sync if something "odd" happens where your MouseEnter event is fired for another button, before the corresponding MouseLeave is caught. To make this more robust, I'd consider storing the old color on the Button's .tag property, so that it's self-contained.
Eg:
private void btnWithHoverBorder_MouseEnter(object sender, EventArgs e)
{
Button eventButton = (Button) sender;
eventButton.tag = eventButton.FlatAppearance.BorderColor;
eventButton.FlatAppearance.BorderColor = mouseOverColor;
}
private void btnWithHoverBorder_MouseLeave(object sender, EventArgs e)
{
Button eventButton = (Button) sender;
eventButton.FlatAppearance.BorderColor = (Color)eventButton.tag;
}
(The tag is basically a hook on which to tag "anything" relevant to a specific instance of a control, that there is not already a property for. It's of type Object which means you can tag anything there, but when you read from it, you need to cast it back to whatever type you put there in the first place. But because it's an Object you can put anything there, including eg a custom class that contains multiple properties, or an array, etc if you need to tag a control with more than one thing).
I have some context menu items that are not clickable. They just report the status of something. I don't like how the cursor still appears like they're clickable though.
Anyway to change this?
There isn't a Cursor Field like one would expect.
Handle the MouseMove event of the whole ToolStrip and check if the current mouse location is between the toolStripItem.Bounds. if so, change ToolStrip.Cursor
Amiram sent me in the right direction. You can't set the Cursor on the "ToolStripMenuItem" you have to set it on the parent ContextMenuStrip.
As for the mouse events, that has to go on the ToolStripMenuItems. As the MouseMove event is not fired when the Mouse is over ToolStripMenuItems.
// Init Code
contextMenuStrip1.Cursor = Cursors.Hand;
recentMessagesToolStripMenuItem.MouseLeave += new EventHandler(SetCursorToHandOn_MouseLeave);
recentMessagesToolStripMenuItem.MouseEnter += new EventHandler(SetCursorToArrowOn_MouseEnter);
private void SetCursorToArrowOn_MouseEnter(object sender, EventArgs e)
{
contextMenuStrip1.Cursor = Cursors.Arrow;
}
private void SetCursorToHandOn_MouseLeave(object sender, EventArgs e)
{
contextMenuStrip1.Cursor = Cursors.Hand;
}
I am creating an application where I need to generate dynamically created controls say textbox or label etc.
Now what I that user can relocate that textbox to his desired location. Like we do in Visual Studio.
One way is to get new location by getting values from him using textbox. But I want the user interface easy.
Can we have such functionality in winforms
I have created a simple form that demonstrate how to move the control by dragging the control.
The example assumes there is a button named button1 on the form attached to the relevant event handler.
private Control activeControl;
private Point previousLocation;
private void button1_Click(object sender, EventArgs e)
{
var textbox = new TextBox();
textbox.Location = new Point(50, 50);
textbox.MouseDown += new MouseEventHandler(textbox_MouseDown);
textbox.MouseMove += new MouseEventHandler(textbox_MouseMove);
textbox.MouseUp += new MouseEventHandler(textbox_MouseUp);
this.Controls.Add(textbox);
}
void textbox_MouseDown(object sender, MouseEventArgs e)
{
activeControl = sender as Control;
previousLocation = e.Location;
Cursor = Cursors.Hand;
}
void textbox_MouseMove(object sender, MouseEventArgs e)
{
if (activeControl == null || activeControl != sender)
return;
var location = activeControl.Location;
location.Offset(e.Location.X - previousLocation.X, e.Location.Y - previousLocation.Y);
activeControl.Location = location;
}
void textbox_MouseUp(object sender, MouseEventArgs e)
{
activeControl = null;
Cursor = Cursors.Default;
}
It is the easiest way: First go to your solution name and right click. Select "Manage NuGet Packages". After a while a window opens with a search bar on top of it. Choose the "Browse option " and search DraggableControl package. The name Control.Draggable must be seen. Click on it then click install. Now you can use it's special commands for example.
private void button1_Click(object sender, EventArgs e)
{ Point p = new Point(20,70 * i);
RichTextBox tb = new RichTextBox();
tb.Location = p;
tb.Height= 60;
tb.Width = 100;
tb.Font = Normal;
ControlExtension.Draggable(tb,true);
this.Controls.Add(tb);
i++;
The ControlExtension.Draggable command can set it to be draggable or not. Just write the name of the object in the brackets (tb for me) and write a comma. Then write it is draggable (true) or not (false).
NOTE: Do not Forget to put a semicolon.
Hope it helps.
Link : https://www.nuget.org/packages/Control.Draggable/
You can call DoDragDrop with a data object containing or representing the control to begin a drag&drop operation, then handle the container's DragDrop event and move the control.
If you want to see the control as it's dragged, you can either make a transparent (handle WM_NCHITTEST) form under the mouse showing the control (call DrawToBitmap), or not use drag&drop at all and instead handle mouse events and track state manually.
If you want Visual Studio-style snaplines, you can compare the control's bounds to other controls, make a set of lines to draw, and draw them in a paint event.