How to force WPF startup window to specific screen? - c#

I have a WPF application that will show information on a projector through a dedicated window.
I would like to configure what screen to be used for projector display and what to be used for main application window.
This code will generate projector output on specified screen:
var screen = GetProjectorScreen();
_projectorWindow = new ProjectorWindow();
_projectorWindow.Left = screen.WorkingArea.Left;
_projectorWindow.Top = screen.WorkingArea.Top;
_projectorWindow.Owner = _parentWindow;
_projectorWindow.Show();
public static Screen GetProjectorScreen()
{
var screens = Screen.AllScreens;
if (screens.Length > 1 && Settings.Default.DisplayScreen < screens.Length)
{
return screens[Settings.Default.DisplayScreen];
}
return screens[0];
}
I have tried to do the same trick with startup form, but so far without success.
I tried to set Top and Left properties in MainWindow constructor but that did not work.
The startup window is launched from App.xaml.cs by setting StartupUri:
StartupUri = new Uri("Windows/MainWindow.xaml", UriKind.Relative);
Is there any other way to launch startup form?
I tried to just call the constructor but that causes a crash because some resources are no longer loaded.

I got it working. It is necessary to set WindowState to Normal before setting window location. And the setting will not work at all until the window is created, i.e. after constructor call. I therefore call the explicit setting in Windows_Loaded event. That might cause a flickering if window need to be moved, but that is acceptable to me.
The SetScreen method should also be called after screen settings have changed manually by user.
private void SetScreen()
{
var mainScreen = ScreenHandler.GetMainScreen();
var currentScreen = ScreenHandler.GetCurrentScreen(this);
if (mainScreen.DeviceName != currentScreen.DeviceName)
{
this.WindowState = WindowState.Normal;
this.Left = mainScreen.WorkingArea.Left;
this.Top = mainScreen.WorkingArea.Top;
this.Width = mainScreen.WorkingArea.Width;
this.Height = mainScreen.WorkingArea.Height;
this.WindowState = WindowState.Maximized;
}
}
The backup ScreenHandler utility is very simple:
public static class ScreenHandler
{
public static Screen GetMainScreen()
{
return GetScreen(Settings.Default.MainScreenId);
}
public static Screen GetProjectorScreen()
{
return GetScreen(Settings.Default.ProjectorScreenId);
}
public static Screen GetCurrentScreen(Window window)
{
var parentArea = new Rectangle((int)window.Left, (int)window.Top, (int)window.Width, (int)window.Height);
return Screen.FromRectangle(parentArea);
}
private static Screen GetScreen(int requestedScreen)
{
var screens = Screen.AllScreens;
var mainScreen = 0;
if (screens.Length > 1 && mainScreen < screens.Length)
{
return screens[requestedScreen];
}
return screens[0];
}
}

The accepted answer no longer works on Windows 10 with per-monitor DPI in the app’s manifest.
Here’s what worked for me:
public partial class MyWindow : Window
{
readonly Rectangle screenRectangle;
public MyWindow( System.Windows.Forms.Screen screen )
{
screenRectangle = screen.WorkingArea;
InitializeComponent();
}
[DllImport( "user32.dll", SetLastError = true )]
static extern bool MoveWindow( IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint );
protected override void OnSourceInitialized( EventArgs e )
{
base.OnSourceInitialized( e );
var wih = new WindowInteropHelper( this );
IntPtr hWnd = wih.Handle;
MoveWindow( hWnd, screenRectangle.Left, screenRectangle.Top, screenRectangle.Width, screenRectangle.Height, false );
}
void Window_Loaded( object sender, RoutedEventArgs e )
{
WindowState = WindowState.Maximized;
}
}
Just setting Left/Top doesn’t work. Based on my tests, per-monitor DPI awareness only kicks in after window is already created and placed on some monitor. Before that, apparently Left/Top properties of the window scale with DPI of the primary monitor.
For some combinations of per-monitor DPI and monitors layout, this caused a bug where setting Left/Top properties to the pixels values of System.Windows.Forms.Screen rectangle caused the window to be positioned somewhere else.
The above workaround is only suitable for maximizing, it does not always sets the correct size of the window. But at least it sets correct top-left corner which is enough for the maximize to work correctly.

Related

C# WPF Message Only Window [duplicate]

I create a global hot key to show a window by PInvoking RegisterHotKey(). But to do this I need that window's HWND, which doesn't exist until the window is loaded, that means shown for the first time. But I don't want to show the window before I can set the hot key. Is there a way to create a HWND for that window that is invisible to the user?
If you are targeting .NET 4.0 you can make use of the new EnsureHandle method available on the WindowInteropHelper:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(thanks to Thomas Levesque for pointing this out.)
If you are targeting an older version of the .NET Framework, the easiest way is to show the window to get to the HWND while setting a few properties to make sure that the window is invisible and doesn't steal focus:
var window = new Window() //make sure the window is invisible
{
Width = 0,
Height = 0,
WindowStyle = WindowStyle.None,
ShowInTaskbar = false,
ShowActivated = false
};
window.Show();
Once you want to show the actual window you can then set the Content, the size and change the style back to a normal window.
You can also change the window into a so called message-only window. As this window type does not support graphical elements it will never be shown. Basically it comes down to calling:
SetParent(hwnd, (IntPtr)HWND_MESSAGE);
Either create a dedicated message window which will always be hidden, or use the real GUI window and change it back to a normal window when you want to display it. See the code below for a more complete example.
[DllImport("user32.dll")]
static extern IntPtr SetParent(IntPtr hwnd, IntPtr hwndNewParent);
private const int HWND_MESSAGE = -3;
private IntPtr hwnd;
private IntPtr oldParent;
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource hwndSource = PresentationSource.FromVisual(this) as HwndSource;
if (hwndSource != null)
{
hwnd = hwndSource.Handle;
oldParent = SetParent(hwnd, (IntPtr)HWND_MESSAGE);
Visibility = Visibility.Hidden;
}
}
private void OpenWindowMenuItem_Click(object sender, RoutedEventArgs e)
{
SetParent(hwnd, oldParent);
Show();
Activate();
}
For me the solution of setting the width, height to zero and style to none didn't work out, as it still showed a tiny window, with an annoying shadow of what seems to be the border around a 0x0 window (tested on Windows 7). Therefore I'm providing this alternative option.
This is a dirty hack, but it should work, and doesn't have the downsides of changing the opacity :
set the WindowStartupLocation to Manual
set the Top and Left properties to somewhere outside the screen
set ShowInTaskbar to false so that the user doesn't realize there is a new window
Show and Hide the window
You should now be able to retrieve the HWND
EDIT: another option, probably better : set ShowInTaskBar to false and WindowState to Minimized, then show it : it won't be visible at all
I had already posted an answer to that question, but I just found a better solution.
If you just need to make sure that the HWND is created, without actually showing the window, you can do this:
public void InitHwnd()
{
var helper = new WindowInteropHelper(this);
helper.EnsureHandle();
}
(actually the EnsureHandle method wasn't available when the question was posted, it was introduced in .NET 4.0)
I've never tried to do what you are doing, but if you need to show the Window to get the HWND, but don't want to show it, set the Window Opacity to 0. This will also prevent any hit testing from occurring. Then you could have a public method on the Window to change the Opacity to 100 when you want to make it visible.
I know absolutely nothing about WPF, but could you create a message only window using other means (PInvoke for example) to receive the WM_HOTKEY message? If yes, then once you receive the WM_HOTKEY, you could launch the WPF window from there.
I've noticed that the last thing that happens when the window is being initialized, is the change of WindowState, if it differs from normal. So, you can actually make use of it:
public void InitializeWindow(Window window) {
window.Top = Int32.MinValue;
window.Left = Int32.MinValue;
window.Width = 0;
window.Height = 0;
window.ShowActivated = false;
window.ShowInTaskbar = false;
window.Opacity = 0;
window.StateChanged += OnBackgroundStateChanged;
window.WindowStyle = WindowStyle.None;
}
public void ShowWindow(Window window) {
window.Show();
window.WindowState = WindowState.Maximized;
}
protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
if (isStateChangeFirst) {
isStateChangeFirst = false;
window.Top = 300;
window.Left = 200;
window.Width = 760;
window.Height = 400;
window.WindowState = WindowState.Normal;
window.ShowInTaskbar = true;
window.Opacity = 1;
window.Activate();
}
}
That works fair enough for me. And it does not require working with any handles and stuff, and, more importantly, does not require to have a custom class for a window. Which is great for dynamically loaded XAML. And it is also a great way if you are making a fullscreen app. You do not even need to change its state back to normal or set proper width and height. Just go with
protected bool isStateChangeFirst = true;
protected void OnBackgroundStateChanged(object sender, EventArgs e) {
if (isStateChangeFirst) {
isStateChangeFirst = false;
window.ShowInTaskbar = true;
window.Opacity = 1;
window.Activate();
}
}
And you're done.
And even if I am wrong in my assumption that change of state is last thing done when window is being loaded, you can still change to any other event, it does not really matter.
The WindowInteropHelper class should allow you to get the HWND for the WPF window.
MyWindow win = new MyWindow();
WindowInteropHelper helper = new WindowInteropHelper(win);
IntPtr hwnd = helper.Handle;
MSDN Documentation
Another option in a similar vein to setting the opacity to 0, is to set the size to 0 and set the position to be off the screen. This won't require the AllowsTransparency = True.
Also remember that once you have shown it once you can then hide it and still get the hwnd.
Make the size of the window 0 x 0 px, put ShowInTaskBar to false, show it, then resize it when needed.
I've created extension method for showing invisible window, next Show calls will behave OK.
public static class WindowHelper
{
public static void ShowInvisible(this Window window)
{
// saving original settings
bool needToShowInTaskbar = window.ShowInTaskbar;
WindowState initialWindowState = window.WindowState;
// making window invisible
window.ShowInTaskbar = false;
window.WindowState = WindowState.Minimized;
// showing and hiding window
window.Show();
window.Hide();
// restoring original settings
window.ShowInTaskbar = needToShowInTaskbar;
window.WindowState = initialWindowState;
}
}
Start Wpf Window in Hidden mode:
WpfWindow w = new WpfWindow() { Visibility = Visibility.Hidden };
Start Wpf Window in Visible mode:
WpfWindow w = new WpfWindow();
w.Show();

Convert a MultipleMonitor Application From WinForms to WPF

I've got a little problem and tried to solve it now for nearly 6 to 8 hours but I didn't find any matching answer. I'm a complete newbie to WPF, so please point me any errors I made.
At first I have the following in my App.xaml.cs:
namespace WpfVideowand
{
public partial class App : Application
{
...
private void Application_Startup(object sender, StartupEventArgs e)
{
foreach (System.Windows.Forms.Screen MyScreen in System.Windows.Forms.Screen.AllScreens)
{
List<string> MyStrings = Xml.GetScreens(i);
if (MyStrings[1] == "true")
{
OpenWindow(MyScreen, MyStrings[0], i);
}
i++;
Shelf MyShelf = new Shelf(MyScreen, i, MyStrings[0]);
MyShelf.Show();
}
}
private void OpenWindow(System.Windows.Forms.Screen myScreen, string configName, int screenNumber)
{
Shelf NewShelf = new Shelf(myScreen, screenNumber, configName);
}
}
}
And inside the Shelf.xaml.cs it looks this way:
namespace WpfVideowand
{
public partial class Shelf : Window
{
[DllImport("user32.dll")]
static extern IntPtr GetActiveWindow();
System.Windows.Forms.Screen _Screen { get; set; }
...
public Shelf(System.Windows.Forms.Screen myScreen, int screenNumber, string configName)
{
InitializeComponent();
_Screen = myScreen;
ShowOnMonitor(screenNumber);
...
}
private void ShowOnMonitor(int screenNumber)
{
System.Windows.Forms.Screen[] ScreenArray;
ScreenArray = System.Windows.Forms.Screen.AllScreens;
int XCoord = Convert.ToInt32(ScreenArray[screenNumber].Bounds.Left);
this.Left = XCoord;
int YCoord = Convert.ToInt32(ScreenArray[screenNumber].Bounds.Top);
this.Top = XCoord;
IntPtr active = GetActiveWindow();
System.Windows.Application.Current.Windows.OfType<Window>().SingleOrDefault(window => new WindowInteropHelper(window).Handle == active).Name = "Monitor" + screenNumber.ToString();
System.Windows.Application.Current.Windows.OfType<Window>().SingleOrDefault(window => new WindowInteropHelper(window).Handle == active).WindowState = WindowState.Maximized;
}
...
}
}
The way described above worked fine in Windows Forms Application. In WPF I have the problem, that I get the error message, that rectangle (the window) would have no Top or Left property.
I even tried it in some other ways, like creating with
System.Windows.Forms.Screen _screen = System.Windows.Forms.Screen.FromControl(this);
an object, that would have .Top and .Left. But there I get the message, that I cannot convert a Shelf-object into a System.Windows.Forms.Control.
Anyone a suggestion, how I could make my Screens appear on the monitor where it should be?
Ok, I found it myself...
For anyone who is interested in finding some answers to this problem here it is:
At first, see that you have the correct reference implemented. For this you need System.Drawing and System.Windows.Forms. (Afterwards you have to declare many things explicit, like System.Windows.Controls.Button instead of Button, etc.)
Then see that you start the app with something like Startup="Application_Startup" and not an uri, because you want to start many forms and not only one.
Afterwards be absolutly sure to NOT set the Windowstyle to maximized in the XAML (this did cost me nearly 4 hours. In between i grew 56 grey hairs). Use this in the Code-Behind:
System.Windows.Application.Current.Windows.OfType().SingleOrDefault(window => new WindowInteropHelper(window).Handle == active).WindowState = WindowState.Maximized;

Form loses ability to be TopMost without activation (on Windows XP only)

I have an background WinForms application, which doesn't show any windows usually. But it shows a form on top most without activation, when occurred some app event. And I have a problem that when users clicks on the form and after this application closes this form and showes another instance of form, then last one shows without top most option (it shows under all opened windows). And I really don't understand why.
I noticed, that problem disappears when I run application with overloaded method:
Application.Run(new TestForm());
But my application is background and so I need to use follow overloaded method:
Application.Run();
And a problem appears again...
I maximally simplified my code to make my problem more clear.
Here is code of my form:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace winscr
{
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
this.Load += Form_Load;
}
void Form_Load(object sender, EventArgs e)
{
//-1 HWND_BOTTOM Places the window at the bottom of the Z order
//0x0010 SWP_NOACTIVATE Does not activate the window.
//0x0002 SWP_NOMOVE Retains the current position (ignores X and Y parameters).
//0x0001 SWP_NOSIZE Retains the current size (ignores the cx and cy parameters).
//always returns true
bool result = SetWindowPos(this.Handle, new IntPtr(-1), 0, 0, 0, 0, 0x0010 | 0x0002 | 0x0001);
this.Text = result + " " + DateTime.Now.ToString();
}
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
protected static extern bool SetWindowPos(
IntPtr hWnd, // window handle
IntPtr hWndInsertAfter, // placement-order handle
int X, // horizontal position
int Y, // vertical position
int cx, // width
int cy, // height
uint uFlags); // window positioning flags
protected override bool ShowWithoutActivation { get { return true; } }
}
}
And code of Program class:
using System;
using System.Windows.Forms;
namespace winscr
{
static partial class Program
{
static TestForm frm;
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//I use timer only for example on stackoverflow. In my real program form appears after particular events
Timer tmr = new System.Windows.Forms.Timer();
tmr.Interval = 2000;
tmr.Tick += new EventHandler(tmr_Tick);
tmr.Enabled = true;
Application.Run();
//Application.Run(new TestForm()); //if I use this overloading, then all works without any bugs
}
static void tmr_Tick(object sender, EventArgs e)
{
if (frm != null)
{
//close previous instance of form
frm.Close();
}
//construct and show new form
frm = new TestForm();
frm.Show();
}
}
}
Here is direct link to the project with this code (you can just run project and try to reproduce problem):
http://hidescreener.com/downloads/?r=form_top_most_tests.zip
And I recorded the video, in which I show this problem:
http://youtu.be/mxJYZE-oMDI
Please, help me to solve this problem. I have trying to solve it for 2 days.
P.S. I found like problem Really annoying bug with TopMost property in Windows Forms but there is no answer...
Updated:
Thanks for the Hans Passant's and King King's comments.
It was found that this problem only occurs in Windows XP.

App Bar Window pops away from docking postion, then moves into the docking position

I have recently added a window to my WPF application which can be docked to an edge of the desktop as an "app bar". The code I'm using to do the docking came from this stackoverflow post.
The program has three user settings defined related to this window. One is the edge where the window is docked, the other two are the values of the Left & Top properties. The idea is that when the window is closed, or the program is shut down, the window will open back in the same state and location when the program restarts.
The problem I'm having is that when the program opens up, the window is first displayed at a random location on the screen (probably the coordinates assigned to it by Windows when the window is created) and then it moves into the docked position. Other programs I've seen that have the app bar functionality, like Trillian, are drawn in the docked position from the beginning. It's a little disconcerting to see the window move like that.
Here is some code from the window:
private void AppBarWindow_Activated( object sender, EventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
AppBarFunctions.SendShellActivated( this );
}
}
private void AppBarWindow_Closing( object sender, CancelEventArgs e ) {
Settings.Default.AppBarWindowLeft = Left;
Settings.Default.AppBarWindowTop = Top;
Settings.Default.Save();
AppBarFunctions.SetAppBar( this, ABEdge.None );
// Other, app specific code . . .
}
private void AppBarWindow_LocationChanged( object sender, EventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
AppBarFunctions.SendShellWindowPosChanged( this );
}
}
private void AppBarWindow_SourceInitialized( object sender, EventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
SizeWindow( Settings.Default.AppBarWindowEdge == ABEdge.None ? ABEdge.Left : ABEdge.None );
}
}
private void AppBarWindow_SizeChanged( object sender, SizeChangedEventArgs e ) {
if ( Settings.Default.AppBarWindowEdge != ABEdge.None ) {
AppBarFunctions.SendShellWindowPosChanged( this );
}
}
private void SizeWindow( ABEdge originalEdge ) {
// App specific code to compute the window's size . . .
if ( originalEdge != Settings.Default.AppBarWindowEdge ) {
AppBarFunctions.SetAppBar( this, Settings.Default.AppBarWindowEdge );
}
Settings.Default.AppBarWindowLeft = Left;
Settings.Default.AppBarWindowTop = Top;
Settings.Default.Save();
}
I have added functions to call SHAppBarrMessage when the window is activated, or when its position and size change, as I read in this acrticle. The calls don't seem to have any effect on the behavior, so I might remove them.
I know that the SourceInitialized and Loading events are called before the window is displayed but after the window handle and the layout & measure passes have been completed. It appears, though, that the window is rendered before the call to AppBarFunctions.SetAppBar is made, which is why I see it appear and then move into place.
I've also tried to move the window into the docked position by setting the Left and Top properties to the values saved in the settings in the window's constructor. That didn't work, either. In fact, it was worse, as the window was first drawn in the docked position, then apparently was moved away from that desktop edge to make room for it, and then moved back into the docked location.
How do I get this window to appear in the docked position upon start up and not move afterward?
Edit:
I think I have found the cause of the problem. There is a comment in the AppBarFunctions class code, in the ABSetPos method, just before it schedules a call to the DoResize method on the window's Dispatcher (UI thread). The comment reads:
// This is done async, because WPF will send a resize after a new appbar is added.
// if we size right away, WPFs resize comes last and overrides us.
So apparently WPF or Windows is moving the window out of the space being reserved for the window, and I then move it back in. I added a lot of trace points in my code & I can see that the window isn't rendered until after that move is made (the one mentioned in the comment in the code). After the window is rendered, it is moved into the docked position by my code.
The AppBarFunctions class already adds a window procedure hook for wathcing for messages from the shell. If I add a check for WM_WINDOWPOSCHANGED, could I somehow stop the message from being processed? Or maybe I can change the values for the Left and Top properties for the move done by Windows / WPF so the window ends up where I want it to be?
I've found a way to keep the window from moving from the docked area. Basically, the code I'm using already uses a Window Procedure Hook method to watch for ABN_* notification messages. I added code in this method to watch for WM_WINDOWPOSCHANGING messages.
Here's the code I wrote:
public IntPtr WindowProcedureHook( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled ) {
if ( msg == (int) WinMessages.WM_WINDOWPOSCHANGING ) {
if ( IsDocked && !IsDragging ) {
WindowPos pos = (WindowPos) Marshal.PtrToStructure( lParam, typeof( WindowPos ) );
// Keep this window in its docked position.
pos.x = (int) DockedPosition.X;
pos.y = (int) DockedPosition.Y;
pos.cx = (int) DockedSize.Width;
pos.cy = (int) DockedSize.Height;
Marshal.StructureToPtr( pos, lParam, false );
handled = true;
}
} else if ( msg == CallbackId ) {
if ( wParam.ToInt32() == (int) ABNotify.ABN_WINDOWPOSCHANGED ) {
SetDockedPosition( Window, this, true );
handled = true;
}
}
return IntPtr.Zero;
}
When the window is docked to an edge & registered with the shell, it remembers the docking rectangle returned from the call to SHAppBarMessage / ABM_SETPOS. When the method receives a WM_WINDOWPOSCHANGED message, it checks to see if the window is docked along an edge and is not being dragged. If it is, it marshals the WINDOWPOS structure from unmanaged memory into a managed object, sets the position & size of the window back to the docked position & size, and marshals it back to unmanaged memory. It then sets handled to true & exits.
This works perfectly & keeps the window from bouncing away from its docked position & back in. And there is no need to schedule a move into the docked position on the window's Dispatcher thread.

Show a Form without stealing focus?

I'm using a Form to show notifications (it appears at the bottom right of the screen), but when I show this form it steals the focus from the main Form. Is there a way to show this "notification" form without stealing focus?
Hmmm, isn't simply overriding Form.ShowWithoutActivation enough?
protected override bool ShowWithoutActivation
{
get { return true; }
}
And if you don't want the user to click this notification window either, you can override CreateParams:
protected override CreateParams CreateParams
{
get
{
CreateParams baseParams = base.CreateParams;
const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOOLWINDOW = 0x00000080;
baseParams.ExStyle |= ( int )( WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW );
return baseParams;
}
}
Stolen from PInvoke.net's ShowWindow method:
private const int SW_SHOWNOACTIVATE = 4;
private const int HWND_TOPMOST = -1;
private const uint SWP_NOACTIVATE = 0x0010;
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
int hWnd, // Window handle
int hWndInsertAfter, // Placement-order handle
int X, // Horizontal position
int Y, // Vertical position
int cx, // Width
int cy, // Height
uint uFlags); // Window positioning flags
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
static void ShowInactiveTopmost(Form frm)
{
ShowWindow(frm.Handle, SW_SHOWNOACTIVATE);
SetWindowPos(frm.Handle.ToInt32(), HWND_TOPMOST,
frm.Left, frm.Top, frm.Width, frm.Height,
SWP_NOACTIVATE);
}
(Alex Lyman answered this, I'm just expanding it by directly pasting the code. Someone with edit rights can copy it over there and delete this for all I care ;) )
This is what worked for me. It provides TopMost but without focus-stealing.
protected override bool ShowWithoutActivation
{
get { return true; }
}
private const int WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= WS_EX_TOPMOST;
return createParams;
}
}
Remember to omit setting TopMost in Visual Studio designer, or elsewhere.
This is stolen, err, borrowed, from here (click on Workarounds):
https://connect.microsoft.com/VisualStudio/feedback/details/401311/showwithoutactivation-is-not-supported-with-topmost
If you're willing to use Win32 P/Invoke, then you can use the ShowWindow method (the first code sample does exactly what you want).
Doing this seems like a hack, but it seems to work:
this.TopMost = true; // as a result the form gets thrown to the front
this.TopMost = false; // but we don't actually want our form to always be on top
Edit: Note, this merely raises an already created form without stealing focus.
The sample code from pinvoke.net in Alex Lyman/TheSoftwareJedi's answers will make the window a "topmost" window, meaning that you can't put it behind normal windows after it's popped up. Given Matias's description of what he wants to use this for, that could be what he wants. But if you want the user to be able to put your window behind other windows after you've popped it up, just use HWND_TOP (0) instead of HWND_TOPMOST (-1) in the sample.
In WPF you can solve it like this:
In the window put these attributes:
<Window
x:Class="myApplication.winNotification"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Notification Popup" Width="300" SizeToContent="Height"
WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True" Focusable="False" ShowActivated="False" >
</Window>
The last one attribute is the one you need ShowActivated="False".
I have something similar, and I simply show the notification form and then do
this.Focus();
to bring the focus back on the main form.
Create and start the notification Form in a separate thread and reset the focus back to your main form after the Form opens. Have the notification Form provide an OnFormOpened event that is fired from the Form.Shown event. Something like this:
private void StartNotfication()
{
Thread th = new Thread(new ThreadStart(delegate
{
NotificationForm frm = new NotificationForm();
frm.OnFormOpen += NotificationOpened;
frm.ShowDialog();
}));
th.Name = "NotificationForm";
th.Start();
}
private void NotificationOpened()
{
this.Focus(); // Put focus back on the original calling Form
}
You can also keep a handle to your NotifcationForm object around so that it can be programmatically closed by the main Form (frm.Close()).
Some details are missing, but hopefully this will get you going in the right direction.
This works well.
See: OpenIcon - MSDN and SetForegroundWindow - MSDN
using System.Runtime.InteropServices;
[DllImport("user32.dll")]
static extern bool OpenIcon(IntPtr hWnd);
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
public static void ActivateInstance()
{
IntPtr hWnd = IntPtr hWnd = Process.GetCurrentProcess().MainWindowHandle;
// Restore the program.
bool result = OpenIcon(hWnd);
// Activate the application.
result = SetForegroundWindow(hWnd);
// End the current instance of the application.
//System.Environment.Exit(0);
}
You might want to consider what kind of notification you would like to display.
If it's absolutely critical to let the user know about some event, using Messagebox.Show would be the recommended way, due to its nature to block any other events to the main window, until the user confirms it. Be aware of pop-up blindness, though.
If it's less than critical, you might want to use an alternative way to display notifications, such as a toolbar on the bottom of the window. You wrote, that you display notifications on the bottom-right of the screen - the standard way to do this would be using a balloon tip with the combination of a system tray icon.
You can handle it by logic alone too, although I have to admit that the suggestions above where you end up with a BringToFront method without actually stealing focus is the most elegant one.
Anyhow, I ran into this and solved it by using a DateTime property to not allow further BringToFront calls if calls were made already recently.
Assume a core class, 'Core', which handles for example three forms, 'Form1, 2, and 3'. Each form needs a DateTime property and an Activate event that call Core to bring windows to front:
internal static DateTime LastBringToFrontTime { get; set; }
private void Form1_Activated(object sender, EventArgs e)
{
var eventTime = DateTime.Now;
if ((eventTime - LastBringToFrontTime).TotalMilliseconds > 500)
Core.BringAllToFront(this);
LastBringToFrontTime = eventTime;
}
And then create the work in the Core Class:
internal static void BringAllToFront(Form inForm)
{
Form1.BringToFront();
Form2.BringToFront();
Form3.BringToFront();
inForm.Focus();
}
On a side note, if you want to restore a minimized window to its original state (not maximized), use:
inForm.WindowState = FormWindowState.Normal;
Again, I know this is just a patch solution in the lack of a BringToFrontWithoutFocus. It is meant as a suggestion if you want to avoid the DLL file.
I don't know if this is considered as necro-posting, but this is what I did since I couln't get it working with user32's "ShowWindow" and "SetWindowPos" methods. And no, overriding "ShowWithoutActivation" doesn't work in this case since the new window should be always-on-top.
Anyway, I created a helper method that takes a form as parameter; when called, it shows the form, brings it to the front and makes it TopMost without stealing the focus of the current window (apparently it does, but the user won't notice).
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern IntPtr SetForegroundWindow(IntPtr hWnd);
public static void ShowTopmostNoFocus(Form f)
{
IntPtr activeWin = GetForegroundWindow();
f.Show();
f.BringToFront();
f.TopMost = true;
if (activeWin.ToInt32() > 0)
{
SetForegroundWindow(activeWin);
}
}
I know it may sound stupid, but this worked:
this.TopMost = true;
this.TopMost = false;
this.TopMost = true;
this.SendToBack();
I needed to do this with my window TopMost. I implemented the PInvoke method above but found that my Load event wasn't getting called like Talha above. I finally succeeded. Maybe this will help someone. Here is my solution:
form.Visible = false;
form.TopMost = false;
ShowWindow(form.Handle, ShowNoActivate);
SetWindowPos(form.Handle, HWND_TOPMOST,
form.Left, form.Top, form.Width, form.Height,
NoActivate);
form.Visible = true; //So that Load event happens
You don't need to make it anywhere near as complicated.
a = new Assign_Stock();
a.MdiParent = this.ParentForm;
a.Visible = false; //hide for a bit.
a.Show(); //show the form. Invisible form now at the top.
this.Focus(); //focus on this form. make old form come to the top.
a.Visible = true; //make other form visible now. Behind the main form.
Github Sample
Form.ShowWithoutActivation Property
Add this in your child form class
protected override bool ShowWithoutActivation
{
get { return true; }
}
Working Code
Form2
public partial class Form2 : Form
{
Form3 c;
public Form2()
{
InitializeComponent();
c = new Form3();
}
private void textchanged(object sender, EventArgs e)
{
c.ResetText(textBox1.Text.ToString());
c.Location = new Point(this.Location.X+150, this.Location.Y);
c .Show();
//removethis
//if mdiparent 2 add this.focus() after show form
c.MdiParent = this.MdiParent;
c.ResetText(textBox1.Text.ToString());
c.Location = new Point(this.Location.X+150, this.Location.Y);
c .Show();
this.Focus();
////-----------------
}
}
Form3
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
//ShowWithoutActivation = false;
}
protected override bool ShowWithoutActivation
{
get { return true; }
}
internal void ResetText(string toString)
{
label2.Text = toString;
}
}
When you create a new form using
Form f = new Form();
f.ShowDialog();
it steals focus because your code can't continue executing on the main form until this form is closed.
The exception is by using threading to create a new form then Form.Show(). Make sure the thread is globally visible though, because if you declare it within a function, as soon as your function exits, your thread will end and the form will disappear.
Figured it out: window.WindowState = WindowState.Minimized;.

Categories

Resources