I can set a cursor like this:
Me.Cursor = Cursors.Cross
Using IntelliSense, I don't find this "Copy" cursor:
Is there any way to get it in a managed way?
I wouldn't want to load a bitmap or so.
I would like to leave that up to Windows as the user may have changed the cursor size or set a different color schema.
Drag and drop cursors belong ole32.dll. You can load them from that library. To do so, you need to load ole32.dll using LoadLibrary, then using LoadCursor get the handle of those cursors. You can use 1 to 7 as LoadCursor parameter to get cursors from ole32.dll. The cursor which you are looking for is 3 or 6:
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("user32.dll")]
public static extern IntPtr LoadCursor(IntPtr hInstance, UInt16 lpCursorName);
private void button1_Click(object sender, EventArgs e)
{
var l = LoadLibrary("ole32.dll");
var h = LoadCursor(l, 6);
this.Cursor = new Cursor(h);
}
Related
I want to use C# code to simulate a user dragging and dropping a file onto a control in a separate process. As a stepping stone to this goal, I am trying to send a WM_DROPFILES message to my own TextBox and verifying that the DragDrop event is triggered.
With the code below in a Form containing a single TextBox and two Buttons, clicking on button1 successfully sets the text of textBox1 to "Hello world". So, it seems that I'm using SendMessage correctly and am able to supply arguments via pointers. Dragging and dropping a file from Windows Explorer onto textBox1 displays the MessageBox, so textBox1 is set up to receive drag-dropped files correctly. However, when I click button2, nothing happens. Why don't I see a MessageBox when I click button2?
using System;
using System.Data;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace StackOverflow
{
public partial class BadDragDrop : Form
{
#region WINAPI
[Serializable]
[StructLayout(LayoutKind.Sequential)]
struct POINT
{
public Int32 X;
public Int32 Y;
}
[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
class DROPFILES
{
public Int32 size;
public POINT pt;
public Int32 fND;
public Int32 WIDE;
}
const uint WM_DROPFILES = 0x0233;
const uint WM_SETTEXT = 0x000C;
[DllImport("Kernel32.dll", SetLastError = true)]
static extern int GlobalLock(IntPtr Handle);
[DllImport("Kernel32.dll", SetLastError = true)]
static extern int GlobalUnlock(IntPtr Handle);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
#endregion
public BadDragDrop()
{
InitializeComponent();
textBox1.AllowDrop = true;
}
private void button1_Click(object sender, EventArgs e)
{
string textToSet = "Hello world\0";
IntPtr p = Marshal.AllocHGlobal(textToSet.Length);
Marshal.Copy(textToSet.Select(c => (byte)c).ToArray(), 0, p, textToSet.Length);
int success = GlobalUnlock(p);
SendMessage(textBox1.Handle, WM_SETTEXT, IntPtr.Zero, p);
Marshal.FreeHGlobal(p);
}
private void button2_Click(object sender, EventArgs e)
{
string filePath = #"C:\Windows\win.ini" + "\0\0";
DROPFILES s = new DROPFILES()
{
size = Marshal.SizeOf<DROPFILES>(),
pt = new POINT() { X = 10, Y = 10 },
fND = 0,
WIDE = 0,
};
int wparamLen = s.size + filePath.Length;
IntPtr p = Marshal.AllocHGlobal(wparamLen);
int iSuccess = GlobalLock(p);
Marshal.StructureToPtr(s, p, false);
Marshal.Copy(filePath.Select(c => (byte)c).ToArray(), 0, p + s.size, filePath.Length);
iSuccess = GlobalUnlock(p);
var verify = new byte[wparamLen];
Marshal.Copy(p, verify, 0, wparamLen);
var ipSuccess = SendMessage(textBox1.Handle, WM_DROPFILES, p, IntPtr.Zero);
Marshal.FreeHGlobal(p);
}
private void textBox1_DragDrop(object sender, DragEventArgs e)
{
MessageBox.Show(this, "Drag drop!");
}
private void textBox1_DragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Copy;
}
}
}
The reason you don't see your MessageBox appear is likely because the TextBox doesn't handle WM_DROPFILES messages to begin with. It implements its drop support using OLE Drag&Drop instead by implementing the IDropTarget interface (see Drag and Drop Overview in the WPF documentation).
WM_DROPFILES has been deprecated ever since DoDragDrop() was introduced way back in Windows 95. OLE Drag&Drop has been the preferred way to implement drag&drop on Windows for a very long time. WM_DROPFILES is still supported by Windows (but not .NET) but only for backwards compatibility with legacy apps.
Dragging items from Windows Explorer to other apps uses OLE Drag&Drop under the hood, even if the receiver doesn't implement OLE Drag&Drop.
If you drag&drop an IDataObject onto a window that has had RegisterDragDrop() called on it (like your TextBox has), the IDataObject will be passed to the window's IDropTarget interface for handling.
If you drag&drop an IDataObject onto a window that doesn't implement IDropTarget, but has had DragAcceptFiles() called on it, or at least has the WS_EX_ACCEPTFILES window style, Windows will generate a WM_DROPFILES message if the IDataObject contains CF_HDROP data in it.
So, to do what you are attempting with your TextBox, you need to implement the IDropSource and IDataObject interfaces, and then call DoDragDrop() with them. Let Windows handle the actual dragging and dropping for you. See Shell Clipboard Formats and Handling Shell Data Transfer Scenarios.
Now, with that said, if you are still determined to send a WM_DROPFILES message to another process (which your question says is your ultimate goal), you can do that, and Windows will marshal your DROPFILES struct across process boundaries for you, but you need to use PostMessage() instead of SendMessage() (not sure why, only that it is required), and be sure to free the DROPFILES only if PostMessage() fails. Windows will free the DROPFILES for you if PostMessage() is successful.
But even then, there is no guarantee that the receiving process actually handles WM_DROPFILES messages (and even if it does, your manual WM_DROPFILES message might get blocked by UIPI, unless the receiver calls ChangeWindowMessageFilter/Ex() on itself to allow WM_DROPFILES and WM_COPYGLOBALDATA messages). The receiver might be expecting IDataObject instead. If the receiver even supports drag&drop at all.
Okay, so you know how in Windows Vista and Windows 7 MS changed the Hand Cursor (the one that shows up when you hover over a hyperlink), and added more detail to it so it's antialiased and nice and smooth around the edges?
Well, why isn't it like that in Windows Forms apps?
I'm sick off looking at a crappy hand cursor that looks like it was drawn by a caveman.
Is there a way to programmatically tell it to display the one that's actually installed in the system? I looked in the Cursors folder in my Windows directory, and the old hand cursor isn't even there! So why is WinForms still using the old one? How can I 'upgrade' it?
Yes, the WinForms controls still use the old-school hand cursor, as shipped with Windows 98/2000. It lacks the anti-aliasing effects that the one included with the Aero cursors does. This is because the .NET Framework includes its own hard-coded cursor, which it uses instead of the system default. I presume this is because early versions of .NET were targeting operating systems like Windows 95 that didn't come bundled with this cursor, but haven't done the archaeology to prove it.
Fortunately, it's easy enough to force it to use the right one. You just have to tell the operating system you want it to use the default hand cursor, and then it will be correct no matter what version of Windows the user runs your program on, and even if they've changed their mouse cursors from the default theme.
The simplest way of doing that is to subclass the existing control, override the WndProc function to intercept the WM_SETCURSOR message, and tell it to use the system IDC_HAND cursor. You just need a little bit of P/Invoke magic.
The following code is an example of how that might look using the LinkLabel control:
public class LinkLabelEx : LinkLabel
{
private const int WM_SETCURSOR = 0x0020;
private const int IDC_HAND = 32649;
[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
[DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern IntPtr SetCursor(IntPtr hCursor);
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SETCURSOR)
{
// Set the cursor to use the system hand cursor
SetCursor(LoadCursor(IntPtr.Zero, IDC_HAND));
// Indicate that the message has been handled
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
}
Excuse me for resurrecting a year-old thread!!!
After messing around with the original solution and taking a look at the reflected LinkLabel source code, I "finally" found a quick yet clean way of doing it :
using System.Runtime.InteropServices;
namespace System.Windows.Forms {
public class LinkLabelEx : LinkLabel {
private const int IDC_HAND = 32649;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
private static readonly Cursor SystemHandCursor = new Cursor(LoadCursor(IntPtr.Zero, IDC_HAND));
protected override void OnMouseMove(MouseEventArgs e) {
base.OnMouseMove(e);
// If the base class decided to show the ugly hand cursor
if(OverrideCursor == Cursors.Hand) {
// Show the system hand cursor instead
OverrideCursor = SystemHandCursor;
}
}
}
}
This class actually does what we want: It shows the proper system hand cursor without flickering and does this only on the LinkArea of the control.
This post solves problems of the other posts:
It respects to the link location and shows the hand just when cursor is on link
It doesn't flicker on mouse move
You need to change the cursor to system hand cursor. To do so, you need to handle WM_SETCURSOR and check if OverrideCursor is Cursors.Hand then change it to the system cursor by calling SetCursor:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class MyLinkLabel : LinkLabel
{
const int IDC_HAND = 32649;
const int WM_SETCURSOR = 0x0020;
const int HTCLIENT = 1;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
static extern IntPtr SetCursor(HandleRef hcursor);
static readonly Cursor SystemHandCursor =
new Cursor(LoadCursor(IntPtr.Zero, IDC_HAND));
protected override void WndProc(ref Message msg)
{
if (msg.Msg == WM_SETCURSOR)
WmSetCursor(ref msg);
else
base.WndProc(ref msg);
}
void WmSetCursor(ref Message m)
{
if (m.WParam == (IsHandleCreated ? Handle : IntPtr.Zero) &&
(unchecked((int)(long)m.LParam) & 0xffff) == HTCLIENT) {
if (OverrideCursor != null) {
if (OverrideCursor == Cursors.Hand)
SetCursor(new HandleRef(SystemHandCursor, SystemHandCursor.Handle));
else
SetCursor(new HandleRef(OverrideCursor, OverrideCursor.Handle));
}
else {
SetCursor(new HandleRef(Cursor, Cursor.Handle));
}
}
else {
DefWndProc(ref m);
}
}
}
Sorry for getting this old post back, but i also have some kind of solution for this.
If you need to apply the systemcursor application wide without touching old controls, use this at applicationstart:
private static void TrySetCursorsDotHandToSystemHandCursor()
{
try
{
typeof(Cursors).GetField("hand", BindingFlags.Static | BindingFlags.NonPublic)
.SetValue(null, SystemHandCursor);
}
catch { }
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
private static readonly Cursor SystemHandCursor = new Cursor(LoadCursor(IntPtr.Zero, 32649 /*IDC_HAND*/));
Doing that without creating a new control wee need to change the Control's Cursor AND create a custom linklabel or else it wouldn't work
we create the custom linklabel by adding a label changing the font underline and changing it's fore color and add an click event
Private Const IDC_HAND As Integer = 32649
<DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)>
Private Shared Function LoadCursor(ByVal hInstance As IntPtr, ByVal lpCursorName As Integer) As IntPtr
End Function
Private Shared ReadOnly SystemHandCursor As Cursor = New Cursor(LoadCursor(IntPtr.Zero, IDC_HAND))
'add the cursor to custom linklabel
CustomLinkLabel1.Cursor = SystemHandCursor
sorry only have vb .net code you might use an online converter
EDIT: some code was missing
I couldn't find any solution except moving the cursor by Cursor class, clicking with mouse_event then moving the cursor to its old position. I am playing with SendInput function right now but still no chance for a good solution. Any advice?
You should use Win32 API.
Use pInvoked SendMessage from user32.dll
pInvoked Function
Then read about mouse events:
Mouse Input on msdn
And then read about: System events and Mouse Mess.......
Also there is lots of info:
Info
Here's an example following the approach Hooch suggested.
I created a form which contains 2 buttons. When you click upon the first button, the position of the second button is resolved (screen coördinates). Then a handle for this button is retrieved. Finally the SendMessage(...) (PInvoke) function is used to send a click event without moving the mouse.
public partial class Form1 : Form
{
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg,
IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "WindowFromPoint",
CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr WindowFromPoint(Point point);
private const int BM_CLICK = 0x00F5;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Specify the point you want to click
var screenPoint = this.PointToScreen(new Point(button2.Left,
button2.Top));
// Get a handle
var handle = WindowFromPoint(screenPoint);
// Send the click message
if (handle != IntPtr.Zero)
{
SendMessage( handle, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
}
}
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show("Hi", "There");
}
}
I am writing a small project where I would like to make use of drag and drop functionalty to ease some of the operations for the end user. To make the application a little more appealing, I would like to display the object being dragged. I have found some resources with WPF, but I don't know any WPF, so it becomes a bit tough to bite down on that whole subject for this single task. I would like to know how this can be done with "regular" C# Windows Forms. So far, all drag drop tutorials I've found just talk about the drop effects which is just a preset of a few icons.
WPF sounds like something I want to learn after this project.
The blog link provided by #Jesper gives the two or three key nuggets of info, but I think it is worth bringing it into S.O. for posterity.
Set up the custom cursor
The code below allows you to use an arbitrary image for your cursor
public struct IconInfo
{
public bool fIcon;
public int xHotspot;
public int yHotspot;
public IntPtr hbmMask;
public IntPtr hbmColor;
}
[DllImport("user32.dll")]
public static extern IntPtr CreateIconIndirect(ref IconInfo icon);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);
public static Cursor CreateCursor(Bitmap bmp, int xHotSpot, int yHotSpot)
{
IconInfo tmp = new IconInfo();
GetIconInfo(bmp.GetHicon(), ref tmp);
tmp.xHotspot = xHotSpot;
tmp.yHotspot = yHotSpot;
tmp.fIcon = false;
return new Cursor(CreateIconIndirect(ref tmp));
}
Set up the drag and drop event handling
This is well covered in other tutorials and answers. The specific events we are concerned about here are GiveFeedback and DragEnter, on any control where you want the custom cursor to apply.
private void DragSource_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
e.UseDefaultCursors = 0;
}
private void DragDest_DragEnter(object sender, DragEventArgs e)
{
Cursor.Current = CreateCursor(bitmap, 0, 0);
}
You need to hide the default cursor and create your own window containing your custom image and then move that window with the position of the mouse.
You might also take a look at http://web.archive.org/web/20130127145542/http://www.switchonthecode.com/tutorials/winforms-using-custom-cursors-with-drag-drop
UPDATE 2015-11-26
Updated the link to point to archive.org's last snapshot
I've been assigned to make a custom grid control in C# with windows forms. One thing I'm unsure of is how to handle showing a blinking cursor (caret) to indicate where cell editing is taking place and the next character will be shown.
Does anyone know how this is done with the standard textbox? Is there a standard framework construct that will do this for me?
Obviously I can setup a timer and draw the cursor myself, but I was wondering if there was a better option. Note that this is a completely user drawn control, not a UserControl derivative and that subclassing an existing class is not an option for various reason.
Here you go:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public class MyWidget : Control {
public MyWidget() {
this.BackColor = Color.Yellow;
}
protected override void OnGotFocus(EventArgs e) {
CreateCaret(this.Handle, IntPtr.Zero, 2, this.Height - 2);
SetCaretPos(2, 1);
ShowCaret(this.Handle);
base.OnGotFocus(e);
}
protected override void OnLostFocus(EventArgs e) {
DestroyCaret();
base.OnLostFocus(e);
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool CreateCaret(IntPtr hWnd, IntPtr hBmp, int w, int h);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetCaretPos(int x, int y);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool ShowCaret(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool DestroyCaret();
}
I'll gladly pass the buck on figuring out where to put it.
The MSDN reference about Carets is here. The last time I looked (which was in 2.0 of the framework) carets weren't available as a managed API: and so you need to use the unmanaged API, or paint your own caret.
One thing to remember, when you implement a caret, is that you should not show it whenever your control doesn't have the focus (only one control at a time on the user's desktop, i.e. the control which has the input focus, should ever be showing the input caret).
Why re-invent the wheel? Just display a textbox when the grid needs editing.
Ok, I see you you use custom drawing, but what prevents you from placing a textbox on over it?
If you wanna go the hard way, Microsoft does have some old libraries that can provide a virtual textarea (or something like that, been a very long).