I need to use an OnScreenKeyboard Component like Native Windows OSK in my Wpf application. I can call the UI by Process.Start("osk.exe") but the UI appears on top of the main ui window. I want to start the OSK app just bottom of my app. Is this possible with any arguments? -e.g. process.StartInfo.WindowPosition=xxx- I'd prefer to use it before I create my own Component.
OK. I solved my problem with Win32 SetWindowPos method. Sharing if it might help others. Below code is opening and resizing/positioning the Native OSK.exe. It places the keyboard relative to the mouse position.
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public static implicit operator Point(POINT point)
{
return new Point(point.X, point.Y);
}
}
[DllImport("user32.dll")]
public static extern bool GetCursorPos(out POINT lpPoint);
public static Point GetCursorPosition()
{
POINT lpPoint;
GetCursorPos(out lpPoint);
//bool success = User32.GetCursorPos(out lpPoint);
// if (!success)
return lpPoint;
}
private void OSKMenuItem_Click(object sender, RoutedEventArgs e)
{
Process osk = new Process();
osk.StartInfo.FileName = "osk.exe";
osk.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
osk.Start();
Task.Delay(1000).ContinueWith((a) => {
Point p = GetCursorPosition();
try
{
IntPtr target_hwnd = FindWindowByCaption(IntPtr.Zero, "On-Screen Keyboard");
if (target_hwnd == IntPtr.Zero)
{
Microsoft.Windows.Controls.MessageBox.Show("Error");
}
SetWindowPos(target_hwnd, IntPtr.Zero, (int)p.X - 200, (int)p.Y, 1300, 600, 0);
}
catch (Exception ex)
{
//throw;
}
});
Related
I want to show a window with a custom region and a custom appearance. I hook a window proc and set the custom region on WM_SIZE message by SetWindowRgn function. Also i process a WM_NCCALCSIZE to remove standard window frame.
[SecuritySafeCritical]
protected virtual IntPtr HwndSourceHookHandler(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
if(msg == WinApi.WM_NCCALCSIZE) {
handled = true;
return IntPtr.Zero;
}
if(msg == WinApi.WM_SIZE) {
if(region != IntPtr.Zero)
WinApi.DeleteObject(region);
int width = lParam.ToInt32() & 0xFFFF;
int height = lParam.ToInt32() >> 16;
region = WinApi.CreateRoundRectRgn(0, 0, width, height, 40, 40);
WinApi.SetWindowRgn(new WindowInteropHelper(this).Handle, region, true);
}
return IntPtr.Zero;
}
The window is appeared on my Windows 8.1 OS with a round corners as expected and without border and window buttons. But i notice that in this case window is closed immediately without performing the close animation. If i comment SetWindowRgn, all works fine. Anyone knows what i am doing wrong?
The animation is being performed in the Window. Therefore, you need to stop the Window from actually closing when the close Button is pressed and start the animation then instead. When the animation is complete, then you should close the Window. This can be achieved as follows:
Handle the Window.Closing and Window.Close events:
private void AnimationWindow_Closing(object sender, CancelEventArgs e)
{
if (!isCloseAnimationComplete)
{
e.Cancel = true;
CloseWindow();
}
}
private void AnimationWindow_Close(object sender, RoutedEventArgs e)
{
CloseWindow();
}
public void CloseWindow()
{
// Set up animation
animation.Completed += CloseAnimation_Completed;
// Start animation
}
private void CloseAnimation_Completed(object sender, EventArgs e)
{
isCloseAnimationComplete = true;
// Actually close Window here
Close();
}
We know very well how easy is too create a WPF application where user can paint a rectangle using the mouse. To do this you just create a Rectangle control and set its coordinates, you don't worry about DoubleBuffering, repainting and such stuff. Well, I'd be very happy yo use WPF for the application where user can paint different shapes, but the clients insists to be a WinForms application. So the solution here is to use the XOR or ROP operation like in old good WinAPI years and I don't really like this. This doesn't give me a nice option to move a text while in XOR mode.
So I was thinking how can I achieve same smooth painting experience in a WinForms application like I'd have in WPF. Put together such a code, where I wanted to create a separate layer where I'd paint the current shape, while leaving intact the rest of the objects. I used same technique in an iPad application and worked pretty well.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace TestPainting
{
public partial class Form1 : Form
{
private bool _isMouseDown;
private Graphics _bufferGraphics;
private Point _startPos;
private TransparentPanel _paintBuffer;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
_isMouseDown = true;
_paintBuffer = new TransparentPanel
{
Size = Size,
};
Controls.Add(_paintBuffer);
_paintBuffer.BringToFront();
_bufferGraphics = Graphics.FromHwnd(_paintBuffer.Handle);
_startPos = e.Location;
Capture = true;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (!_isMouseDown)
return;
_bufferGraphics.Clear(Color.Transparent);
_bufferGraphics.DrawRectangle(Pens.Green, _startPos.X, _startPos.Y, e.X - _startPos.X, e.Y - _startPos.Y);
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
_isMouseDown = false;
Capture = false;
_bufferGraphics.Dispose();
Controls.Remove(_paintBuffer);
_paintBuffer.Dispose();
}
}
public class TransparentPanel : Panel
{
public TransparentPanel()
{
DoubleBuffered = true;
}
[Browsable(false)]
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// Do Nothing
}
}
}
which if course doesn't work as needed. I'm getting a black panel when pressing the mouse instead of a transparent one. Plus the rectangle while is painted flickers a lot, even though I did set the DoubleBuffering stuff.
Can someone provide some better ideas of such an implementation or maybe there some other open source project where I can see how other people are doing. I'd need to have same experience as in Paint.NET, just too bad is not open source anymore. (I know I can use Reflector, and I did, but man, there is tons of code over there :) )
Thx for any ideas.
Try this (see FIX #1 and FIX #2):
private void Form1_MouseDown( object sender, MouseEventArgs e )
{
_isMouseDown = true;
_paintBuffer = new TransparentPanel
{
Size = Size,
};
Controls.Add( _paintBuffer );
_paintBuffer.BringToFront();
// FIX #1:
//
this.Refresh();
_bufferGraphics = Graphics.FromHwnd( _paintBuffer.Handle );
_startPos = e.Location;
Capture = true;
}
private void Form1_MouseMove( object sender, MouseEventArgs e )
{
if ( !_isMouseDown )
return;
//FIX #2:
// _bufferGraphics.Clear( Color.Transparent );
_bufferGraphics.Clear( this.BackColor );
_bufferGraphics.DrawRectangle( Pens.Green, _startPos.X, _startPos.Y, e.X - _startPos.X, e.Y - _startPos.Y );
}
i'm trying to call AnimateWindow to animate the show and hide of a WinForms window.
Here's a copy of the win32 translation:
private static class NativeMethods
{
public const int AW_ACTIVATE = 0x20000;
public const int AW_HIDE = 0x10000;
public const int AW_BLEND = 0x80000;
public const int AW_CENTER = 0x00000010;
public const int AW_SLIDE = 0X40000;
public const int AW_HOR_POSITIVE = 0x1;
public const int AW_HOR_NEGATIVE = 0X2;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int AnimateWindow(IntPtr hwand, int dwTime, int dwFlags);
}
But the problem is how to fit a call to AnimateWindow into the) WinForms scheme. One person suggests OnLoad:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
AnimateWindow(this.Handle, 200, AW_ACTIVATE | AW_HOR_NEGATIVE | AW_SLIDE);
}
and OnClosing:
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing(e);
if (e.Cancel == false)
{
AnimateWindow(this.Handle, 200, AW_HIDE | AW_HOR_POSITIVE | AW_SLIDE);
}
}
Except that it doesn't work.
the form doesn't use any animation while appearing
during hide the form animates its horizontal slide off the screen, then reappears, before hiding the normal way
What is the correct way to mix AnimateWindow with WinForms?
See also
.NET AnimateWindow: this guy asked the same question. But since it was trying to achieve something else, people solved his problem rather than answering his question.
C# WinForms AnimateWindow issue: This guy was interested in using AnimateWindow with child controls, rather than a top-level window.
Bonus Chatter
i was perusing through the Form -> Show -> Visible -> SetVisibleCore, when i discovered this bug:
protected virtual void SetVisibleCore(bool value)
{
try
{
HandleCollector.SuspendCollect();
//...snip...
}
finally
{
HandleCollector.ResumeCollect();
}
}
Nice to know that everyone can introduce these subtle errors.
I think AnimateWindow has it's limitations to work properly. For example, it doesn't play well with Aero, so to animate a sliding form, you would need to set the BorderStyle to None. Also, make sure the StartPosition is set to Manual.
Simple example:
public partial class Form1 : Form {
public const int AW_ACTIVATE = 0x20000;
public const int AW_HIDE = 0x10000;
public const int AW_BLEND = 0x80000;
public const int AW_CENTER = 0x00000010;
public const int AW_SLIDE = 0X40000;
public const int AW_HOR_POSITIVE = 0x1;
public const int AW_HOR_NEGATIVE = 0X2;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int AnimateWindow(IntPtr hwand, int dwTime, int dwFlags);
public Form1() {
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) {
Form toastForm = new Form();
toastForm.ShowInTaskbar = false;
toastForm.StartPosition = FormStartPosition.Manual;
toastForm.FormBorderStyle = FormBorderStyle.None;
toastForm.Size = new Size(256, 64);
toastForm.Location = new Point(Screen.PrimaryScreen.WorkingArea.Right - toastForm.Width,
Screen.PrimaryScreen.WorkingArea.Bottom - toastForm.Height);
Button closeButton = new Button();
closeButton.Text = "Close";
toastForm.Controls.Add(closeButton);
closeButton.Click += delegate { toastForm.Close(); };
AnimateWindow(toastForm.Handle, 200, AW_ACTIVATE | AW_HOR_NEGATIVE | AW_SLIDE);
toastForm.Show();
}
}
I'm not sure what your AnimateWindow call does, but when you need to change underlying native 'stuff' to do with windows forms, I've always used the CreateParams() override. You may
find a similar function for what you are trying to achieve.
Here's an example of a transparent tool window, that doesn't activate when it is shown.
Protected Overrides ReadOnly Property CreateParams() As System.Windows.Forms.CreateParams
Get
Dim baseParams As Windows.Forms.CreateParams = MyBase.CreateParams
baseParams.ExStyle = baseParams.ExStyle Or NativeMethods.ExtendedWindowsStyles.WS_EX_LAYERED Or NativeMethods.ExtendedWindowsStyles.WS_EX_TRANSPARENT Or NativeMethods.ExtendedWindowsStyles.WS_EX_NOACTIVATE Or NativeMethods.ExtendedWindowsStyles.WS_EX_TOOLWINDOW
Return baseParams
End Get
End Property
I'm using WPF's OpenFileDialog, and I'm looking for a way to make sure it is centered in the parent window when shown. It seems to be missing obvious properties like StartupPosition that might enable this.
Does anybody know the secret?
Update: It seems that the first time I open it, it does appear in the center of the parent, but if I move it, it then remembers its position, and doesn't open centered on subsequent occassions.
here is the code of a generic class that allows to play with "sub dialogs" like this one:
public class SubDialogManager : IDisposable
{
public SubDialogManager(Window window, Action<IntPtr> enterIdleAction)
:this(new WindowInteropHelper(window).Handle, enterIdleAction)
{
}
public SubDialogManager(IntPtr hwnd, Action<IntPtr> enterIdleAction)
{
if (enterIdleAction == null)
throw new ArgumentNullException("enterIdleAction");
EnterIdleAction = enterIdleAction;
Source = HwndSource.FromHwnd(hwnd);
Source.AddHook(WindowMessageHandler);
}
protected HwndSource Source { get; private set; }
protected Action<IntPtr> EnterIdleAction { get; private set; }
void IDisposable.Dispose()
{
if (Source != null)
{
Source.RemoveHook(WindowMessageHandler);
Source = null;
}
}
private const int WM_ENTERIDLE = 0x0121;
protected virtual IntPtr WindowMessageHandler(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_ENTERIDLE)
{
EnterIdleAction(lParam);
}
return IntPtr.Zero;
}
}
And this is how you would use it in a standard WPF app. Here I just copy the parent window size, but I'll let you do the center math :-)
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
bool computed = false; // do this only once
int x = (int)Left;
int y = (int)Top;
int w = (int)Width;
int h = (int)Height;
using (SubDialogManager center = new SubDialogManager(this, ptr => { if (!computed) { SetWindowPos(ptr, IntPtr.Zero, x, y, w, h, 0); computed= true; } }))
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.ShowDialog(this);
}
}
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, int flags);
}
CommonDialog in WPF does not inherit from window class, so it does not have StartupPosition property.
Check this blog post for one solution: OpenFileDialog in .NET on Vista
In short, it wraps dialog in a window and then shows it.
In my application I have to resize forms and all its control on mouse drag effect and forms should have drop shadow effect the problem is that all my forms are custom one (with no boarder).
Thanks in advance
i think u have to implement by yourself
on mouse down start bind on mouse drag + change cursor to resize icon
on mouse drag, just simply reduce your form size
on mouse up unbind mouse drag event
the reason i suggest dynamic event binding so u can specified which control or area should have mouse down
I'm not sure about the drop shadow effect, but you should be able to resize a form by placing a button in the bottom right corner with some appropriate icon. When the user clicks and drags this button, it resizes the form. Here's some example code:
public partial class Form1 : Form
{
private int bottomBorder;
private int rightBorder;
private Point mouseStart;
private bool isResizing = false;
public Form1()
{
InitializeComponent();
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (isResizing)
{
var newLocation = button1.Location;
newLocation.Offset(
e.X - mouseStart.X,
e.Y - mouseStart.Y);
button1.Location = newLocation;
this.Height = button1.Bottom + bottomBorder;
this.Width = button1.Right + rightBorder;
button1.Refresh();
}
}
private void button1_MouseDown(object sender, MouseEventArgs e)
{
isResizing = true;
mouseStart = e.Location;
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
isResizing = false;
}
private void Form1_Load(object sender, EventArgs e)
{
bottomBorder = this.Height - button1.Bottom;
rightBorder = this.Width - button1.Right;
}
}
Without a border (or some control), how do you intend to resize? Figure that part out, then try this code in your form:
public class CustomForm : Form
{
private const int WmNcLButtonDown = 0xA1;
private const int HtBottomRight = 17;
[DllImport("user32.dll")]
private static extern int ReleaseCapture();
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hwnd, int msg, int wparam, int lparam);
// elsewhere
void ResizeForm()
{
ReleaseCapture();
SendMessage(this.Handle, WmNcLButtonDown, HtBottomRight, 0);
}
}
This code will resize your form as though the bottom right corner was used. Look up HT_BOTTOMRIGHT and other HT_ constants for different locations for resizing.
I used the solutions by Don Kirkby and Matthew Ferreira and created my own solution combining the two. I added a StatusStrip named "resizeHandle", made it's size 20x20 pixels and listened to it's events.
public class CustomForm : Form
{
private const int WmNcLButtonDown = 0xA1;
private const int HtBottomRight = 17;
private const int wmNcLButtonUp = 0xA2;
private bool isResizing = false;
[DllImport("user32.dll")]
private static extern int ReleaseCapture();
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hwnd, int msg, int wparam, int lparam);
private void resizeHandle_MouseDown(object sender, MouseEventArgs e)
{
isResizing = true;
}
private void resizeHandle_MouseMove(object sender, MouseEventArgs e)
{
if (isResizing)
{
// Check if we have released the Left mouse button
isResizing = (e.Button == MouseButtons.Left);
ReleaseCapture();
if (isResizing)
{
SendMessage(Handle, wmNcLButtonDown, HtBottomRight, 0);
}
else
{
// Left Mouse button was released, end resizing.
SendMessage(Handle, wmNcLButtonUp, HtBottomRight, 0);
}
}
}