Drag form with control from another class - c#

Firstly, I apologize if the title does not make much sense, as I did not know the best way to explain it.
Now to really explain it. What I have done is created a control in a Class Library project in Visual Studio 2013. This control is supposed to act as the caption bar for form that is set with the "FormBorderStyle" as "None". This imitation caption bar control is supposed to move the form, just like a normal forms' caption bar would.
I have achieved this, but only in the forms code. This is the code I use:
private int mouseStartX, mouseStartY;
private int formStartX, formStartY;
private bool FormDragging = false;
private void titleBar_MouseDown(object sender, MouseEventArgs e)
{
this.mouseStartX = MousePosition.X;
this.mouseStartY = MousePosition.Y;
this.formStartX = this.Location.X;
this.formStartY = this.Location.Y;
FormDragging = true;
}
private void titleBar_MouseMove(object sender, MouseEventArgs e)
{
if (FormDragging)
{
this.Location = new Point(
this.formStartX + MousePosition.X - this.mouseStartX,
this.formStartY + MousePosition.Y - this.mouseStartY
);
}
}
private void titleBar_MouseUp(object sender, MouseEventArgs e)
{
FormDragging = false;
}
"this.*" is obviously referring to the form, when in the forms code. So of course, if I were to simply put this into the controls code, it'd obviously be referring to the control, and thus the control would be the one moving around on the form.
I've also created a control in the Class Library that acts as a close button. All I had to do was:
Form.ActiveForm.Close();
Same for minimize being:
Form.ActiveForm.WindowState = FormWindowState.Minimized;
And maximize being:
Form.ActiveForm.WindowState = FormWindowState.Maximized;
On the controls' click events.
When I try to replace "this." with "Form.ActiveForm.", in the first code posted - it returns this error:
'System.Windows.Forms.Form' does not contain a definition for 'mouseStarX' and no extension method 'mousStartX' accepting a first argument of type 'System.Windows.Forms.Form' could be found (are you missing a using directive or an assembly reference?)
That's about it, I don't know how else to go about this.

There is a simple pinvoke you can use to move the form via your control.
Adapted from C# - Make a borderless form movable?, instead of using Form.ActiveForm, you would use this.FindForm() to get the parent form of the control. It's used here to pass the form's handle value:
public class MyHeader : Control {
private const int WM_NCLBUTTONDOWN = 0xA1;
private const int HT_CAPTION = 0x2;
[DllImportAttribute("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int Msg,
int wParam, int lParam);
[DllImportAttribute("user32.dll")]
private static extern bool ReleaseCapture();
protected override void OnMouseDown(MouseEventArgs e) {
if (e.Button == MouseButtons.Left) {
ReleaseCapture();
SendMessage(this.FindForm().Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
base.OnMouseDown(e);
}
}
For closing the form, you would use the same method:
this.FindForm().Close();

Related

How to implement the common "Drag and Drop" icon in WinForms

I'm currently designing a simple WinForms UserControl in C# where a user can drag and drop an excel file onto a panel instead of browsing for the file. I have it technically working, but it's very crude.
In short, my code currently looks like this for the DragEnter and DragDrop events on the panel (removed error handling):
private void dragPanel_DragEnter(object sender, DragEventArgs e)
{
var filenames = (string[])e.Data.GetData(DataFormats.FileDrop, false);
if (Path.GetExtension(filenames[0]) == ".xlsx") e.Effect = DragDropEffects.All;
else e.Effect = DragDropEffects.None;
}
private void dragPanel_DragDrop(object sender, DragEventArgs e)
{
var filenames = (string[])e.Data.GetData(DataFormats.FileDrop, false);
string filename = filenames[0];
// Do stuff
}
I'm trying to get the Excel icon to show up as I drag the file, but all I can get is this thing:
Anywhere I've looked online (mostly on this forum) has said I need to implement my own custom cursor if I want a specific icon to show up, but I honestly don't believe that. I took screenshots of multiple applications from different companies all using the exact same control (this is just a subset). Note that none of them are even cursors, the icons just follow the cursor:
Windows Explorer:
Google Chrome:
Adobe Acrobat:
Microsoft Edge:
(Same icon, but DragDropEffects are likely set to None)
So my conclusion is there must be a common windows control for this, but where is it? There's no way all of these companies just coincidentally built the exact same design and functionality!
Any help would be appreciated!
Bonus Question: Apparently in Windows 10 you're not allowed to drag-and-drop onto a program that's running as Administrator, however Chrome definitely lets you do this. You can run Chrome as Admin and drag a file onto it without any issue. What magic did Google use to bypass this security feature? I'd like to implement it as well as my control could potentially be used inside a program running as admin.
Standard way to do that is to delegate drop icon rendering DragDropHelper COM Object provided by Shell.
It allows application to negotiate image and icon to be displayed. In your case, Explorer already uses IDragSourceHelper for drag icon negotiation, so all you have to do is to delegate drop events to IDropTargetHelper exposed by DragDropHelper:
Interop:
using IDataObject_Com = System.Runtime.InteropServices.ComTypes.IDataObject;
[StructLayout(LayoutKind.Sequential)]
public struct Win32Point
{
public int x;
public int y;
}
[ComImport]
[Guid("4657278A-411B-11d2-839A-00C04FD918D0")]
public class DragDropHelper { }
[ComVisible(true)]
[ComImport]
[Guid("4657278B-411B-11D2-839A-00C04FD918D0")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDropTargetHelper
{
void DragEnter(
[In] IntPtr hwndTarget,
[In, MarshalAs(UnmanagedType.Interface)] IDataObject_Com dataObject,
[In] ref Win32Point pt,
[In] int effect);
void DragLeave();
void DragOver(
[In] ref Win32Point pt,
[In] int effect);
void Drop(
[In, MarshalAs(UnmanagedType.Interface)] IDataObject_Com dataObject,
[In] ref Win32Point pt,
[In] int effect);
void Show(
[In] bool show);
}
Form:
private IDropTargetHelper ddHelper = (IDropTargetHelper)new DragDropHelper();
private void Form1_DragDrop(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
Point p = Cursor.Position;
Win32Point wp;
wp.x = p.X;
wp.y = p.Y;
ddHelper.Drop(e.Data as IDataObject_Com, ref wp, (int)e.Effect);
}
private void Form1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
Point p = Cursor.Position;
Win32Point wp;
wp.x = p.X;
wp.y = p.Y;
ddHelper.DragEnter(this.Handle, e.Data as IDataObject_Com, ref wp, (int)e.Effect);
}
private void Form1_DragLeave(object sender, EventArgs e)
{
ddHelper.DragLeave();
}
private void Form1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
Point p = Cursor.Position;
Win32Point wp;
wp.x = p.X;
wp.y = p.Y;
ddHelper.DragOver(ref wp, (int)e.Effect);
}
WPF Version is basically the same, with minor changes:
private void Window_DragEnter(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Copy;
e.Handled = true;
Point p = this.PointToScreen(e.GetPosition(this));
Win32Point wp;
wp.x = (int)p.X;
wp.y = (int)p.Y;
ddHelper.DragEnter(new WindowInteropHelper(this).Handle, e.Data as IDataObject_Com, ref wp, (int)e.Effects);
}
References:
PInvoke on IDropTargetHelper
MSDN Blogs: Shell Style Drag and Drop in .NET (WPF and WinForms) - covers both source and target implementation.
Chrome / Chromium DragDrop Target Helper.

Proper way of activating my MainForm from a NotifyIcon if it wasn't focused already

I just want to replace the task bar button of my winforms application by a tray notification icon. That means, if the user left-clicks the icon, the form should be activated if it wasn't focused, otherwise minimized or hidden.
I read lots and lots of articles about properly using a NotifyIcon, and it seems I have to accept a hackish solution. So, what's the most proper way?
I got it mostly to run, but now I'm stuck at detecting if my form was active already - because when clicking the icon, the form looses focus, so I cannot check the Focused property.
The following code does not yet solve this, so if the form was just hidden by other windows, you have to click 2 times, because the first click minimizes.
How can it be improved?
private void FormMain_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized)
Hide();
}
private void notifyIcon_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
if (WindowState == FormWindowState.Minimized) {
WindowState = FormWindowState.Normal;
Show();
Activate();
}
else {
Hide();
WindowState = FormWindowState.Minimized;
}
}
(I also don't understand why the Click event fires on right-clicking, which already opens the context menu in my case...)
(And of course it would be nice to have a proper minimize animation, but there are other questions here, where this wasn't really solved)
(I know I said Focused, but if the form was already fully visible (but maybe not focused), and the user clicks the tray icon, he most likely wants to hide it)
#LarsTech suggested a hack using a timer, and this works, thanks, but I still hope for better solutions or improvements.
Maybe interesting: I also hack-solved part of the animation problem - when there is no taskbar button, the animation originates from where the minimized form is located. You can make it visible by calling Show() after minimizing. It sits in the lower left of the screen and it is not possible to move it by setting i.e. the Left property. So I used winapi directly and it works!
Unfortunately only for the restore animation, because I don't know how to set the minimized position before minimizing. Hide() disables the animation anyway, so I would have to delay it...
That all is so disappointing! Why is there no nice solution? I can never be sure if it will work in every scenario. For example on one machine it worked well with a 100ms timer, but on another I needed 200ms. So I suggest to have a least 500ms.
[DllImport("user32.dll", SetLastError = true)]
static extern bool MoveWindow(IntPtr hWnd,
int X, int Y, int nWidth, int nHeight, bool bRepaint);
private void FormMain_Resize(object sender, EventArgs e)
{
if (!ShowInTaskbar && WindowState == FormWindowState.Minimized) {
Hide();
//Move the invisible minimized window near the tray notification area
if (!MoveWindow(Handle, Screen.PrimaryScreen.WorkingArea.Left
+ Screen.PrimaryScreen.WorkingArea.Width - Width - 100,
Top, Width, Height, false))
throw new Win32Exception();
}
}
private void notifyIcon_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
if (WindowState == FormWindowState.Minimized || !timer.Enabled) {
Show();
WindowState = FormWindowState.Normal;
Activate();
}
else {
WindowState = FormWindowState.Minimized;
//FormMain_Resize will be called after this
}
}
private void FormMain_Deactivate(object sender, EventArgs e)
{
timer.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
}
//other free goodies not mentioned before...
private void taskbarToolStripMenuItem_Click(object sender, EventArgs e)
{
ShowInTaskbar = !ShowInTaskbar;
taskbarToolStripMenuItem.Checked = ShowInTaskbar;
}
private void priorityToolStripMenuItem_Click(object sender, EventArgs e)
{
//Set the process priority from ToolStripMenuItem.Tag
//Normal = 32, Idle = 64, High = 128, BelowNormal = 16384, AboveNormal = 32768
Process.GetCurrentProcess().PriorityClass
= (ProcessPriorityClass)int.Parse((sender as ToolStripMenuItem).Tag.ToString());
foreach (ToolStripMenuItem item in contextMenuStrip.Items)
if (item.Tag != null)
item.Checked = item.Equals(sender);
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}

Showing starting text of Mulitline TextBox when focus is changed

I am creating a form which has a Multiline TextBox to enter an URL. Expected URLs will be very long.
User will paste the URL and move to next box.
Right now, TextBox shows ending part of the URL when user moves to next TextBox. I want such that it will show starting of URL (Domain name) instead of trailing part.
Current:
Expected:
And this should happen when user leaves the TextBox.
I tried various methods of Selection in textBox_Leave() event but I guess, these methods won't work if focus is lost.
I am using .Net framework 3.5.
Update: Textbox I am using is Multiline. Answers suggested by #S.Akbari and #Szer are perfect if the Mutliline property is set to False. I realized it late that Multiline will play such a significant role. Hence updating the question!
Use SelectionStart in the Leave event should works:
private void textBox1_Leave(object sender, EventArgs e)
{
textBox1.SelectionStart = 0;
}
Before:
After leaving TextBox:
Tried it and it works. Proof
public Form1()
{
InitializeComponent();
textBox1.LostFocus += TextBox1_LostFocus;
}
private void TextBox1_LostFocus(object sender, EventArgs e)
{
textBox1.SelectionStart = 0;
textBox1.SelectionLength = 0;
}
I can see how it doesn't work with the Multiline property set to true.
A simple API call can make this work:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
private const int WM_VSCROLL = 0x115;
private const int SB_TOP = 6;
void textBox1_Leave(object sender, EventArgs e) {
SendMessage(textBox1.Handle, WM_VSCROLL, (IntPtr)SB_TOP, IntPtr.Zero);
}

C# - Fix linklabel hand-cursor

I have two link labels in my windows forms program which links to my website.
I got rid of the underlines and the ugly blue colour and tried to fix them up a little bit.
But the biggest problem still remains and It's just so disturbing for me, I don't know why.
The hand cursor when you hover over them is that old Windows 98 hand/link cursor.
Is there any way to change it to the system cursor?
I've checked some other links about this problem, but I couldn't get it to work so I decided to ask here.
Here's my code to get rid of the underline btw:
linkLabel1.LinkBehavior = System.Windows.Forms.LinkBehavior.NeverUnderline;
Unfortunately the LinkLabel class is hard-coded to use Cursors.Hand as the hover cursor.
However, you can work around it by adding a class like this to your project:
public class MyLinkLabel : LinkLabel
{
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
OverrideCursor = Cursors.Cross;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
OverrideCursor = null;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
OverrideCursor = Cursors.Cross;
}
}
and using that instead of LinkLabel on your form. (This sets the cursor to a cross for testing purposes, but you can change it to whatever you want.)
I should say that the real LinkLabel code has much more complex logic to do with changing the cursor according to whether or not the link is enabled, but you might not care about that.
Set the Cursor property to Arrow in the properties pane of the LinkLabel in Visual Studio
Update
I prefer Hamido-san's answer here. His solution works properly when the LinkLabel is set to AutoSize = false and works with a LinkArea.
Old solution:
public class LnkLabel : LinkLabel
{
const int WM_SETCURSOR = 32,
IDC_HAND = 32649;
[DllImport("user32.dll")]
public static extern int LoadCursor(int hInstance, int lpCursorName);
[DllImport("user32.dll")]
public static extern int SetCursor(int hCursor);
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SETCURSOR)
{
int cursor = LoadCursor(0, IDC_HAND);
SetCursor(cursor);
m.Result = IntPtr.Zero; // Handled
return;
}
base.WndProc(ref m);
}
}

Winform and WPF form/window border

I have been dealing with this issue and i dont know how to proceed.
I have a project with both windows forms and WPF forms.
I want every form to be displayed like this (its a WPF one):
WPF = http://imageshack.us/photo/my-images/545/wpfk.png/
I achieved this windows style = none and canresize = yes. I dont actually want it to be resized. I just want that thin border arround the form. But if i put canresize = false i lose the border. I also want to be able to move the window in the screen, not to be static in that place.
I need all that for my winforms too.
Winforms:
WINFORM = http://imageshack.us/photo/my-images/836/winforms.png/
I hope you guys understand what i need. Graphically, it has to be like the first image.
I'm not sure if it helps, but you can create Forms collection for this. (a)
http://support.microsoft.com/kb/815707/en-us
Solution: Paste this code into your form or base form.
private const int WS_SYSMENU = 0x80000;
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.Style &= ~WS_SYSMENU;
return cp;
}
}
Thanks Killercam for the help!
Solution for WPF Window:
public MainWindow()
{
SourceInitialized += Window_SourceInitialized;
InitializeComponent();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private void button3_Click(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
private void Window_SourceInitialized(object sender, EventArgs e)
{
WindowInteropHelper wih = new WindowInteropHelper(this);
int style = GetWindowLong(wih.Handle, GWL_STYLE);
SetWindowLong(wih.Handle, GWL_STYLE, style & ~WS_SYSMENU);
}
private const int GWL_STYLE = -16;
private const int WS_SYSMENU = 0x00080000;
[DllImport("user32.dll")]
private extern static int SetWindowLong(IntPtr hwnd, int index, int value);
[DllImport("user32.dll")]
private extern static int GetWindowLong(IntPtr hwnd, int index);
You just need to set the WinForms FormBorderStyle property in the designer to Sizable, FixedDialog, Fixed3D etc. One of these is bound to give you the behaviour you require.

Categories