Rendering issue in WPF window while dragging - wpf

I have WPF window in which my Direct3D engine render. WPF Window I start from my engine, which I develop on C++/CLI.
I have 2 displays and when I move window between them, part of the window became white and not update until I am not stop dragging it (like on screen shot).
Does somebody know how to fix this issue?
It will be hard to post code here as it is a lot of code, but I will try:
I use managed C++ and unmanaged in one project:
So, I add .Net dll`s to have access to the WPF window:
Window^ wnd = gcnew Window();
wnd->Width = 1280;
wnd->Height = 720;
wnd->Show();
WindowInteropHelper^ helper = gcnew WindowInteropHelper(wnd);
HWND WPFHwnd = (HWND)helper->Handle.ToPointer();
HINSTANCE hinstance = (HINSTANCE)Marshal::GetHINSTANCE(this->GetType()->Module).ToPointer();
engine->SetHisnstanceHWND(WPFHwnd, hinstance);
engine->InitializeSystem(wnd->ActualWidth, wnd->ActualHeight);
engine - this is unmanaged class which incapsulates all work with rendering.
Further it is a stardard d3d rendering loop. Nothing special.
It render OK, but when I resizing or move window, it draw a part of it like this (with white). When I stop resizing or moving - all OK again.
My question is: is it possible to get rid of this effect or not?
UPDATE:
I hooked the wndProc if the WPF window and try to use UpdateLayout() when size changing, but this has no effect. Seems window is not react on any intervention.

Well, it sounds like your engine is receiving WM_ERASEBKGND and it clears the window. After you release the window, it sends WM_PAINT and everything is clear again. That's what I believe might be happening.
In order to test this theory, you can handle that message and return true. Put that to your engine message handling routine:
case WM_ERASEBKGND:
{
PAINTSTRUCT pss;
BeginPaint(hWnd,&pss);
EndPaint(hWnd,&pss);
return true;
}
You can use WinSpy++ to check out what messages are delivered to underneath window. If you want to see why the background is whited out, you can do it through ProcessMonitor and see what API calls are being issued. You're probably interested in API calls that include the color white.

Related

parts drawn with DrawThemeBackground on Windows 10 are not correct

Problem description
I want to create a Windows API app in C which renders the menu and the caption buttons in the same non-client area, similar to Firefox
In order to do this, I've determined the solution needs to:
be type WS_POPUP, in order for the menu to be aligned to the top
take ownership of the non-client area (where the menu is rendered)
manually render the minimize/maximize/close buttons
The solution needs to work on Windows 7, 8, and 10 (and ideally future versions too).
How it looks now
I have a test program available on GitHub.
In my app, I've overridden the appropriate events:
WM_NCCALCSIZE, WM_NCHITTEST, WM_NCLBUTTONDOWN, WM_NCLBUTTONUP, WM_NCMOUSEMOVE, WM_NCPAINT
And then I repaint non-client areas on these events:
WM_NCACTIVATE, WM_SETTEXT
Here's an example of how I'm doing the rendering:
// globals set elsewhere
RECT customAreaRect, minRect, maxRect, closeRect, coverMenuRect;
BOOL maximized;
// ...
LRESULT OnPaintNCA(HWND hWnd, WPARAM wParam, LPARAM lParam) {
RECT windowRect;
HRGN hRgn = NULL;
GetWindowRect(hWnd, &windowRect);
if (wParam == 1) {
hRgn = CreateRectRgnIndirect(&windowRect);
} else {
hRgn = (HRGN)wParam;
}
if (hRgn) {
// Carve out the area for custom content
HRGN captionButtonRgn = CreateRectRgnIndirect(&customAreaRect);
CombineRgn(hRgn, hRgn, captionButtonRgn, RGN_XOR);
DeleteObject(captionButtonRgn);
// Force default painting for non-client area
LRESULT ret = DefWindowProc(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
// black background covering part of menu, behind buttons
HDC hDC = GetWindowDC(hWnd);
FillRect(hDC, &coverMenuRect, (HBRUSH)GetStockObject(BLACK_BRUSH));
HTHEME hTheme = OpenThemeData(hWnd, TEXT("WINDOW"));
DrawThemeBackground(hTheme, hDC, WP_MINBUTTON, partState, minRect, NULL);
DrawThemeBackground(hTheme, hDC, maximized ? WP_RESTOREBUTTON : WP_MAXBUTTON, partState, maxRect, NULL);
DrawThemeBackground(hTheme, hDC, WP_CLOSEBUTTON, partState, closeRect, NULL);
CloseThemeData(hTheme);
}
}
The rendered result looks like this:
Unfortunately, the styles used for the parts (minimize, maximize/restore, close) look like the styles for Windows 7/8, and not the native Windows 10 controls. I've been searching for a way to do this for several days without luck. I need help understanding how to render these buttons for Windows 10 using the Windows API.
Current status (and what I've tried so far)
My first hunch was that I need to properly enable Visual Styles.
Per this article, calls checking the OS version will get Windows 8 unless you specifically target Windows 10 via a manifest. Click here to view my manifest. This does work:
Before GetVersionEx returned major=6, minor=2, build=9200
Now GetVersionEx returns major=10, minor=0, build=10586
Per the official "Enabling Visual Styles" article, I made sure to use Common Controls version 6.
Added linker input for Comctl32.lib
Call is made to InitCommonControls on program start
Added dependency for version 6 to the application manifest
Here are some screenshots of relevant project settings that I have tried:
Other ideas
I'm running low on things to try. Before throwing in the towel, there were some things I was going to try:
Idea 1: using GetThemeStream which allows you to retrieve the size/bitmap for controls.
Load aero msstyles file like so:
HMODULE themeFile = LoadLibraryEx(TEXT("C:\\Windows\\Resources\\Themes\\aero\\aero.msstyles"), NULL, LOAD_LIBRARY_AS_DATAFILE);
Get the bitmap for the part (minimize button, maximize button, etc) like so (passing the loaded theme file):
GetThemeStream(h, WP_MAXBUTTON, MAXBS_NORMAL, TMT_DISKSTREAM, (void**)&buffer, &bufferSize, themeFile);
Load the bitmap; it appears to be in PNG format (I haven't gotten this far)
Draw the bitmap
Idea 2: copy the non-client area from a hidden window which has the caption area (and minimize, maximize, close buttons).
Create a window which has the caption and min/max buttons, never activating it.
In the non-client paint, get the DC for that Window and capture the pixels for the min/max/close button
Render them using bitblt
I think the issue comes from trying to use WM_NCPAINT on OS version >= Win Vista.
Since Vista all NC rendering is controlled by DWM (desktop window manager). If you still dare to handle WM_NCPAINT, DWM rendering will be turned off and you get "old-school" look:
From the Shell Revealed Blog:
The DWM doesnt have any legacy worries because applications cannot
draw inside the glass frame, since its rendered and managed by a
totally different process. If an application tries to do it, Windows
will detect it and remove the glass frame entirely (and therefore
revert to the Basic frame), so that the application can draw what it
wants to draw.
To get proper results, you have to do it the "DWM way" (specifically the sections "Removing the Standard Frame" and "Drawing in the Extended Frame Window"). This works by letting DWM render the frame within the client area, so you can paint over it. Also with this solution you don't have to draw the caption buttons on your own. This answer summarizes the required steps (under "Aero supported solution").
The caveat is that you propably have to draw the menu on your own and you can't use most of GDI API for that, because GDI ignores the alpha channel and things will look ugly if the frame is translucent (Vista and Win 7 by default, Win8+ with extensions). BitBlt() works if the source is a memory DC that contains a 32bpp bitmap with an alpha channel. GDI+ works aswell.

Is this how I make custom controls both transparent and flicker-free in Windows? Or do I have one of these steps wrong?

I could begin by asking the question outright or by citing my sources (this, this, this, and this) descriptively, but I'll walk you ll through what I'm trying to do instead.
Let's start with a main window. It has its own window class whose hbrBackground is set to COLOR_BTNFACE + 1. Now let's do
EnableThemeDialogTexture(hwnd, ETDT_ENABLE | ETDT_USETABTEXTURE)
so the tab control we're about to add will be drawn with visual styles. (Try Windows XP with the standard Luna theme for best results.) Now let's add a tab control and two tabs.
On the first tab, we create an instance (let's call it container) of a new window class. This window class is going to hold various controls. I could set hbrBackground to COLOR_BTNFACE + 1, but then it will draw over the tab background. So I want this new child window to be transparent. So what I do is
set the class hbrBackground to GetStockObject(HOLLOW_BRUSH)
set container's extended style to WS_EX_TRANSPARENT
set the class WM_ERASEBKGND handler to do SetBkMode((HDC) wParam, TRANSPARENT); return 0; to set the device context and have Windows draw the transparent background.
So far so good, right? I'm not sure if I'm really doing all this correctly, and I'd like this to also be flicker-free, which doesn't seem to happen: when I resize the window (at least in wine) I get either flicker or garbage drawn (even in child controls, somehow!). Windows XP in a VM just shows flicker. I tried tweaking some settings but to no avail.
But wait, now I want to have another control, one that just draws some bitmap data. On the next tab, create another container, then have a third window class area as a child of that. area only draws in the upper-left 100x100 area and has scrollbars; the rest of the window area should be transparent.
Right now, what I have for area is:
the window class hbrBackground set to NULL and styles CS_HREDRAW and CS_VREDRAW set
the extended window style being 0
the WM_ERASEBKGND simply doing return 1;
the WM_PAINT filling the entire update rect with COLOR_BTNFACE + 1 before drawing, and rendering all of it
This is flicker-free, but obviously not transparent. NOW I'm really not sure what to do, because I want the area to be transparent in such a way that it shows the tab control background. Again, I tried tweaking settings to bring them closer to what I tried above with container, but I got either flicker or invalidation leftovers when I tried.
So how do I get both of these custom control types (the container and the drawing area) to be both flicker-free and transparent?
I presently must target Windows XP at a minimum, though if the solution would be easier with Vista+ only I'd be happy to keep that solution on the side in case I ever drop XP support (unfortunately Stack Overflow doesn't let me hand out silver medals...).
Thanks!
To paint your window in a manner that is "flicker free", you will need to paint your window to a bitmap, then copy the bitmap to the destination device context. On Windows XP, you will need to create a bitmap, adjust the origin of the drawing DC and then paint your window. On Vista and later you can use BeginBufferedPaint and its associated routines to do the buffering for you.
Once you have buffered painting working, you can then use WM_PRINTCLIENT to paint your window's parent window into the your drawing DC before you do any actual drawing. Unfortunately, not all windows will support WM_PRINTCLIENT.
You could consider using DrawThemeParentBackground, rather than WM_PRINTCLIENT directly.
Combining these two methods together will leave you with transparent flicker-free drawing.

Screen Capture Complications with WPF window on top

I need to capture the entire screen with a transparent WPF window as the topmost window.
I tried 2 approaches:
using System.Windows.Drawing.Graphics.CopyFromScreen
using WINAPI GetDesktopWindow
Both methods yield the same result. I get the entire screen EXCEPT my topmost transparent WPF window.
The WPF window w is created with:
w.AllowsTransparency = true;
w.WindowStyle = System.Windows.WindowStyle.None;
w.Background = new SolidColorBrush( Color.FromArgb( 1, 0, 0, 0 ) );
w.Topmost = true;
plus some content of course. The window covers the entire screen surface.
Apparently, the WPF window draws on a surface that is not included in GetDesktopWindow.
Can anybody pls shed some light on this and share some ideas how to truly get the entire screen surface?
Just found the solution:
As far as I can tell there is no solution for the Graphics.CopyFromScreen approach because you'd need to OR CopyPixelOperation.CaptureBlt with CopyPixelOperation.SourceCopy but you can't. The usual M$ inconsistency madness...
However, the WINAPI approach works since you can combine SRCCOPY and CAPTUREBLT when using BitBlt. Without CAPTUREBLT transparent and layered windows will not be included.

Fix display garbage left by WPF dialog window?

In my WPF app, I have WPF windows that can open other WPF dialog windows, which I do as follows:
PickForEveryone PickForEveryoneWindow = new PickForEveryone(sSelRecipe, selMRM.sDay, selMRM.MealTypeID);
PickForEveryoneWindow.Owner = this;
PickForEveryoneWindow.ShowDialog();
Where PickForEveryone is defined as:
public partial class PickForEveryone : Window
and
<Window x:Class="PFWb0.PickForEveryone"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dg="http://schemas.microsoft.com/wpf/2008/toolkit"
ShowInTaskbar="False"
Title="Pick Recipe For All" Height="536" Width="441"
WindowStartupLocation="CenterOwner">
And contains a Grid with a DataGrid and a few buttons and check boxes.
Which works fine on my development computers. However, my client keeps seeing the dialog windows leave part of their display as visual litter on top of the parent window when the dialog window closes. That is, only some of the window undraws when ShowDialog() returns.
I tried adding this.InvalidateVisual(); below the above code, but it didn't solve the problem.
I also saw a suggestion here (for another kind of WPF display problem) to call OnRender() to force a redraw, but OnRender requires a parameter of type DrawingContext, which I don't know how to get.
So, I am asking if anyone knows how to either fix the display problem in the first place, or how to recover from it by getting WPF to redraw a window.
Update: As seen in comments to suggested answers below, I still have no solution that works on my client's computers, and my workaround (of having the windows dodge each other) is no longer enough. The only thing that works is to minimize and maximize the polluted underlying window.
I had a similar problem on a specific computer with an ATOM N270 processor.
The problem seamed to be linked to the graphic hardware acceleration.
To deactivate the accelaration, just add this to the registery (this will deactivate hardware acceleration for all WPF applications) :
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Avalon.Graphics\DisableHWAcceleration
I had to create the Avalon.Graphics folder.
DisableHWAcceleration is a DWORD that has to be set to 1.
This had solve my problem, if I reactivate the acceleration, the problem come back.
Hope this helps.
References :
Guidelines for troubleshooting graphic issues in WPF applications : http://msdn.microsoft.com/en-us/library/aa970912(v=vs.90).aspx
Graphics Rendering Registry Settings : http://support.microsoft.com/kb/963021
This ugly code works for me:
void RefreshWindow()
{
switch (WindowState)
{
case WindowState.Maximized:
{
double oldWidth = Width;
Width = System.Windows.SystemParameters.PrimaryScreenWidth - 1;
WindowState = System.Windows.WindowState.Normal;
WindowState = System.Windows.WindowState.Maximized;
Width = oldWidth;
}
break;
case WindowState.Normal:
if (Width > 1)
{
Width -= 1;
Width += 1;
}
else
{
Width += 1;
Width -= 1;
}
break;
case WindowState.Minimized:
default:
// no action necessary
break;
}
}
So I have been looking for an answer to this on the MS forums, and apparently, variations of this question have been a asked for a few years now.
Sometimes, they say, the problem has to do with video drivers, of all things, although in my case, my client has recently updated his video drivers.
My impression is that Microsoft thought they designed WPF so that a developer should never need to do such a thing as force a redraw of the display, so they make no way to do so by design. Of course, when things go wrong for whatever reason, this means there is no straightforward way to do so. And the ways that seem like they might do so (such as InvalidateVisual()), don't.
But I did find one hack that does work. Well, two. The ugly one is to tell the window to minimize and return to normal. But that results in a visual animation of it doing so, which is not ideal. In my case, it also made it hide behind other open windows, requiring me to make it go topmost. But is does solve the problem, in a jarring way.
Code after ShowDialog:
this.WindowState = WindowState.Minimized;
this.WindowState = WindowState.Normal;
this.Topmost = true;
The better hack, looks a bit like this:
Code outside:
public delegate void NoArgDelegate();
Code after ShowDialog:
this.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Loaded,
(NoArgDelegate)delegate {}
);
Presto ala kazzam!
This solution works, but it is not very pretty (easy to see that dialog is minimized and then set to normal).
this.WindowState = WindowState.Minimized;
this.WindowState = WindowState.Normal;
this.Topmost = true;
So far nothing I have tried actually works on my client's computer.
I have a new fix (workaround hack) attempt in for client testing, which involves moving the window away, and trying to make it actually take effect by launching an empty window just before I close the dialog window. Sigh...

WPF Window with Style=None cover taskbar when Maximised after app initialization

I want to achieve the same effect as Windows Media Player or Browser based Flash players which take up the ENTIRE (not even the taskbar is visible) real estate when maximized.
This works fine if the WindowState is set to Maximized and the WindowStyle is set to None in XAML so the app is started in that state. Problem is I want to start the app in a bordered window and when the user chooses, maximize as specified above. In the StateChanged handler I check for Maximized state and if this is the case I set the WindowStyle to None. This has the effect of maximizing the window but NOT covering the taskbar. The following code will make this work as I want but its a hack and I'd like to clean it up:
if (WindowState == WindowState.Maximized)
{
m_videoWindow.Maximize();
WindowStyle = WindowStyle.None;
//the following makes this work but I would like to clean it up
Hide();
Show();
}
EDIT This (from 2006 when still in CTP) mentions the problem and someone from MS states they hope to improve full screen support in the next version, have these improvements been made?
This article explains it all: Maximizing window (with WindowStyle=None) considering Taskbar.
Also worth checking out: Custom Window Chrome in WPF.
Edit: Now new, is the WPF Shell Integration Library that allows complete restyle of the window chrome without the headaches of reimplementing move, resizing, etc.
Edit 2015: Shell Integration Library is now integrated in WPF and MS retired the code
I found I could maximize to full screen (covering the taskbar) by setting the properties when creating the window (in xaml), but was not able to switch back and forth after creation. After some experimenting, I found the order the properties are set seems to matter:
public bool IsFullscreen
{
get
{
return WindowState == System.Windows.WindowState.Maximized
&& ResizeMode == System.Windows.ResizeMode.NoResize
&& WindowStyle== System.Windows.WindowStyle.None;
}
set
{
if ( value )
{
ResizeMode = System.Windows.ResizeMode.NoResize;
WindowStyle = System.Windows.WindowStyle.None;
WindowState = System.Windows.WindowState.Maximized;
}
else
{
ResizeMode = System.Windows.ResizeMode.CanResize;
WindowStyle = System.Windows.WindowStyle.SingleBorderWindow;
WindowState = System.Windows.WindowState.Normal;
}
}
}
Note that WindowState comes last in the setter.
To get this to properly work in my WPF/.NET 4.0 application I am calling this function whenever I enter or exit full screen mode:
private static void RefreshWindowVisibility(Window window)
{
if (window.OriginalWindowState == WindowState.Maximized)
{
window.Hide();
window.Show();
window.BringIntoView();
}
}
There is a flicker associated with this method, but it seems the same flicker exists when going to full screen mode on Chrome. Internet Explorer seems to take a different approach.
I don't know if this is ok for you, but you can resize the window to have the same size than the working area (that is, in most cases, all the screen except the taskbar) and locate it at 0,0 (top-left corner):
Width = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Width;
Height = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea.Height;
Left = 0;
Top = 0;
The exact definition for the WorkingArea property (from MSDN) is:
Gets the working area of the display. The working area is the desktop area of the display, excluding taskbars, docked windows, and docked tool bars.
Hope it helps

Resources