Can't receive WM_NCHITTEST over transparent area in WinForms - c#

I've a question about transparency in WinForms, but first things first.
I have just created a super-simple application in WinForms with transparent "body". I've only changed the color of the default Form. Designer code:
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form 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()
{
this.SuspendLayout();
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(192)))), ((int)(((byte)(192)))));
this.ClientSize = new System.Drawing.Size(538, 312);
this.Name = "Form1";
this.Text = "Form1";
this.TransparencyKey = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(192)))), ((int)(((byte)(192)))));
this.ResumeLayout(false);
}
#endregion
}
So in runtime it's basically just a frame + caption. In Form1.cs the only thing I've added was overridden WndProc function where I did:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
const UInt32 WM_NCHITTEST = 0x0084;
const UInt32 WM_MOUSEMOVE = 0x0200;
if (m.Msg == WM_NCHITTEST || m.Msg == WM_MOUSEMOVE)
{
Point screenPoint = new Point(m.LParam.ToInt32());
Point clientPoint = this.PointToClient(screenPoint);
string position = "Position: X=" + clientPoint.X.ToString() + "; Y=" + clientPoint.Y.ToString();
Console.WriteLine(position);
this.Text = position;
}
base.WndProc(ref m);
}
}
In my mind it should work this way: It simply shows mouse pointer position on the window in window's title. BUT, what is super important - position over transparent area should be shown as well. The "Form" inside my application's window is transparent but it's still a part of my application, right? And here comes my question, because not on every machine it works the same.
There are two cases (the same application!!):
1. I can click on stuff under my application (through this transparent area)
2. The window is just transparent. I can't click on stuff like icons or whatever which are under transparent area.
In first case WM_NCHITTEST message is not send at all (over transparent area) in my WndProc overridden function. Transparent area (so the Form) is literally a hole in my app. In second case Form is transparent but in Window's title I can see my pointer position, so simply WM_NCHITTEST message is being send.
Can anyone explain me where could be a problem? This is the same application.
I mean, If you want to create an app with a "hole" or with a "glass" or "window"? You have to control it somehow..

Related

How can I bring a second (child) form to the front of the windows z-order along with main (parent) form

As the title suggests I have a situation where I want to bring a secondary form (FormB which does not have a taskbar icon) to the front (z-order) when my main form (FormA) is activated (has focus).
For example, I have my program running with two forms (FormA and FormB). I then open up notepad maximized (just to cover both of the forms [FormA and FormB] that my application contains on the screen). Then I click the Windows Taskbar item for my application (which is FormA) at the bottom of the screen to open my application back up. When that happens FormB is not shown and is left in the background of the z-order. I would like for both forms be brought to the front of all other forms.
In the end when restoring FormA like any normal application, FormB should be the same Z-ORDER minus ONE.
Current Source Example
public partial class FormA : Form
{
public FormA()
{
Log FormB = new FormB();
FormB.Show();
Log FormB = new FormB();
FormB.ShowDialog();
}
private void FormA_Activated(object sender, EventArgs e)
{
if (FormB.Visible)
{
FormB.Show();
}
}
}
I ended up finding out how to do this with a little trial and error. In hopes this helps someone else out in the future I wanted to post my findings here.
Because this isn't directly supported by the .NET Form class you need to use the the SetWindowPos API. Four things need to be done to make this happen. All of the source below can be added to your FORM's source (FormA).
As shown below the Usage (#4) is showing an example that I had using tool strip menu items (File, Edit, etc) at the top of my main form (FormA). Then the second form which is my log form (FormB) is considered the secondary form. This can be done with multiple forms (more than 2 like my example shows).
Note: All of my tool strip menu items have CheckOnClick set to TRUE. This way each time you select the menu items they are being turned on and off.
Declare The Enums
#region Enums
public enum SpecialWindowHandles
{
/// <summary>
/// Places the window at the top of the Z order.
/// </summary>
HWND_TOP = 0,
/// <summary>
/// Places the window at the bottom of the Z order. If the hWnd parameter identifies a topmost window, the window loses its topmost status and is placed at the bottom of all other windows.
/// </summary>
HWND_BOTTOM = 1,
/// <summary>
/// Places the window above all non-topmost windows. The window maintains its topmost position even when it is deactivated.
/// </summary>
HWND_TOPMOST = -1,
/// <summary>
/// Places the window above all non-topmost windows (that is, behind all topmost windows). This flag has no effect if the window is already a non-topmost window.
/// </summary>
HWND_NOTOPMOST = -2
}
[Flags]
public enum SetWindowPosFlags : uint
{
/// <summary>
/// If the calling thread and the thread that owns the window are attached to different input queues, the system posts the request to the thread that owns the window. This prevents the calling thread from blocking its execution while other threads process the request.
/// </summary>
SWP_ASYNCWINDOWPOS = 0x4000,
/// <summary>
/// Prevents generation of the WM_SYNCPAINT message.
/// </summary>
SWP_DEFERERASE = 0x2000,
/// <summary>
/// Draws a frame (defined in the window's class description) around the window.
/// </summary>
SWP_DRAWFRAME = 0x0020,
/// <summary>
/// Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is sent only when the window's size is being changed.
/// </summary>
SWP_FRAMECHANGED = 0x0020,
/// <summary>
/// Hides the window.
/// </summary>
SWP_HIDEWINDOW = 0x0080,
/// <summary>
/// Does not activate the window. If this flag is not set, the window is activated and moved to the top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter).
/// </summary>
SWP_NOACTIVATE = 0x0010,
/// <summary>
/// Discards the entire contents of the client area. If this flag is not specified, the valid contents of the client area are saved and copied back into the client area after the window is sized or repositioned.
/// </summary>
SWP_NOCOPYBITS = 0x0100,
/// <summary>
/// Retains the current position (ignores X and Y parameters).
/// </summary>
SWP_NOMOVE = 0x0002,
/// <summary>
/// Does not change the owner window's position in the Z order.
/// </summary>
SWP_NOOWNERZORDER = 0x0200,
/// <summary>
/// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent window uncovered as a result of the window being moved. When this flag is set, the application must explicitly invalidate or redraw any parts of the window and parent window that need redrawing.
/// </summary>
SWP_NOREDRAW = 0x0008,
/// <summary>
/// Same as the SWP_NOOWNERZORDER flag.
/// </summary>
SWP_NOREPOSITION = 0x0200,
/// <summary>
/// Prevents the window from receiving the WM_WINDOWPOSCHANGING message.
/// </summary>
SWP_NOSENDCHANGING = 0x0400,
/// <summary>
/// Retains the current size (ignores the cx and cy parameters).
/// </summary>
SWP_NOSIZE = 0x0001,
/// <summary>
/// Retains the current Z order (ignores the hWndInsertAfter parameter).
/// </summary>
SWP_NOZORDER = 0x0004,
/// <summary>
/// Displays the window.
/// </summary>
SWP_SHOWWINDOW = 0x0040,
}
#endregion
Declare The APIs
#region APIs
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr hWnd, SpecialWindowHandles hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
#endregion
Create The Functions
#region Functions
private void BringVisibleWindowsToFront()
{
//Get parent form handle
IntPtr hndParentWindow = this.Handle;
//Bring all childern forms to the front
foreach (Form frmChild in Application.OpenForms)
{
//If form is not this form
if (frmChild.Handle != this.Handle)
{
//If form is visible and not minimized
if (frmChild.WindowState != FormWindowState.Minimized && frmChild.Visible == true)
SetWindowPos(frmChild.Handle, SpecialWindowHandles.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
}
}
//Set the parent form to the top most z order
SetWindowPos(hndParentWindow, SpecialWindowHandles.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
}
private void SetAllFormWindowsAlwaysOnTop(bool active)
{
SpecialWindowHandles OnTop = (active ? SpecialWindowHandles.HWND_TOPMOST : SpecialWindowHandles.HWND_NOTOPMOST);
//Get parent form handle
IntPtr hndParentWindow = this.Handle;
//Bring all childern forms to the front
foreach (Form frmChild in Application.OpenForms)
{
//If form is not this form
if (frmChild.Handle != this.Handle)
{
//If form is visible and not minimized
if (frmChild.WindowState != FormWindowState.Minimized && frmChild.Visible == true)
SetWindowPos(frmChild.Handle, OnTop, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
}
}
//Set the parent form to the top most z order
SetWindowPos(hndParentWindow, OnTop, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
}
#endregion
Usage (tool strip menu items example)
#region ToolStripMenuItems events
//Shows or hides the log form (FormB)
private void showLogToolStripMenuItem_Click(object sender, EventArgs e)
{
if (showLogToolStripMenuItem.Checked)
{
FormB.ShowLog(true);
SetAllFormWindowsAlwaysOnTop(alwaysOnTopToolStripMenuItem.Checked);
}
else
FormB.ShowLog(false);
}
//Sets all the forms (in this case FormA and FormB) to always be on top or not
private void alwaysOnTopToolStripMenuItem_Click(object sender, EventArgs e)
{
SetAllFormWindowsAlwaysOnTop(alwaysOnTopToolStripMenuItem.Checked);
}
#endregion

How can I get the "touchscreen keyboard" in a Windows 10 tablet to stop opening twice in our WPF app?

Please note that I have spent a lot of time searching through online posts including on SO but have been unsuccessful so far.
The problem is with the touchscreen keyboard that started being opened automatically because of the Windows 10 Touch Keyboard and Handwriting Panel Service whenever somebody clicks on a textbox whereas before with Windows 8.1 the keyboard was opened only due to the C# API calls from within our Structures Asset Management (SAM) app. Thus with Windows 10, the virtual keyboard was being opened twice whenever someone clicks on a textbox-—once because of the SAM C# API call and once due to the Touch Keyboard and Handwriting Panel Service.
Please note that we have attempted to disable the Touch Keyboard and Handwriting Panel Service but that then causes the touchscreen keyboard to not appear at all.
Normally, it would be fine to just let the OS open this touchscreen keyboard using the Touch Keyboard and Handwriting Panel Service but the problem is we need to sometimes show the touchscreen keyboard and other times show only a numeric keypad, so just relying on the Windows service is not an option.
Here are the classes that worked successfully for controlling the keyboard in Windows 8.1:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Input;
namespace Cpr.Apps.Sam.Controls
{
/// <summary>
/// Shows or hides the touch keyboard on tablets.
/// </summary>
public class TouchKeyboard
{
/// <summary>
/// The touch keyboard app's file path.
/// </summary>
private static string _touchKeyboardAppFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles), #"Microsoft Shared\ink\TabTip.exe");
/// <summary>
/// Set to true if the app is currently running on a touch device and false otherwise.
/// </summary>
private static bool _isTouchScreen = Tablet.TabletDevices.Cast<TabletDevice>().Any(tabletDevice => tabletDevice.Type == TabletDeviceType.Touch);
/// <summary>
/// The keyboard visible flag.
/// </summary>
/// <remarks>
/// This flag only keeps track of the keyboard's visibility if it was set using this class.
/// </remarks>
private static bool _isKeyboardVisible;
/// <summary>
/// The delay after which the keyboard will be hidden, in seconds.
/// </summary>
/// <remarks>
/// The keyboard is not hidden immediately when the associated input field loses the keyboard focus, so that it will
/// not flicker if another input field with this behavior obtains the keyboard focus immediately afterwards.
/// </remarks>
private const double KEYBOARD_HIDE_DELAY = 0.25;
/// <summary>
/// The number of milliseconds per second. Used for time conversions.
/// </summary>
private const long MILLISECONDS_PER_SECOND = 1000;
/// <summary>
/// True if the current device has a touch screen and false otherwise.
/// </summary>
public static bool IsTouchScreen
{
get { return _isTouchScreen; }
}
/// <summary>
/// Shows the touch keyboard if the app is running on a touch device.
/// </summary>
/// <remarks>
/// This method does nothing if the app is not currently running on a touch device.
/// </remarks>
public static void Show()
{
// check if the app is running on a touch device
if (_isTouchScreen && _touchKeyboardAppFilePath != null)
{
try
{
// launch the touch keyboard app
Process.Start(_touchKeyboardAppFilePath);
// set the keyboard visible flag
_isKeyboardVisible = true;
}
catch (Exception)
{
// do nothing
}
}
}
/// <summary>
/// Hides the touch keyboard if the app is running on a touch device.
/// </summary>
/// <remarks>
/// This method does nothing if the app is not currently running on a touch device.
/// </remarks>
public static void Hide()
{
// check if the app is running on a touch device
if (_isTouchScreen)
{
// reset the keyboard visible flag
_isKeyboardVisible = false;
// hide the keyboard after a delay so that if another input field with this behavior obtains the focus immediately,
// the keyboard will not flicker
Timer timer = null;
timer = new Timer((obj) =>
{
// check if the keyboard should still be hidden
if (!_isKeyboardVisible)
{
// check if the keyboard is visible
var touchKeyboardWindowHandle = FindWindow("IPTip_Main_Window", null);
if (touchKeyboardWindowHandle != _nullPointer)
{
// hide the keyboard
SendMessage(touchKeyboardWindowHandle, WM_SYSCOMMAND, SC_CLOSE, _nullPointer);
}
}
// release the timer
timer.Dispose();
}, null, (long)(KEYBOARD_HIDE_DELAY * MILLISECONDS_PER_SECOND), Timeout.Infinite);
}
}
// Win32 null pointer parameter
private static IntPtr _nullPointer = new IntPtr(0);
// Win32 command from the Window menu
private const uint WM_SYSCOMMAND = 0x0112;
// Win32 command to close a window
private static IntPtr SC_CLOSE = new IntPtr(0xF060);
// Win32 API to get a window reference
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr FindWindow(string sClassName, string sAppName);
// Win32 API to send a message to a window
[DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
}
}
And:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using Cpr.Apps.Sam.Controls;
namespace Cpr.Apps.Sam.Styles.Behaviors
{
/// <summary>
/// Behavior that shows the touch keyboard (on tablets only) when the associated control gets the keyboard focus.
/// </summary>
public class ControlShowTouchKeyboardOnFocusBehavior : Behavior<Control>
{
protected override void OnAttached()
{
base.OnAttached();
// add the event handlers
WeakEventManager<Control, KeyboardFocusChangedEventArgs>.AddHandler(AssociatedObject, "GotKeyboardFocus", OnGotKeyboardFocus);
WeakEventManager<Control, KeyboardFocusChangedEventArgs>.AddHandler(AssociatedObject, "LostKeyboardFocus", OnLostKeyboardFocus);
}
/// <summary>
/// Called when the associated control receives the keyboard focus.
/// </summary>
/// <param name="sender">The object triggering this event.</param>
/// <param name="e">The event parameters.</param>
private void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
// show the touch keyboard
TouchKeyboard.Show();
var textBox = sender as TextBox;
if (textBox != null)
textBox.SelectionStart = Math.Max(0, textBox.Text.Length); //Move the caret to the end of the text in the text box.
}
/// <summary>
/// Called when the associated control loses the keyboard focus.
/// </summary>
/// <param name="sender">The object triggering this event.</param>
/// <param name="e">The event parameters.</param>
private void OnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
// hide the touch keyboard
TouchKeyboard.Hide();
}
}
}
Basically, my question is, how can I make the touchscreen keyboard in Windows 10 behave the same way it did in Windows 8.1? Are there some configuration values I can change on the OS settings, or do I need to change something in the registry? What are the differences between the Touch Panel and Handwriting Service in Windows 8.1 and Windows 10? TIA.
UPDATE:
Please note that I have explored using the "On Screen Keyboard" which I believe is based on COM instead of the newer "Touchscreen Keyboard" but that did not help because ultimately because this COM keyboard requires admin privileges to close or minimize it. This is what I attempted with the "On Screen Keyboard":
using System;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using Cpr.Apps.Sam.Controls;
namespace Cpr.Apps.Sam.Styles.Behaviors
{
/// <summary>
/// Behavior that shows the touch keyboard (on tablets only) when the associated control gets the keyboard focus.
/// </summary>
public class ControlShowTouchKeyboardOnFocusBehavior : Behavior<Control>
{
[DllImport("User32")]
private static extern int ShowWindow(int hwnd, int nCmdShow);
private const int SW_HIDE = 0;
private const int SW_RESTORE = 9;
private int hWnd;
private readonly string _USB = "USB";
private readonly string _keyboard = #"osk.exe";
private Process _keyboardProcess = null;
private ProcessStartInfo _startInfo = null;
protected override void OnAttached()
{
base.OnAttached();
// add the event handlers
WeakEventManager<Control, KeyboardFocusChangedEventArgs>.AddHandler(AssociatedObject, "GotKeyboardFocus", OnGotKeyboardFocus);
WeakEventManager<Control, KeyboardFocusChangedEventArgs>.AddHandler(AssociatedObject, "LostKeyboardFocus", OnLostKeyboardFocus);
}
private bool GetKeyboardPresent()
{
bool flag = false;
foreach (ManagementBaseObject managementBaseObject in new ManagementObjectSearcher("Select * from Win32_Keyboard").Get())
{
foreach (PropertyData property in managementBaseObject.Properties)
{
if (Convert.ToString(property.Value).Contains(this._USB))
{
flag = true;
break;
}
}
}
return flag;
}
/// <summary>
/// Called when the associated control receives the keyboard focus.
/// </summary>
/// <param name="sender">The object triggering this event.</param>
/// <param name="e">The event parameters.</param>
private void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
// show the touch keyboard
// TouchKeyboard call here not needed in Windows 10 because of “Touch Keyboard and Handwriting Panel Service” causes virtual keyboard to show up twice. Use on screen keyboard instead.
//TouchKeyboard.Show();
if (!this.GetKeyboardPresent())
{
//_keyboardProcess.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
Process[] pocesses = Process.GetProcessesByName(_keyboard);
if (pocesses.Any())
{
foreach (var proc in pocesses)
{
hWnd = (int) proc.MainWindowHandle;
ShowWindow(hWnd, SW_RESTORE);
}
}
else
{
_startInfo = new ProcessStartInfo(_keyboard);
_keyboardProcess = new Process
{
EnableRaisingEvents = true,
StartInfo = _startInfo
};
_keyboardProcess.Exited += new EventHandler(ProcessExited);
//Don't need this because it is for parent process: AppDomain.CurrentDomain.ProcessExit += (a, b) => _keyboardProcess.Kill();
_keyboardProcess.Start();
}
}
var textBox = sender as TextBox;
if (textBox != null)
textBox.SelectionStart = Math.Max(0, textBox.Text.Length); //Move the caret to the end of the text in the text box.
}
/// <summary>
/// Called when the associated control loses the keyboard focus.
/// </summary>
/// <param name="sender">The object triggering this event.</param>
/// <param name="e">The event parameters.</param>
private void OnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
// hide the touch keyboard
// TouchKeyboard call here not needed in Windows 10 because of “Touch Keyboard and Handwriting Panel Service” causes virtual keyboard to show up twice. Use on screen keyboard instead.
//TouchKeyboard.Hide();
if (!GetKeyboardPresent() && _keyboardProcess != null)
{
//Keyboard doesn't minimize if I call Kill() or SW_HIDE, and instead this simply causes the textbox to lose focus so commented out this code
//Process[] pocesses = Process.GetProcessesByName("osk");
//for (int i = 0; i < pocesses.Count(); i++)
//{
// var proc = pocesses[i];
// proc.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
//hWnd = (int)proc.MainWindowHandle;
//ShowWindow(hWnd, SW_HIDE);
//}
//Task.Delay(500);
}
}
private void ProcessExited(object sender, System.EventArgs e)
{
Debug.WriteLine("Exited _keyboardProcess");
_keyboardProcess = null;
}
}
}
UPDATE 2:
It looks like I may need to port my app from WPF to WinRT to get it to work on Windows 10: See https://learn.microsoft.com/en-us/windows/uwp/porting/ where it says
Move from WPF and Silverlight to WinRT
in
Related Topics
Ended up using a custom WPF software keyboard built in C# instead of the Windows “Touch Keyboard and Handwriting Panel Service” Touchscreen Keyboard and the Onscreen Keyboard.

Why would commenting out a line of code change whether a *previous* line of code is executed?

With this code:
using (pbDialog = new pbDialogs())
{
ProgressBar = new frmProgress(this, false);
ProgressBar.SetProgressLabelText("Inventory Data");
MessageBox.Show("Set progress label text to Inventory data");
typeProgress = (int) ProgressStates.ProgressQRY;
ProgressBar.label1.Text += " (Receiving)";
if (pbDialog != null)
{
MessageBox.Show("pbDialog is not null");
//pbDialog.ShowDialog(ProgressBar, this);
}
else
{
MessageBox.Show("pbDialog IS null");
ProgressBar.ShowDialog();
}
ProgressBar = null;
MessageBox.Show("Made it to compressDB()");
compressDB();
. . .
}
I see "Set progress label text to Inventory data"
then "pbDialog is not null"
then "Made it to compressDB()"
Nothing too odd there; but if I uncomment the line that is commented above, I see only "pbDialog is not null"
It is hanging for some reason as a result to the call to ShowDialog(); what is really strange, though, is that this prevents "Set progress label text to Inventory data" from displaying. Why would that be the case?
Note: I think the "pb" in the code stands for "peanut brittle" or some such; I'm pretty sure about the "brittle" part, anyway.
UPDATE
Yeah, the use of ShowDialog() with pbDialog is one of scads of examples that the original coder was practicing job security by obscurity - but then he [un]fortunately skedaddled, leaving in his wake a cesspool of spaghetti/eggshell code with no comments, misleading names and every sort of bizarre and convoluted, counterintuitive practice imaginable in the witches brew he purportedly considered a masterpiece of elegant design and clever-clever tricks.
pbDialog is an instance of a class (pbDialogs). Just to give you a taste of how macabre and convoluted and tangled it all is, here is that class:
public class pbDialogs : IDisposable
{
private static Form m_top;
public pbDialogs()
{
} // pbDialogs Constructor
public static void Activate( Form form )
{
form.Capture = true;
IntPtr hwnd = OpenNETCF.Win32.Win32Window.GetCapture();
form.Capture = false;
OpenNETCF.Win32.Win32Window.SetForegroundWindow( hwnd );
} // Activate
/// <summary>
/// This method makes ShowDialog work the way I want, I think.
/// </summary>
/// <remarks>
/// Here is what it does:
/// 1. Sets the caption of the new window to the same as the caller's.
/// 2. Clears the caption of the parent so it won't show up in the task list.
/// 3. When the ShowDialog call returns, brings the previous window
/// back to the foreground.
/// </remarks>
/// <param name="dialog"></param>
/// <param name="parent"></param>
public void ShowDialog( Form dialog, Control parent )
{
Control top = parent.TopLevelControl;
string caption = top.Text;
dialog.Text = caption;
top.Text = "--pending--"; // Don't show parent in task list
dialog.Activated += new EventHandler( form_Activated );
dialog.Closed += new EventHandler( form_Closed );
m_top = dialog; // New top-most form
dialog.ShowDialog();
m_top = (Form)top; // The top dialog just changed
dialog.Activated -= new EventHandler( form_Activated );
dialog.Closed -= new EventHandler( form_Closed );
top.Text = caption; // Make visible in task list again
Activate( (Form)top ); // And make it the active window
} // ShowDialog
/// <summary>
/// If one of our other windows, such as the main window,
/// receives an activate event, it will activate the current
/// top-most window instead.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void form_Activated( object sender, EventArgs e )
{
if( (m_top != null) && !(sender == m_top) ) // Is this the top-most window?
Activate( m_top ); // No, activate the top dialog
} // form_Activated
private static void form_Closed( object sender, EventArgs e )
{
m_top = null; // When you close the top dialog, it's not top anymore
} // form_Closed
#region IDisposable Members
public void Dispose()
{
// TODO: Add pbDialogs.Dispose implementation
}
#endregion
} // class pbDialogs
There is also a "related" ProgressBar -- a form which shares a file with pbDialogs, and whose instance variable is defined in the file that contains the code above thusly:
public static frmProgress ProgressBar;
This is definitely "whack-a-mole" code - if I make one small, seemingly innocuous change, all Dallas breaks loose in what even a semi-sane person would consider a completely unrelated part of the code.
This may be an indication of just how squirrelly this code/project is: I will make a new build after commenting out a couple of lines, and the size of the file will change from 400KB to 408, or from 412 to 408, etc. It's not normal behavior for an .exe to change that much in size (in a relative sense) with such a small change, is it?
UPATE 2
This, in frmProgress (which has both "public class frmProgress : System.Windows.Forms.Form" and "public class pbDialogs : IDisposable") scares me:
using System.Windows.Forms;
using OpenNETCF.Windows.Forms;
The second (OpenNETCF) is grayed out, indicating it's not really used, but it may be that it was previously used, and somehow that "Windows.Forms" code inadvertently got switched to "System" code, and that may be contributing to its current groundsquirellyness.
ShowDialog is generally a blocking call. The code will not continue past this until the dialog is closed.

Running a Windows application as a Command Line application

I am working on a migration tool that is primarily a Windows Forms application. What I want to do is provide the ability to run the application as a sort of command line utility where parameters can be passed in and the migration occurs completely void of the GUI. This seems straight forward enough and the entry point for my application looks like this:
[STAThread]
static void Main(string[] args)
{
if (args.Length == 0)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainWindow());
}
else
{
//Command Line Mode
Console.WriteLine("In Command Line Mode");
Console.ReadLine();
}
}
The problem I am running into is that when execution passes into the else block the text is not wrote back to the user at the command prompt which is problematic as I want to update the user at the command prompt as various executions complete. I could easily write a standalone console application but I was hoping to provide a single tool that allowed to different types of entries for a given scenario. Is what I am looking to do possible and, if so, how is it achieved?
Thanks!
Here is a topic that discusses this: http://social.msdn.microsoft.com/forums/en-US/Vsexpressvb/thread/875952fc-cd2c-4e74-9cf2-d38910bde613/
The key is calling AllocConsole function from kernel32.dll
The usual pattern for this would be to write the logic into a class library that you either call from a visual UI or from a command line application.
For example, if on the UI you accepted "Width", "Height" and "Depth" and then calculated volume you would put the calculation into the class library.
So you have either a Console app accepting three arguments, or a forms app with three inputs, and in both cases, they make the same call...
var volumeCalculations = new VolumeCalculations();
var volume = volumeCalculations.GetVolume(width, height, depth);
The console app is very thin and the forms app is very thin, because all they do is get the inputs to pass to the class library.
Here is completed runnable example. Compile with:
csc RunnableForm.cs RunnableForm.Designer.cs
RunnableForm.cs:
using System;
using System.Linq;
using System.Windows.Forms;
namespace Test
{
public partial class RunnableForm : Form
{
public RunnableForm()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("bang!");
}
[STAThread]
static void Main()
{
string[] args = Environment.GetCommandLineArgs();
// We'll always have one argument (the program's exe is args[0])
if (args.Length == 1)
{
// Run windows forms app
Application.Run(new RunnableForm());
}
else
{
Console.WriteLine("We'll run as a console app now");
Console.WriteLine("Arguments: {0}", String.Join(",", args.Skip(1)));
Console.Write("Enter a string: ");
string str = Console.ReadLine();
Console.WriteLine("You entered: {0}", str);
Console.WriteLine("Bye.");
}
}
}
}
RunnableForm.Designer.cs:
namespace Test
{
partial class RunnableForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form 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()
{
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(42, 42);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(153, 66);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// RunnableForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 261);
this.Controls.Add(this.button1);
this.Name = "RunnableForm";
this.Text = "RunnableForm";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button button1;
}
}

Tooltip that's not linked to any particular control

I'm trying to make something like a spellchecker, that will list possible words under the current caret position. I thought I would do this by creating a tooltip, moving it according to the caret's location, and changing the text inside the tooltip.
I'm having problems.
I'm trying to show the tooltip with tip.Show(form, x, y);
However, this app is running from the systray. It has no GUI elements aside from that? What do I use as the form parameter? the notifyIcon1, Form1, etc. do not work.
I would start with an example that displayed a static tooltip that moved along with my mouse cursor or something. Can someone point me in the right direction?
Thanks
You may be able to do this but not using a tooltip class as that is quite limiting, there is a fantastic tooltip helper called VXPLib, using html formatting (which I suppose would give your listing of words an edge - say in different colours). The VXPLib is a COM object (written in C++) but accessible from the .NET language and there is a wrapper that can do it for you along with code samples. I have tried them and they actually work and make it look nice...See here for more information.
Hope this helps,
Best regards,
Tom.
I posted an answer in this thread that uses a transparent, maximized for to simulate drawing a tooltip anywhere on the screen, including the desktop. Maybe it will help: Creating a tooltip from a system-tray only app
Edit: Copied the code over from the linked post for ease of reading :-)
Here you go, use a transparent, maximized form that you BringToFront() before showing the ToolTip
Form1 Code:
using System;
using System.Windows.Forms;
namespace SO_ToolTip
{
public partial class Form1 : Form
{
Random _Random = new Random();
ToolTip _ToolTip = new ToolTip();
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
BringToFront();
_ToolTip.Show("Blah blah... Blah blah... Blah blah...", this, _Random.Next(0, Width), _Random.Next(0, Height), 10000);
}
}
}
Form1 Designer Code: So you can see the forms properties:
namespace SO_ToolTip
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form 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()
{
this.components = new System.ComponentModel.Container();
this.timer1 = new System.Windows.Forms.Timer(this.components);
this.SuspendLayout();
//
// timer1
//
this.timer1.Enabled = true;
this.timer1.Interval = 1000;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 264);
this.ControlBox = false;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Form1";
this.Opacity = 0;
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.WindowState = System.Windows.Forms.FormWindowState.Maximized;
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Timer timer1;
}
}
Update: With ControlBox = false; and Opacity = 0; the form is not only visually transparent but is immune to user input. That is even when the Form1 above if the top most window clicking in it's area falls through to the next window/desktop. Just as if the form wasn't there. The BringToFront() before showing the tooltip is required because otherwise the tooltip could be drawn under other windows, which is not what you want.
If there's no GUI in your application, then in what application are you to providing a spell checker?
If you are integrating your application with another existing application (even non-.NET applications), then you need to obtain the handle (HWND) of the other application and convert it to a System.Windows.Forms.IWin32Window. Once you do this, you can use that handle as the form in the ToolTip.Show method.
Here is the code you need:
using System.Diagnostics;
//...
public class MyWindow : System.Windows.Forms.IWin32Window
{
private IntPtr _hwnd;
public IntPtr Handle
{
get
{
return _hwnd;
}
}
public MyWindow(IntPtr handle)
{
_hwnd = handle;
}
//...
public static MyWindow GetWindowFromName(string processName)
{
Process[] procs = Process.GetProcessesByName(processName);
if (procs.Length != 0)
{
return new MyWindow(procs[0].MainWindowHandle);
}
else
{
throw new ApplicationException(String.Format("{0} is not running", processName));
}
}
}
//...
tip.Show("this worked...", MyWindow.GetWindowFromName("Notepad"), 0, 0, 2000);
I have worked on creating a tooltip that is "not linked to any particular control", because I wanted to replace one of my AutoHotkey scripts which uses the ToolTip command.
I have my code stored at: https://bitbucket.org/tahir-hassan/dotnettooltip
All you do is, instantiate the control, set the text it displays, set the coordinates, and call Show method:
var tooltip = new ToolTipLib.ToolTip()
{
Text = "this is a nice toolTip",
LocationX = 100,
LocationY = 200
};
tooltip.Show();

Categories