Moving Frameless Windows Form via Mouse Drag - c#

I'm new to C# and I've attempted many of the solutions here on StackExchange and I've had no luck in trying to set up a control or the entire form for that matter to left click and drag the entire window. I'm working with a frameless window in visual studio 12. The closest I've come to moving the window is moving a single control with the pastebin component(last response) from this-
How do I make mousedrag inside Panel move form window?
I could only get the panel itself to move with that component.
I've tried most of the approaches but I seem get lost where I am to customize it to my own needs. I've tried WndProc override but it didn't do anything when I attempted to move the form window.
I have two panels I want to be able to drag the window with DragPanel and DragPanel2.
Here is my most recent failed approach trying to use the whole form.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
InsideMover _dragger = new InsideMover();
_dragger.ControlToMove = this.DragPanel;
}
private void close_Click(object sender, EventArgs e)
{
Close();
}
}
public class InsideMover : Component
{
/// <summary>
/// Required designer variable.
/// </summary>
private Container components = null;
public InsideMover(IContainer container)
{
///
/// Required for Windows.Forms Class Composition Designer support
///
container.Add(this);
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
public InsideMover()
{
///
/// Required for Windows.Forms Class Composition Designer support
///
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
public Control ControlToMove
{
set
{
if (_parent != null)
{
// odkvaci prijasnje evente
_parent.MouseDown -= new MouseEventHandler(_parent_MouseDown);
_parent.MouseMove -= new MouseEventHandler(_parent_MouseMove);
_parent.MouseUp -= new MouseEventHandler(_parent_MouseUp);
_parent.DoubleClick -= new EventHandler(_parent_DoubleClick);
}
_parent = value;
if (value != null)
{
// zakači se na evente od containera koji ti trebaju
_parent.MouseDown += new MouseEventHandler(_parent_MouseDown);
_parent.MouseMove += new MouseEventHandler(_parent_MouseMove);
_parent.MouseUp += new MouseEventHandler(_parent_MouseUp);
_parent.DoubleClick += new EventHandler(_parent_DoubleClick);
}
}
get
{
return _parent;
}
}
Control _parent;
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
int _lastMouseX;
int _lastMouseY;
bool _moving;
public void StartMouseDown(MouseEventArgs e)
{
_parent_MouseDown(null, e);
}
private void _parent_MouseDown(object sender, MouseEventArgs e)
{
_lastMouseX = e.X;
_lastMouseY = e.Y;
_moving = true;
}
private void _parent_MouseMove(object sender, MouseEventArgs e)
{
if (_moving)
{
Point newLocation = _parent.Location;
newLocation.X += e.X - _lastMouseX;
newLocation.Y += e.Y - _lastMouseY;
_parent.Location = newLocation;
}
}
private void _parent_MouseUp(object sender, MouseEventArgs e)
{
_moving = false;
}
private void _parent_DoubleClick(object sender, EventArgs e)
{
if (_parent is Form)
{
Form f = (Form)_parent;
if (f.WindowState == FormWindowState.Normal)
{
f.WindowState = FormWindowState.Maximized;
}
else
{
f.WindowState = FormWindowState.Normal;
}
}
}
}
}
How can I set the panels to left click drag the window?
I've tried all of the methods at the post above and the WndProc method here:
Drag borderless windows form by mouse

If you follow the answer in the link you've posted, it is using the mousemove event, whereas judging by the code you've posted, you're using the mousedown event. The mouse down event is only called once when you press a mouse button, it is not called again if you move the mouse while you keep pressing the button. Whereas the mousemove event is called whenever your pointer moves. So your best bet would be to change your mousedown event with the mousemove event i.e.
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
}
if that doesn't work, you can do something like this in the mousemove event, first create a Point 'prevpoint' and an offset point in the form.
Point prevpoint=new Point(0,0);
Point offset=new Point(0,0);
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
offset.X=e.X-prevpoint.X;
offset.Y=e.Y-prevpoint.Y;
prevpoint.X=e.X;
prevpoint.Y=e.Y;
this.Location = new Point(this.Location.X + offset.X, this.Location.Y + offset.Y);
}
}
I have not tested the above code but that will hopefully give you the basic idea.

Related

C# Winforms How to Understand Form object is moved

I want to trigger an event only when the form is moved via the mouse click over the title bar. I could not find proper event for this.
Currently I implemented a move event for my forms in winform. I only want the move event to be triggered when the user drags the form via clicking on the title bar. However, this event is triggered also when form is tried to be resized by mouse or minimized/maximized. How can I disable this? I only want to trigger an event only when the form is moved. I am trying to implement my own floating forms and I want to catch this specific event to change the MDiParent of the form.
You can check if the form is resized and the Form's FormWindowState is changed in the Move event, if not so, you can decide that Form is moving using the title bar.
To do so, you should cache both the window state and size each time when Move event is triggered.
Note: The Move event is triggered even when you change the location of the form via the Location property, not only using the title bar. So, the event FromDragged will be triggered in the case. This is a "false positive".
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WhenDraggingUsingCaptionBar
{
public partial class CustomForm : Form
{
public CustomForm()
{
InitializeComponent();
FormDragged += Form1_FormDragged;
}
private void Form1_FormDragged(object sender, EventArgs e)
{
MessageBox.Show("Test");
}
public event EventHandler FormDragged;
private Size _cachedSize = new Size(0, 0);
private FormWindowState _cachedState = FormWindowState.Normal;
private void Form1_Move(object sender, EventArgs e)
{
if (_cachedSize == Size && _cachedState == WindowState)
if (FormDragged != null)
FormDragged(this, new EventArgs());
_cachedSize = Size;
_cachedState = WindowState;
}
}
}
As an addition, there is a low-level solution using Win32 API. This solution eliminates the "false-positive" issues in the approach above.
You can handle the WM_NCLBUTTONDOWN message. This message is sent when you down the mouse left button in the non-client area of the window. When we get the message, we set a variable to true. We also catch another message WM_EXITSIZEMOVE to understand when the dragging the window is stopped and set the variable to false.
If the variable is set to true when the move event is triggered, we can say the window is being dragged using the title bar.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace WhenDraggingUsingCaptionBar
{
public partial class CustomForm : Form
{
public CustomForm()
{
InitializeComponent();
FormDragged += Form1_FormDragged;
}
private void Form1_FormDragged(object sender, EventArgs e)
{
Debug.WriteLine("{1}: Move:{0}", _ncbuttonDown, DateTime.Now);
}
public event EventHandler FormDragged;
private const int WM_NCLBUTTONDOWN = 0x00A1;
private const int WM_EXITSIZEMOVE = 0x0232;
private bool _ncbuttonDown = false;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NCLBUTTONDOWN)
_ncbuttonDown = true;
else if (m.Msg == WM_EXITSIZEMOVE)
_ncbuttonDown = false;
base.WndProc(ref m);
}
private void CustomForm_Move(object sender, EventArgs e)
{
if (_ncbuttonDown)
if (FormDragged != null)
FormDragged(this, new EventArgs());
}
}
}

winforms: move a borderless form (without title bar)

I have a host class which inherits from Form. I use it to embed an WPF user control to use it in a winform application. This is customizable, I mean, you can choose to make form borderless or not.
If borderless, you cannot move the window, so I need to implement it manually. In this case I subscribe to some event which do the trick (I have removed the not necessary things from the class to focus only on the important parts):
public class MyHostDialog : Form
{
public MyDialogHost()
{
InitializeComponent();
}
public MyHostDialog(MyDialog myDialog) : this()
{
this.ElementHost.Child = new MyHostDialogView(myDialog);
if (this.Borderless)
{
this.ElementHost.Child.MouseDown += Child_MouseDown;
this.ElementHost.Child.MouseUp += Child_MouseUp;
this.ElementHost.Child.MouseMove += Child_MouseMove;
}
}
// BELOW CODE DOES NOT WORK, IT WOULD WORK IF Input.MouseButtonEventArgs and Input.MouseEventArgs WERE
// System.Windows.FORMS.MouseEventArgs INSTEAD.
private bool dragging = false;
private System.Drawing.Point startPoint = new System.Drawing.Point(0,0);
private void Child_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
dragging = true;
startPoint = new System.Drawing.Point(e.X, e.Y);
}
private void Child_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
dragging = false;
}
private void Child_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (dragging)
{
System.Drawing.Point p= PointToScreen(e.Location);
Location = new System.Drawing.Point(p.X - this.startPoint.X, p.Y - this.startPoint.Y);
}
}
}
Above code would work if instead of using Input.MouseButtonEventArgs and Input.MouseEventArgs, they were System.Windows.FORMS.MouseEventArgs. So, how to convert that code to work with MouseButtonEventArgs?

How to disable tab headers in tabcontrol

I have a wizard to make a project, I make us e of a tabcontrol. I have buttons to go to the next tab or the previous. My problem now is that even thought there is validation on the buttons for required field, you can still switch between the tabs by clicking the tab headers. If I disable the tabcontrol, the user can't use whatever is inside the tabs either. Can I solve this?
UPDATE: all the code of the wizard form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WorldEstablisher
{
public partial class ProjectWizard : Form
{
#region variables
public Form1 MainForm { get; set; }
#endregion
#region constructor and page load
public ProjectWizard(Form1 form)
{
InitializeComponent();
MainForm = form;
}
private void ProjectWizard_Load(object sender, EventArgs e)
{
}
#endregion
#region navigation
private void nextButton_Click(object sender, EventArgs e)
{
if (tabs.SelectedIndex == 0)//field validation tab 1
{
if (folderLocationTextBox.Text != "" && worldNameTextBox.Text != "")
{
backButton.Visible = true;
tabs.SelectedIndex = tabs.SelectedIndex + 1;
}
}
if (tabs.SelectedIndex == 1)//field validation tab 2
{
if (authorTextBox.Text != "")
{
tabs.SelectedIndex = tabs.SelectedIndex + 1;
}
}
if (tabs.SelectedIndex == 2)
{
finishButton.Visible = true;
}
}
private void backButton_Click(object sender, EventArgs e)
{
if (tabs.SelectedIndex != 0)
{
tabs.SelectedIndex = tabs.SelectedIndex - 1;
if (tabs.SelectedIndex == 0)//Make the back button invisible
{
backButton.Visible = false;
}
if (tabs.SelectedIndex != 2)//Make the finish button invisible
{
finishButton.Visible = false;
}
}
}
private void finishButton_Click(object sender, EventArgs e)
{
World world = new World("test");
MainForm.CurrentWorld = world;
this.Close();
}
#endregion
private void selectFolderButton_Click(object sender, EventArgs e)
{
if (folderBrowser.ShowDialog() == DialogResult.OK)
{
folderLocationTextBox.Text = folderBrowser.SelectedPath;
}
}
}
}
There should be a better way to do this, but in the SelectedIndexChanged event for the TabControl you can set the SelectedTab to be whichever TabPage you want it to be. Track the current TabPage when the user clicks on your navigation buttons.
private TabPage currentTabPage;
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
tabControl1.SelectedTab = currentTabPage;
}
Since you'd be basically eliminating the built-in navigation of a TabControl, I'd rethink your design. Use a stack of Panel objects that you show or hide based on your button navigation mechanism. Don't confuse the user by showing them tab buttons that they can't ever use.

Flashing ToolStripButton

I am using a ToolStrip with a number of ToolStripButtons.
What I would like is to be able to flash one of the buttons to get the user's attention.
For example, if they have made changes to information and need to click the Save button.
If this were a normal button I could do this using a Timer and periodically changing the BackColor however this doesn't work with a ToolStrip.
I could create a Renderer subclass and assign it to the ToolStrip but this appears to only get used in specific situations - i.e. it's event driven.
Does anyone have any ideas?
Well, just use a custom renderer so you can change the color of the button's background. With a timer that blinks it. Add a new class to your project and paste this code:
using System;
using System.Drawing;
using System.Collections.Generic;
using System.Windows.Forms;
class BlinkingButtonRenderer : ToolStripProfessionalRenderer {
public BlinkingButtonRenderer(ToolStrip strip) {
this.strip = strip;
this.strip.Renderer = this;
this.strip.Disposed += new EventHandler(strip_Disposed);
this.blinkTimer = new Timer { Interval = 500 };
this.blinkTimer.Tick += delegate { blink = !blink; strip.Invalidate(); };
}
public void BlinkButton(ToolStripButton button, bool enable) {
if (!enable) blinkButtons.Remove(button);
else blinkButtons.Add(button);
blinkTimer.Enabled = blinkButtons.Count > 0;
strip.Invalidate();
}
protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e) {
var btn = e.Item as ToolStripButton;
if (blink && btn != null && blinkButtons.Contains(btn)) {
Rectangle bounds = new Rectangle(Point.Empty, e.Item.Size);
e.Graphics.FillRectangle(Brushes.Black, bounds);
}
else base.OnRenderButtonBackground(e);
}
private void strip_Disposed(object sender, EventArgs e) {
blinkTimer.Dispose();
}
private List<ToolStripItem> blinkButtons = new List<ToolStripItem>();
private bool blink;
private Timer blinkTimer;
private ToolStrip strip;
}
Sample usage in a form with a Toolstrip containing a button:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
blinker = new BlinkingButtonRenderer(toolStrip1);
}
private void toolStripButton1_Click(object sender, EventArgs e) {
blink = !blink;
blinker.BlinkButton(toolStripButton1, blink);
}
private bool blink;
private BlinkingButtonRenderer blinker;
}

How to change button background image on mouseOver?

I have img1, and img2 in my resources. I have easily set btn.backgroundImage as img1 in btn properties. Images paths are: c:\Project\Resources...
Now I don't know how to set btn.backgroundImage to be img2, I want to do it on event "MouseEnter". So I would apreciate complete code, because I am pretty green about this...
I apreciate any given idea...
In the case of winforms:
If you include the images to your resources you can do it like this, very simple and straight forward:
public Form1()
{
InitializeComponent();
button1.MouseEnter += new EventHandler(button1_MouseEnter);
button1.MouseLeave += new EventHandler(button1_MouseLeave);
}
void button1_MouseLeave(object sender, EventArgs e)
{
this.button1.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.img1));
}
void button1_MouseEnter(object sender, EventArgs e)
{
this.button1.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.img2));
}
I would not recommend hardcoding image paths.
As you have altered your question ...
There is no (on)MouseOver in winforms afaik, there are MouseHover and MouseMove events, but if you change image on those, it will not change back, so the MouseEnter + MouseLeave are what you are looking for I think. Anyway, changing the image on Hover or Move :
in the constructor:
button1.MouseHover += new EventHandler(button1_MouseHover);
button1.MouseMove += new MouseEventHandler(button1_MouseMove);
void button1_MouseMove(object sender, MouseEventArgs e)
{
this.button1.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.img2));
}
void button1_MouseHover(object sender, EventArgs e)
{
this.button1.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.img2));
}
To add images to your resources: Projectproperties/resources/add/existing file
I think something like this:
btn.BackgroundImage = Properties.Resources.*Image_Identifier*;
Where *Image_Identifier* is an identifier of the image in your resources.
I made a quick project in visual studio 2008 for a .net 3.5 C# windows form application and was able to create the following code. I found events for both the enter and leave methods.
In the InitializeComponent() function. I added the event handler using the Visual Studio designer.
this.button1.MouseLeave += new System.EventHandler( this.button1_MouseLeave );
this.button1.MouseEnter += new System.EventHandler( this.button1_MouseEnter );
In the button event handler methods set the background images.
/// <summary>
/// Handles the MouseEnter event of the button1 control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void button1_MouseEnter( object sender, EventArgs e )
{
this.button1.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.img2));
}
/// <summary>
/// Handles the MouseLeave event of the button1 control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private void button1_MouseLeave( object sender, EventArgs e )
{
this.button1.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.img1));
}
You can create a class based on a Button with specific images for MouseHover and MouseDown like this:
public class AdvancedImageButton : Button {
public Image HoverImage { get; set; }
public Image PlainImage { get; set; }
public Image PressedImage { get; set; }
protected override void OnMouseEnter(System.EventArgs e) {
base.OnMouseEnter(e);
if (HoverImage == null) return;
if (PlainImage == null) PlainImage = base.Image;
base.Image = HoverImage;
}
protected override void OnMouseLeave(System.EventArgs e) {
base.OnMouseLeave(e);
if (HoverImage == null) return;
base.Image = PlainImage;
}
protected override void OnMouseDown(MouseEventArgs e) {
base.OnMouseDown(e);
if (PressedImage == null) return;
if (PlainImage == null) PlainImage = base.Image;
base.Image = PressedImage;
}
}
This solution has a small drawback that I am sure can be fixed: when you need for some reason change the Image property, you will also have to change the PlainImage property also.

Categories

Resources