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).
Related
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);
}
I'm trying to dynamically change the view of a panel that contains buttons in my winforms application using the mousewheel. I'm basically changing the location of the panel using the code below however whenever I use the mousewheel to scroll, the scrollbars can be seen. They sort of flask/ flicker into view when I change the location. Is there any way to stop the scrollbars from appearing?
location -= 40;
this.pnl.VerticalScroll.Value = location;
this.pnl.AutoScrollPosition = new Point(0, location);
You cannot simple change it from winforms, since it is being automatically displayed by windows. There is a not so great solution for this issue.I had a similar requirement and what I used was this :
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);
private enum ScrollBarDirection
{
SB_HORZ = 0,
SB_VERT = 1,
SB_CTL = 2,
SB_BOTH = 3
}
protected override void WndProc(ref System.Windows.Forms.Message m)
{
ShowScrollBar(panel1.Handle, (int)ScrollBarDirection.SB_VERT, false);
base.WndProc(ref m);
}
Import the win32 dll user32.dll
Call the ShowScrollBar method in it with required parameters as shown in above example
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 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 realize that this would be COMPLETELY bad practice in normal situations, but this is just for a test app that needs to be taking input from a bar code scanner (emulating a keyboard). The problem is that I need to start up some scripts while scanning, so I need the window to regain focus directly after I click the script to run it. I've tried using Activate(), BringToFront(), Focus() as well as some Win32 calls like SetForegroundWindow(), Setcapture() and SetActiveWindow()... however the best I can get any of them to do is to make the taskbar item start blinking to tell me that it wants to have focus, but something is stopping it. BTW, I'm running this on XP SP2 and using .NET 2.0.
Is this possible?
Edit: To clarify, I am running the scripts by double-clicking on them in explorer. So I need it to steal focus back from explorer and to the test app.
I struggled with a similar problem for quite a while. After much experimentation and guessing, this is how I solved it:
// Get the window to the front.
this.TopMost = true;
this.TopMost = false;
// 'Steal' the focus.
this.Activate();
Visibility
Make the window a "Top-Most" window. This is the way the Task-Manager can remain on top of other windows. This is a property of a Form and you make the form top-most (floating above other windows) by setting the value to true.
You shouldn't need to override any of the "Active window" behaviour with the top-most setting.
Focus
I asked a similar question previously here on StackOverflow and the answer would solve your problem. You can make the application use a low-level input hook and get notification of the key-codes coming from the scanner. This way, your application always gets these keys even though the application does not have focus.
You may need to enhance the solution to squash the key-codes so that they are not transmitted to the "in-focus" application (e.g. notepad).
Since Windows 2000, there is no official mechanism for an application to grab focus without direct intervention of the user. Peeking at the input streams through the RawInputDevices hook is the only sensible way to go.
A number of articles may help (C# implementations)
RawInput article on CodeProject
MSDN documentation of RawInput
I had a similar problem and found the following to do the trick. Adapted to C# from here
// force window to have focus
uint foreThread = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero);
uint appThread = GetCurrentThreadId();
const uint SW_SHOW = 5;
if (foreThread != appThread)
{
AttachThreadInput(foreThread, appThread, true);
BringWindowToTop(form.Handle);
ShowWindow(form.Handle, SW_SHOW);
AttachThreadInput(foreThread, appThread, false);
}
else
{
BringWindowToTop(form.Handle);
ShowWindow(form.Handle, SW_SHOW);
}
form.Activate();
EDIT: Here are the necessary PInvoke definitions for C#:
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
/// <summary>The GetForegroundWindow function returns a handle to the foreground window.</summary>
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern bool BringWindowToTop(HandleRef hWnd);
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
The way I approached this problem was to spawn another thread whose only purpose was to ensure the Form is TopMost and has focus at all times. This code will make all other applications unusable while it is running, which is what I needed for my specific applications. You can add in a Sleep in keepFocus or have some other event trigger it.
using System.Threading; // be sure to include the System.Threading namespace
//Delegates for safe multi-threading.
delegate void DelegateGetFocus();
private DelegateGetFocus m_getFocus;
//Constructor.
myForm()
{
m_getFocus = new DelegateGetFocus(this.getFocus); // initialise getFocus
InitializeComponent();
spawnThread(keepFocus); // call spawnThread method
}
//Spawns a new Thread.
private void spawnThread(ThreadStart ts)
{
try
{
Thread newThread = new Thread(ts);
newThread.IsBackground = true;
newThread.Start();
}
catch(Exception e)
{
MessageBox.Show(e.Message, "Exception!", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
//Continuously call getFocus.
private void keepFocus()
{
while(true)
{
getFocus();
}
}
//Keeps Form on top and gives focus.
private void getFocus()
{
//If we need to invoke this call from another thread.
if (this.InvokeRequired)
{
try
{
this.Invoke(m_getFocus, new object[] { });
}
catch (System.ObjectDisposedException e)
{
// Window was destroyed. No problem but terminate application.
Application.Exit();
}
}
//Otherwise, we're safe.
else
{
this.TopMost = true;
this.Activate();
}
}
}
You might try focusing on a specific input, or try the setting .TopMost property to true (and then unsetting it again).
But I suspect your problem is that these methods all just place messages in the windows event queue, and your program has to wait for all existing events to finish processing before it will handle that one and focus the app.