WPF child window Aero glass displays incorrectly - c#

I have a WPF window that enables glass on itself during its SourceInitialized event. This works perfectly. I'll use the simplest example possible (just one window object) to demonstrate where the issue is.
public partial class MainWindow : Window
{
public bool lolz = false;
public MainWindow()
{
InitializeComponent();
this.SourceInitialized += (x, y) =>
{
AeroExtend(this);
};
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (!lolz)
{
MainWindow mw = new MainWindow();
mw.lolz = true;
mw.ShowDialog();
}
}
}
This creates two MainWindows. When I debug this in Visual Studio everything works as expected.
When I run without debugging, not so much.
The child window has an odd, incorrectly applied glass frame... but only when running it directly without Visual Studio debugging. Same code ran twice but with different results. It doesn't matter when I create the second window, I've tied it to a button click with the same output.
Any ideas?
EDIT: Here is an excerpt of the code I'm using for AeroExtend
[DllImport("dwmapi.dll")]
private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMargins);
[DllImport("dwmapi.dll", PreserveSig = false)]
private static extern bool DwmIsCompositionEnabled();
[StructLayout(LayoutKind.Sequential)]
private class MARGINS
{
public MARGINS(Thickness t)
{
cxLeftWidth = (int)t.Left;
cxRightWidth = (int)t.Right;
cyTopHeight = (int)t.Top;
cyBottomHeight = (int)t.Bottom;
}
public int cxLeftWidth, cxRightWidth,
cyTopHeight, cyBottomHeight;
}
...
static public bool AeroExtend(this Window window)
{
if (Environment.OSVersion.Version.Major >= 6 && DwmIsCompositionEnabled())
{
IntPtr mainWindowPtr = new WindowInteropHelper(window).Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
mainWindowSrc.CompositionTarget.BackgroundColor = Colors.Transparent;
window.Background = System.Windows.Media.Brushes.Transparent;
MARGINS margins = new MARGINS(new Thickness(-1));
int result = DwmExtendFrameIntoClientArea(mainWindowSrc.Handle, ref margins);
if (result < 0)
{
return false;
}
return true;
}
return false;
}

The problem is that you have MARGINS defined as a class. You'll notice that if you tried using a different set of values for the margin (e.g. 10 pixels on each edge) that it will still try to fill the entire area. Also as I mentioned in my comment the other day, you will notice that you have an artifact in the lower right corner even in the original window that wasn't shown modally. If you simply change the MARGINS from a class to a struct then the problem does not occur. e.g.
[StructLayout(LayoutKind.Sequential)]
private struct MARGINS
Alternatively you could leave MARGINS a class but then you should change the way the DwmExtendFrameIntoClientArea is defined. e.g.
[DllImport("dwmapi.dll")]
private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStruct)] MARGINS pMargins);

Related

How to show a window on top of all windows one time in WPF?

I have a singletone instance of window in my WPF application (which is not main window). Due to it's structure, this window only closes when the main window is closed; If the user closes this window, it becomes hidden. When I click on some image in main window, I want the following behaviour of second window:
If window is hidden and image was clicked, I want to show it on top of all windows (but NOT by setting Topmost = true, I want just SHOW it on top, rather than fix it on top forever).
If window is shown on top, there is nothing to do.
If window is open, but covered by other window or minimized, I also want to show it on top only ONCE.
What I have at the moment:
// In some application class
private void Image_MouseDown(object sender, MouseButtonEventArgs e)
{
if (App.Current.MyWindow == null)
{
App.Current.MyWindow = WeightImageWindowView.Instance;
}
App.Current.MyWindow.ShowTop();
}
...
// in MyWindow class
public void ShowTop()
{
this.Topmost = true;
this.Show();
if (this.WindowState == WindowState.Minimized)
{
this.WindowState = WindowState.Normal;
}
var a = this.Activate();
var b = this.Focus();
this.Topmost = false;
}
I tried to use all these commands one by one, in pairs and all together, but didn't get the behaviour described above.
WndHelper.BringToFront(this);
Use the helper below and just call bringtofront
public class WndHelper
{
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindow(IntPtr hWnd, uint nCmdShow);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsIconic(IntPtr hWnd);
public static void HideWindow(Window window)
{
var wih = new WindowInteropHelper(window);
WndHelper.ShowWindow(wih.Handle, 0);
}
public static void BringToFront(Window window)
{
var wih = new WindowInteropHelper(window);
BringToFront(wih.Handle);
}
//If the window is minimized, then Restore must be run first
//Windows that are in the background are enough to set as Foreground
public static void BringToFront(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
return;
try
{
if (WndHelper.IsIconic(hWnd)) //the window is minimized
{
WndHelper.ShowWindow(hWnd, 9); // SW_RESTORE = 9
WndHelper.SetForegroundWindow(hWnd); // set window to foreground
}
else
WndHelper.SetForegroundWindow(hWnd);
}
catch { }
}
}
Thanks #Villiam! I found the following solution:
Set MyWindow.ShowActivated = true;
Rewrite ShowTop method as:
public void ShowTop()
{
if (this.WindowState == WindowState.Minimized)
{
this.WindowState = WindowState.Normal;
}
this.Topmost = true;
this.Show();
this.Topmost = false;
}

c# Detect if specific window (IE Browser, file explorer etc) closes/user presses X button?

Currently doing a WPF app where you will be able to open apps suchs as IE and file explorer (more to come), and when you start it, a button is created and put in a custom appbar/taskbar.
So to the problem, we are using Process.Start() to start the apps, so we are currently checking this
this.ProgramProcess.EnableRaisingEvents = true;
this.ProgramProcess.Exited += (sender, e) => { RemoveRunningProgram((Process)sender); };
and the RemoveRunningProgram method looks like this
private void RemoveRunningProgram(Process process)
{
App.Current.Dispatcher.Invoke((Action)delegate { TaskbarWindow.RunningPrograms.Remove(this); });
}
So the main thought is, if you close a window by the user pressing the X button in the top right corner to close the window, the appbar button/icon will disappear.
So couple of the problems;
If you open 1 IE browser, it works fine, but if you open more than one, you will have to close all windows, for all the icons to disappear.
It takes a LONG time for the computer to see if the file explorer is closed or not, sometimes not even notice it.
Am I thinking at the right spot where I should make adjustments?
RunningProgram.cs:
public class RunningProgram : Button
{
private IntPtr ProgramHandle { get; set; }
private string ProgramName { get; set; }
public Process ProgramProcess { get; set; }
public List<Process> procList = new List<Process>();
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int lParam);
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hwnd);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public RunningProgram(IntPtr programHandle, string programName, Thickness margin, string imagePath, Process programProcess)
{
this.ProgramHandle = programHandle;
this.ProgramName = programName;
this.Margin = margin;
this.Background = SetButtonBackground(imagePath);
this.Width = 70;
this.Height = 70;
this.Click += new RoutedEventHandler(ShowWindow_Click);
this.BorderBrush = Brushes.Transparent;
this.ProgramProcess = programProcess;
this.procList.Add(ProgramProcess);
this.ProgramProcess.EnableRaisingEvents = true;
this.ProgramProcess.Exited += (sender, e) => { RemoveRunningProgram((Process)sender); };
}
private void p_Exited(object sender, EventArgs e)
{
RemoveRunningProgram((Process)sender);
}
private void RemoveRunningProgram(Process process)
{
App.Current.Dispatcher.Invoke((Action)delegate { TaskbarWindow.RunningPrograms.Remove(this); });
}
private void ShowWindow_Click(object sender, RoutedEventArgs e)
{
ShowWindow(ProgramHandle, 3);
SetForegroundWindow(ProgramHandle);
GetForegroundWindow();
}
}
Thanks in advance

How to force ComboBox to scroll to top?

I have a winforms ToolStripComboBox with a ComboBox property. By default, it seems to auto-scroll to the selected index. See screenshot below:
On form load, I'm setting SelectedIndex to 1, which is what needs to happen. But I want the first item in the list (SelectedIndex 0) to be visible, or in other words auto-scroll to the very top. I can't find any way to force the combobox to scroll to the top by default, or to do so programmatically. There is an AutoScrollOffset property on ComboBox which I have experimented with, but it seems to do nothing, no matter what I set it to.
As seen in my screenshot above, I want to force the combobox (either via property or method call) to appear like the 2nd pic in which the top item (All - All Categories) is visible, while still leaving index 1 selected.
How can this be done?
When you open the dropdown, a LB_SETTOPINDEX message will be sent to the list which is in the dropdown menu. This message is responsible to setting the top index in the list.
You can handle this message and change its WParam to Intptr.Zero to always use 0 as top index.
Native Methods
Here is a class which contains native methods, structures and constants to manipulate the combo box for this purpose:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
public struct COMBOBOXINFO
{
public int cbSize;
public RECT rcItem;
public RECT rcButton;
public int stateButton;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; public int Top; public int Right; public int Bottom;
}
[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
public class ListBoxHelper : NativeWindow
{
private const int LB_SETTOPINDEX = 0x0197;
public ListBoxHelper(IntPtr hwnd) { this.AssignHandle(hwnd); }
protected override void WndProc(ref Message m)
{
if (m.Msg == LB_SETTOPINDEX)
m.WParam = IntPtr.Zero;
base.WndProc(ref m);
}
}
}
ComboBox
Here is a ComboBox which its dropdown always opens showing item 0 as top item:
public class MyComboBox : ComboBox
{
NativeMethods.ListBoxHelper listBoxHelper;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
var info = new NativeMethods.COMBOBOXINFO();
info.cbSize = Marshal.SizeOf(info);
NativeMethods.GetComboBoxInfo(this.Handle, ref info);
listBoxHelper = new NativeMethods.ListBoxHelper(info.hwndList);
}
}
ToolStripComboBox
ToolStripComboBox hosts a ComboBox inside. So the solution is similar:
public class MyToolStripComboBox : ToolStripComboBox
{
public MyToolStripComboBox()
{
this.Control.HandleCreated += Control_HandleCreated;
}
NativeMethods.ListBoxHelper listBoxHelper;
private void Control_HandleCreated(object sender, EventArgs e)
{
base.OnVisibleChanged(e);
var info = new NativeMethods.COMBOBOXINFO();
info.cbSize = Marshal.SizeOf(info);
NativeMethods.GetComboBoxInfo(this.Control.Handle, ref info);
listBoxHelper = new NativeMethods.ListBoxHelper(info.hwndList);
}
}

On start-up taskbar covers full screen C# application

I written a Kiosk style C# application using visual studio that is run on startup and should expand to full-screen and cover the task-bar.
I am doing the usual setting boarder style to none, and fill extents and it works perfectly if I just launch the application manually.
When the application launches on startup (by way of a short-cut in the startup folder in the start menu), the task-bar ends up on top of the program and clicking somewhere on the form does not bring the form back to the top.
Has anyone encountered this problem before, or know of possible workarounds.
I have also done this another time:
public class Screensize
{
/// <summary>
/// Selected Win AI Function Calls
/// </summary>
public class WinApi
{
[DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
public static extern int GetSystemMetrics(int which);
[DllImport("user32.dll")]
public static extern void
SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter,
int X, int Y, int width, int height, uint flags);
private const int SM_CXSCREEN = 0;
private const int SM_CYSCREEN = 1;
private static IntPtr HWND_TOP = IntPtr.Zero;
private const int SWP_SHOWWINDOW = 64; // 0x0040
public static int ScreenX
{
get { return GetSystemMetrics(SM_CXSCREEN); }
}
public static int ScreenY
{
get { return GetSystemMetrics(SM_CYSCREEN); }
}
public static void SetWinFullScreen(IntPtr hwnd)
{
SetWindowPos(hwnd, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW);
}
}
/// <summary>
/// Class used to preserve / restore state of the form
/// </summary>
public class FormState
{
private FormWindowState winState;
private FormBorderStyle brdStyle;
private bool topMost;
private Rectangle bounds;
private bool IsMaximized = false;
public void Maximize(Form targetForm)
{
if (!IsMaximized)
{
IsMaximized = true;
Save(targetForm);
targetForm.WindowState = FormWindowState.Maximized;
targetForm.FormBorderStyle = FormBorderStyle.None;
targetForm.TopMost = false;
WinApi.SetWinFullScreen(targetForm.Handle);
}
}
public void Save(Form targetForm)
{
winState = targetForm.WindowState;
brdStyle = targetForm.FormBorderStyle;
topMost = targetForm.TopMost;
bounds = targetForm.Bounds;
}
public void Restore(Form targetForm)
{
targetForm.WindowState = winState;
targetForm.FormBorderStyle = brdStyle;
targetForm.TopMost = topMost;
targetForm.Bounds = bounds;
IsMaximized = false;
}
}
and just call in your form:
screensize.Maximize(this)
I think you mean this
And now I see this post is from 2013...
Workaround: When starting, kill explorer.exe - you don't need it -> taskbar disappear.
When needed, start it using Task Manager

C# Programmatically Unminimize form [duplicate]

This question already has answers here:
What is the "right" way to bring a Windows Forms Application to the foreground?
(12 answers)
Closed 4 years ago.
How do I take a form that is currently minimized and restore it to its previous state. I can't find any way to determine if its previous WindowState was Normal or Maximized; but I know the information has to be stored somewhere because windows doesn't have a problem doing it with apps on the taskbar.
There is no managed API for this. The way to do it is to PInvoke GetWindowPlacement and check for WPF_RESTORETOMAXIMIZED.
For details, see this Microsoft How To (which is demonstrating the technique in VB).
In C#, this would be:
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
private struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public System.Drawing.Point ptMinPosition;
public System.Drawing.Point ptMaxPosition;
public System.Drawing.Rectangle rcNormalPosition;
}
public void RestoreFromMinimzied(Form form)
{
const int WPF_RESTORETOMAXIMIZED = 0x2;
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(form.Handle, ref placement);
if ((placement.flags & WPF_RESTORETOMAXIMIZED) == WPF_RESTORETOMAXIMIZED)
form.WindowState = FormWindowState.Maximized;
else
form.WindowState = FormWindowState.Normal;
}
this.WindowState = FormWindowState.Normal;
You also have:
this.WindowState = FormWindowState.Minimized;
this.WindowState = FormWindowState.Maximized;
Ah, I misunderstood the question:
Restore WindowState from Minimized should be what you're looking for. It says you can mimic the taskbar behavior like this:
SendMessage(form.Handle, WM_SYSCOMMAND, SC_RESTORE, 0);
You can track the window state changes through the Resize event. Like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
prevState = currState = this.WindowState;
}
protected override void OnResize(EventArgs e) {
if (currState != this.WindowState) {
prevState = currState;
currState = this.WindowState;
}
base.OnResize(e);
}
private FormWindowState prevState, currState;
}
If you want to store the previous state whenever there's a change (maximize/minimize), you'll have to hook into the SizeChanged event, according to this post on MSDN. You can get the WindowState there and store it.

Categories

Resources