I'll be copying a large file over the network using my winforms app and I need to show some kind of progress bar. Rather than cook up my own copy routine, I was thinking that it might be better to simply show the built-in file copy dialog.
I would also need a "Copy complete" and "Copy failed" notification.
I need this to work on Windows XP, Vista and 7. Is there a way to call to engage this functionality from my c# code?
Answer taken from: here
Windows Vista does indeed include a new copy engine that supports exactly what you're looking to do. However, it's possible that previously existing functionality may meet your needs. For example, if you want to copy, move, rename, or delete an individual file or directory, you can take advantage of SHFileOperation (exposed from shell32.dll), which is already wrapped by the Visual Basic® runtime. If you're using Visual Basic 2005, you can simply use functionality from the My namespace, for example:
My.Computer.FileSystem.CopyDirectory(
sourcePath, destinationPath, UIOption.AllDialogs)
Accomplishing the same thing in C# involves only a little more work, adding a reference to Microsoft.VisualBasic.dll (from the Microsoft® .NET Framework installation directory) and using code such as the following:
using Microsoft.VisualBasic.FileIO;
...
FileSystem.CopyDirectory(
sourcePath, destinationPath, UIOption.AllDialogs);
When run, this will result in the same progress UI you'd see if you were doing the same file operations from Windows Explorer. In fact, when running on Windows Vista, you automatically get the new Window Vista progress UI, as shown in Figure 1.
I know it's a old thread but I faced to similar requirement and finally this is the code I used,
maybe it helps someone else.
the credit belongs to here
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Pietschsoft
{
public class NativeProgressDialog
{
private IntPtr parentHandle;
private Win32IProgressDialog pd = null;
private string title = string.Empty;
private string cancelMessage = string.Empty;
private string line1 = string.Empty;
private string line2 = string.Empty;
private string line3 = string.Empty;
private uint value = 0;
private uint maximum = 100;
public string Title
{
get
{
return this.title;
}
set
{
this.title = value;
if (pd != null)
{
pd.SetTitle(this.title);
}
}
}
public string CancelMessage
{
get
{
return this.cancelMessage;
}
set
{
this.cancelMessage = value;
if (pd != null)
{
pd.SetCancelMsg(this.cancelMessage, null);
}
}
}
public string Line1
{
get
{
return this.line1;
}
set
{
this.line1 = value;
if (pd != null)
{
pd.SetLine(1, this.line1, false, IntPtr.Zero);
}
}
}
public string Line2
{
get
{
return this.line2;
}
set
{
this.line2 = value;
if (pd != null)
{
pd.SetLine(2, this.line2, false, IntPtr.Zero);
}
}
}
public string Line3
{
get
{
return this.line3;
}
set
{
this.line3 = value;
if (pd != null)
{
pd.SetLine(3, this.line3, false, IntPtr.Zero);
}
}
}
public uint Value
{
get
{
return this.value;
}
set
{
this.value = value;
if (pd != null)
{
pd.SetProgress(this.value, this.maximum);
}
}
}
public uint Maximum
{
get
{
return this.maximum;
}
set
{
this.maximum = value;
if (pd != null)
{
pd.SetProgress(this.value, this.maximum);
}
}
}
public bool HasUserCancelled
{
get
{
if (pd != null)
{
return pd.HasUserCancelled();
}
else
return false;
}
}
public NativeProgressDialog(IntPtr parentHandle)
{
this.parentHandle = parentHandle;
}
public void ShowDialog(params PROGDLG[] flags)
{
if (pd == null)
{
pd = (Win32IProgressDialog)new Win32ProgressDialog();
pd.SetTitle(this.title);
pd.SetCancelMsg(this.cancelMessage, null);
pd.SetLine(1, this.line1, false, IntPtr.Zero);
pd.SetLine(2, this.line2, false, IntPtr.Zero);
pd.SetLine(3, this.line3, false, IntPtr.Zero);
PROGDLG dialogFlags = PROGDLG.Normal;
if (flags.Length != 0)
{
dialogFlags = flags[0];
for (var i = 1; i < flags.Length; i++)
{
dialogFlags = dialogFlags | flags[i];
}
}
pd.StartProgressDialog(this.parentHandle, null, dialogFlags, IntPtr.Zero);
}
}
public void CloseDialog()
{
if (pd != null)
{
pd.StopProgressDialog();
//Marshal.ReleaseComObject(pd);
pd = null;
}
}
#region "Win32 Stuff"
// The below was copied from: http://pinvoke.net/default.aspx/Interfaces/IProgressDialog.html
public static class shlwapi
{
[DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
static extern bool PathCompactPath(IntPtr hDC, [In, Out] StringBuilder pszPath, int dx);
}
[ComImport]
[Guid("EBBC7C04-315E-11d2-B62F-006097DF5BD4")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface Win32IProgressDialog
{
/// <summary>
/// Starts the progress dialog box.
/// </summary>
/// <param name="hwndParent">A handle to the dialog box's parent window.</param>
/// <param name="punkEnableModless">Reserved. Set to null.</param>
/// <param name="dwFlags">Flags that control the operation of the progress dialog box. </param>
/// <param name="pvResevered">Reserved. Set to IntPtr.Zero</param>
void StartProgressDialog(
IntPtr hwndParent, //HWND
[MarshalAs(UnmanagedType.IUnknown)] object punkEnableModless, //IUnknown
PROGDLG dwFlags, //DWORD
IntPtr pvResevered //LPCVOID
);
/// <summary>
/// Stops the progress dialog box and removes it from the screen.
/// </summary>
void StopProgressDialog();
/// <summary>
/// Sets the title of the progress dialog box.
/// </summary>
/// <param name="pwzTitle">A pointer to a null-terminated Unicode string that contains the dialog box title.</param>
void SetTitle(
[MarshalAs(UnmanagedType.LPWStr)] string pwzTitle //LPCWSTR
);
/// <summary>
/// Specifies an Audio-Video Interleaved (AVI) clip that runs in the dialog box. Note: Note This method is not supported in Windows Vista or later versions.
/// </summary>
/// <param name="hInstAnimation">An instance handle to the module from which the AVI resource should be loaded.</param>
/// <param name="idAnimation">An AVI resource identifier. To create this value, use the MAKEINTRESOURCE macro. The control loads the AVI resource from the module specified by hInstAnimation.</param>
void SetAnimation(
IntPtr hInstAnimation, //HINSTANCE
ushort idAnimation //UINT
);
/// <summary>
/// Checks whether the user has canceled the operation.
/// </summary>
/// <returns>TRUE if the user has cancelled the operation; otherwise, FALSE.</returns>
/// <remarks>
/// The system does not send a message to the application when the user clicks the Cancel button.
/// You must periodically use this function to poll the progress dialog box object to determine
/// whether the operation has been canceled.
/// </remarks>
[PreserveSig]
[return: MarshalAs(UnmanagedType.Bool)]
bool HasUserCancelled();
/// <summary>
/// Updates the progress dialog box with the current state of the operation.
/// </summary>
/// <param name="dwCompleted">An application-defined value that indicates what proportion of the operation has been completed at the time the method was called.</param>
/// <param name="dwTotal">An application-defined value that specifies what value dwCompleted will have when the operation is complete.</param>
void SetProgress(
uint dwCompleted, //DWORD
uint dwTotal //DWORD
);
/// <summary>
/// Updates the progress dialog box with the current state of the operation.
/// </summary>
/// <param name="ullCompleted">An application-defined value that indicates what proportion of the operation has been completed at the time the method was called.</param>
/// <param name="ullTotal">An application-defined value that specifies what value ullCompleted will have when the operation is complete.</param>
void SetProgress64(
ulong ullCompleted, //ULONGLONG
ulong ullTotal //ULONGLONG
);
/// <summary>
/// Displays a message in the progress dialog.
/// </summary>
/// <param name="dwLineNum">The line number on which the text is to be displayed. Currently there are three lines—1, 2, and 3. If the PROGDLG_AUTOTIME flag was included in the dwFlags parameter when IProgressDialog::StartProgressDialog was called, only lines 1 and 2 can be used. The estimated time will be displayed on line 3.</param>
/// <param name="pwzString">A null-terminated Unicode string that contains the text.</param>
/// <param name="fCompactPath">TRUE to have path strings compacted if they are too large to fit on a line. The paths are compacted with PathCompactPath.</param>
/// <param name="pvResevered"> Reserved. Set to IntPtr.Zero.</param>
/// <remarks>This function is typically used to display a message such as "Item XXX is now being processed." typically, messages are displayed on lines 1 and 2, with line 3 reserved for the estimated time.</remarks>
void SetLine(
uint dwLineNum, //DWORD
[MarshalAs(UnmanagedType.LPWStr)] string pwzString, //LPCWSTR
[MarshalAs(UnmanagedType.VariantBool)] bool fCompactPath, //BOOL
IntPtr pvResevered //LPCVOID
);
/// <summary>
/// Sets a message to be displayed if the user cancels the operation.
/// </summary>
/// <param name="pwzCancelMsg">A pointer to a null-terminated Unicode string that contains the message to be displayed.</param>
/// <param name="pvResevered">Reserved. Set to NULL.</param>
/// <remarks>Even though the user clicks Cancel, the application cannot immediately call
/// IProgressDialog::StopProgressDialog to close the dialog box. The application must wait until the
/// next time it calls IProgressDialog::HasUserCancelled to discover that the user has canceled the
/// operation. Since this delay might be significant, the progress dialog box provides the user with
/// immediate feedback by clearing text lines 1 and 2 and displaying the cancel message on line 3.
/// The message is intended to let the user know that the delay is normal and that the progress dialog
/// box will be closed shortly.
/// It is typically is set to something like "Please wait while ...". </remarks>
void SetCancelMsg(
[MarshalAs(UnmanagedType.LPWStr)] string pwzCancelMsg, //LPCWSTR
object pvResevered //LPCVOID
);
/// <summary>
/// Resets the progress dialog box timer to zero.
/// </summary>
/// <param name="dwTimerAction">Flags that indicate the action to be taken by the timer.</param>
/// <param name="pvResevered">Reserved. Set to NULL.</param>
/// <remarks>
/// The timer is used to estimate the remaining time. It is started when your application
/// calls IProgressDialog::StartProgressDialog. Unless your application will start immediately,
/// it should call Timer just before starting the operation.
/// This practice ensures that the time estimates will be as accurate as possible. This method
/// should not be called after the first call to IProgressDialog::SetProgress.</remarks>
void Timer(
PDTIMER dwTimerAction, //DWORD
object pvResevered //LPCVOID
);
}
[ComImport]
[Guid("F8383852-FCD3-11d1-A6B9-006097DF5BD4")]
public class Win32ProgressDialog
{
}
/// <summary>
/// Flags that indicate the action to be taken by the ProgressDialog.SetTime() method.
/// </summary>
public enum PDTIMER : uint //DWORD
{
/// <summary>Resets the timer to zero. Progress will be calculated from the time this method is called.</summary>
Reset = (0x01),
/// <summary>Progress has been suspended.</summary>
Pause = (0x02),
/// <summary>Progress has been resumed.</summary>
Resume = (0x03)
}
[Flags]
public enum PROGDLG : uint //DWORD
{
/// <summary>Normal progress dialog box behavior.</summary>
Normal = 0x00000000,
/// <summary>The progress dialog box will be modal to the window specified by hwndParent. By default, a progress dialog box is modeless.</summary>
Modal = 0x00000001,
/// <summary>Automatically estimate the remaining time and display the estimate on line 3. </summary>
/// <remarks>If this flag is set, IProgressDialog::SetLine can be used only to display text on lines 1 and 2.</remarks>
AutoTime = 0x00000002,
/// <summary>Do not show the "time remaining" text.</summary>
NoTime = 0x00000004,
/// <summary>Do not display a minimize button on the dialog box's caption bar.</summary>
NoMinimize = 0x00000008,
/// <summary>Do not display a progress bar.</summary>
/// <remarks>Typically, an application can quantitatively determine how much of the operation remains and periodically pass that value to IProgressDialog::SetProgress. The progress dialog box uses this information to update its progress bar. This flag is typically set when the calling application must wait for an operation to finish, but does not have any quantitative information it can use to update the dialog box.</remarks>
NoProgressBar = 0x00000010
}
#endregion
}
}
How you can use it:
private Pietschsoft.NativeProgressDialog pd;
private uint progressPercent;
Timer timer1 = new Timer();
private void button1_Click_1(object sender, EventArgs e)
{
timer1.Interval = 300;
timer1.Tick += (s,ev)=>
{
progressPercent++;
if (pd.HasUserCancelled)
{
timer1.Stop();
pd.CloseDialog();
}
else
{
// Update the progress value
pd.Value = progressPercent;
pd.Line2 = "Percent " + progressPercent.ToString() + "%";
if (progressPercent >= 100)
{
timer1.Stop();
pd.CloseDialog();
}
}
};
pd = new Pietschsoft.NativeProgressDialog(this.Handle);
pd.Title = "Performing Operation";
pd.CancelMessage = "Please wait while the operation is cancelled";
pd.Maximum = 100;
pd.Value = 0;
pd.Line1 = "Line One";
pd.Line3 = "Calculating Time Remaining...";
//pd.ShowDialog(); // Defaults to PROGDLG.Normal
pd.ShowDialog(Pietschsoft.NativeProgressDialog.PROGDLG.Modal, Pietschsoft.NativeProgressDialog.PROGDLG.AutoTime, Pietschsoft.NativeProgressDialog.PROGDLG.NoMinimize);
progressPercent = 0;
timer1.Start();
}
Related
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
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.
A client has a problem with employees opening a program displaying sensitive information and walking away without closing it. They've asked me to monitor when the application is open and close it after a certain amount of inactivity.
What I've done is created a new program that launches the original, but with another form on top of it. If my form is transparent, I can click through the form with no problems, and manipulate the underlying program. If my form is (slightly) opaque, clicks will register with my program, but will not pass through.
What I need is a way to let my form register that a click has happened, reset its timer, and pass the click through to the underlying application. I can reset the timer, or I can pass the click through, but not both.
Here's the code I'm currently trying. The first section defines my form as transparent and keeps it on top of the other application.
private void Form1_Load(object sender, EventArgs e)
{
this.Opacity = 0.40;
this.TopMost = true;
}
I thought this would let me monitor for a click, reset the timer, then pass it through, but it's not working, so I must be missing something. EDIT: WT_NCHITTEST = 0x84, and HTTRANSPARENT = -1, as indicated here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms645618%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NCHITTEST) // m.Msg 0x84 means that Windows is asking our form whether it is "transparent" or not.
{
timeToClose = 10;
m.Result = new IntPtr(HTTRANSPARENT); // tell Windows yes, to pass everything through.
}
base.WndProc(ref m);
}
That is way over engineered, I implement inactivity checks with System.Timers.Timer and PInvoke GetLastInputInfo. Run a second application that monitors for workstation inactivity and close sensitive applications when the threshold is violated.
Initialize the timer with an interval and set it to auto-reset, then just check for inactivity every time the timer elapses. If the inactivity time is greater than your threshold, shut it down.
Here is the code for GetLastInputInfo.
public static class Tracker
{
/// <summary>
/// Reference to the GetLastInputInfo in the user32.dll file.
/// </summary>
/// <param name="plii">LASTINPUTINFO struct</param>
/// <returns></returns>
[DllImport("user32.dll")]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
/// <summary>
/// Polls the system for the milliseconds elapsed since last user input.
/// </summary>
/// <returns>Milliseconds since last user input.</returns>
public static uint GetIdleTime()
{
LASTINPUTINFO lastInput = new LASTINPUTINFO();
lastInput.cbSize = (uint)Marshal.SizeOf(lastInput);
GetLastInputInfo(ref lastInput);
return ((uint)Environment.TickCount - lastInput.dwTime);
}
}
/// <summary>
/// Struct required and populated by the user32.dll GetLastInputInfo method.
/// </summary>
internal struct LASTINPUTINFO
{
public uint cbSize; //Size of the struct.
public uint dwTime; //TickCount at last input.
}
My inactivity timer implementation looks like this:
public void InitializeTimer()
{
ActivityTimer = new System.Timers.Timer(Configuration.TimeoutSettings["IntervalMilliseconds"]);
ActivityTimer.AutoReset = true;
ActivityTimer.Elapsed += OnElapsedPollForActivity;
ActivityTimer.Start();
}
/// <summary>
/// Method is called in ValidationForm_FormClosing, unsubscribes handler from event and stops the clock.
/// </summary>
public void TerminateTimer()
{
ActivityTimer.Elapsed -= OnElapsedPollForActivity;
ActivityTimer.Stop();
}
/// <summary>
/// Fires on ActivityTimer.Elapsed, polls workstation for time since last user input.
/// </summary>
private void OnElapsedPollForActivity(object sender, System.Timers.ElapsedEventArgs e)
{
if (Tracker.GetIdleTime() > Configuration.TimeoutSettings["TriggerMilliseconds"])
{
Logger.LogException(CurrentSession.SessionID, DateTime.Now, new InactivityException("Detected extended period of workstation inactivity."));
Application.Exit();
}
}
Setup
I'm using System.Windows.Forms.Form as window source. Constructor:
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
Form-Load:
_Device = new Device(this, DeviceConfiguration.SelectConfiguration(Bits.Eight, Bits.Eight, Bits.Eight, Bits.Eight, Bits.Twentyfour, Bits.Eight, true, true), 1024, 768);
Debug.WriteLine(GL.GetString(GL.GL_EXTENSIONS));
base.OnLoad(e);
In the load method the device configuration is selected, which is actually working. Calls like GL.GetString(GL.GL_EXTENSIONS) are working. All gl-methods are loaded too so all used methods are supported.
Form-Shown:
Application.Initialize(this);
RenderSettings = new RenderSettings(this);
Debug.WriteLine("After setup render settings: " + GL.GetErrorDescription());
_Textures[0] = Resources.LoadTexture("Resources\\Buttons\\btn_big_bg_normal.dds");
_Textures[1] = Resources.LoadTexture("Resources\\Buttons\\btn_big_bg_normal.dds");
base.OnShown(e);
_IsShown = true;
The Application.Initialize(this) initializes the framework used ( written by me ) and is also initializing a default shader, which is working, and model which is not working.
This causes the rendering later to fail because the returning handle is invalid.
Framework
I've a own OpenGL-wrapper which has enums for all parameters to enable development without looking in the OpenGL-references many times. As example the method glGenBuffers:
[MethodGL]
public delegate void glGenBuffers(Int32 count, ArrayBufferHandle[] buffers);
The ArrayBufferHandle definition:
/// <summary>
/// Represents a handle to a buffer
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 4)]
[ComVisible(true)]
public struct ArrayBufferHandle
{
[FieldOffset(0)]
UInt32 Handle;
/// <summary>
/// Determines if the buffer is valid
/// </summary>
public Boolean IsValid { get { return Handle > 0; } }
/// <summary>
/// Retrieves the default array buffer
/// <br>which is used to reset bindings</br>
/// </summary>
public static ArrayBufferHandle Default { get { return new ArrayBufferHandle(); } }
/// <summary>
/// Retrieves the string representation of the current object
/// </summary>
/// <returns>The current object represented as string</returns>
public override String ToString()
{
return String.Format("[ArrayBufferHandle Handle:{0} Valid:{1}]", Handle, IsValid);
}
}
Of course the shader creation is working and uses the types like the shown above.
Conclusion
I don't think i've much more to say to the issues i've faced. glGenBuffers and glGenTextures returning zero and i don't know why. The setup seems like others i've already used.
Of course the window is shown with a blue background when not rendering any models and i don't have any errors when using glGetError.
First: Thanks to Mārtiņš Možeiko who led me into the right direction.
As it turns out the method void glGenBuffer(Int32 count, UInt32[] buffers) is working, but the method void glGenBuffers(Int32 count, ArrayBufferHandle[] buffers) isn't. Both have a 4-byte / element array which is passed but .NET seems to handle them different. The solution has been simple:
void glGenBuffers(Int32 count, [Out]ArrayBufferHandle[] buffers)
Add a Out attribute to the array.
I am trying to invoke the "Run" dialogue box that is often on the Start Menu - I did some research and have only managed to find one way of accessing it (using "Windows Key" + R).
So I assume simulating key strokes e.g.:
SendKeys.Send("{TEST}")
would do the job? Although how can you simulate the "Windows" key on the keyboard?
I am sure there is an easier way of doing this - without using sendkeys - anyone have any ideas?
You can use PInvoke to invoke Run dialog.
[Flags()]
public enum RunFileDialogFlags : uint
{
/// <summary>
/// Don't use any of the flags (only works alone)
/// </summary>
None = 0x0000,
/// <summary>
/// Removes the browse button
/// </summary>
NoBrowse = 0x0001,
/// <summary>
/// No default item selected
/// </summary>
NoDefault = 0x0002,
/// <summary>
/// Calculates the working directory from the file name
/// </summary>
CalcDirectory = 0x0004,
/// <summary>
/// Removes the edit box label
/// </summary>
NoLabel = 0x0008,
/// <summary>
/// Removes the separate memory space checkbox (Windows NT only)
/// </summary>
NoSeperateMemory = 0x0020
}
we need to Import the DLL using the DllImport attribute.
[DllImport("shell32.dll", CharSet = CharSet.Auto, EntryPoint = "#61", SetLastError = true)]
static extern bool SHRunFileDialog(IntPtr hwndOwner,
IntPtr hIcon,
string lpszPath,
string lpszDialogTitle,
string lpszDialogTextBody,
RunFileDialogFlags uflags);
Implementation:
private void ShowRunDialog(object sender, RoutedEventArgs e)
{
SHRunFileDialog(IntPtr.Zero,
IntPtr.Zero,
"c:\\",
"Run Dialog using PInvoke",
"Type the name of a program, folder or internet address
and Windows will open that for you.",
RunFileDialogFlags.CalcDirectory);
}