wpf notifyicon context menu not centered on tray icon at 100% dpi - wpf

I'm using the wpf notifyicon (http://www.hardcodet.net/wpf-notifyicon)
When my laptop is at 100% dpi scaling, the left side of the context menu is centred on the tray icon, as expected.
When the laptop isn't at 100%, the context menu is pushed to the far right.
On high resolution laptop displays, 100% scaling is not the default.
Wherever my tray icon is positioned, that is, however far from the clock, the menu always pops up over the clock, as far to the bottom-right of the screen as is possible while remaining visible.
Note: I'm testing on a default installation of Windows 8.1. Also, the NotifyIcon that I'm using is the one that is generally recommended for anyone attempting tray functionality in WPF.
To reproduce: the problem exists in the windowless sample provided by hardcodet. I'm using wpf NotifyIcon without a window, and can reproduce easily in code or xaml. In fact, I cannot stop reproducing it. It occurs when dpi scaling is turned on, i.e. when a 1080p display is actually showing a lesser resolution, which is what windows does to stop applications having text too tiny to read.
Any ideas about how I can make the context menu appear in the expected place regardless of dpi?
Screen shots as suggested by kennyzx:
good behaviour. the m on red background (MEGAsync) has just been right-clicked
bad behaviour. the green tick, my notifyicon, has just been right-clicked and the menu appears over the clock
!good behaviour. the m on red background (MEGAsync) has just been right-clicked
!bad behaviour. the green tick, my notifyicon, has just been right-clicked and the menu appears over the clock
and some code:
var n = new TaskbarIcon();
n.Icon=new System.Drawing.Icon(#"C:\window - 64 - tick.ico");
n.ContextMenu = new System.Windows.Controls.ContextMenu();
n.ContextMenu.Items.Add(new System.Windows.Controls.MenuItem {Header="E_xit" });

Found the solution here: http://www.codeproject.com/Messages/4929452/Problem-with-context-menu-position.aspx
It is, with thanks to codeproject user Igorious:
Get the code for Wpf Notifyicon (http://www.hardcodet.net/wpf-notifyicon)).
In Hardcodet.Wpf.TaskbarNotification.TaskbarIcon.ShowContextMenu()
replace
ContextMenu.HorizontalOffset = cursorPosition.X;
ContextMenu.VerticalOffset = cursorPosition.Y;
with
var g = Graphics.FromHwnd(IntPtr.Zero);
var scaleX = g.DpiX / 96.0;
var scaleY = g.DpiY / 96.0;
ContextMenu.HorizontalOffset = cursorPosition.X / scaleX;
ContextMenu.VerticalOffset = cursorPosition.Y / scaleY;
Explanation (thanks to codeproject user yachting):
It's needed because WinApi.GetPhysicalCursorPos return the mouse position in pixel,
but WPF's measurement unit is device independent pixel (by definition, it's 1/96 inch)
You need to adjust the return value of GetPhysicalCursorPos by DPI (dots per inch) setting,
otherwise the position of the context menu will be incorrect if users set DPI other than the default 96.

Related

Incorrectly scaled menu checkmarks on High DPI using .Net 4.8/Windows Forms

Using .Net 4.8 and Windows Forms, my toolstrip menus with check marks appear normally at 100%, but when the monitor is set to something larger (e.g. 150%), the menu check mark appears far too small, and located in the upper-left of the menu item.
Is there a way to override the painting of the check mark for use on high DPI monitors?
(Using per-monitor DPI scaling, all other form controls draw correctly thanks to the scaling improvements starting in .Net 4.7.2
Turns out the problem wasn't really the checkmarks at all; it was the imagealign of the menu item (apparently, used to align the checkmark when there is no menu image) Setting that to be centered fixed the issue.

ToolStripDropDownButton does not show items when right aligned

I developed a Winforms application that has a Toolbar. The last element in the toolbar is a ToolStripDropDownButton with some items.
I needed this button to be shown apart from all other toolbar buttons, so I set the Alignment property to right.
In my PC this works perfectly, however, when I moved the whole Visual Studio project to my notebook and then ran the application, the menu items are not shown when I click the button, however, the dropdown button changes color indicating that it was selected.
At design time, items are shown correctly.
What is happening here and is it possible to solve it? At the moment, I set the button alignment to left, so that it is shown together with other toolbar buttons.
Thanks
Jaime
Check your DPI settings on your notebook. It is likely changing the size and padding of your elements. You can get around this by checking for the system's DPI value and calculating sizes of controls based on that.
var graphics = this.CreateGraphics();
var dpiX = graphics.DpiX / 96d; // Default DPI
var dpiY = graphics.DpiY / 96d; // Default DPI
myToolbar.Width = myWidth * dpiX;
myToolbar.Height= myHeight * dpiY;

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.

Why is there a black lag every time a WPF window is resized?

Other questions on SE address how to speed up nested UI control resizing, but- what if there aren't any controls?
As you drag the edge of a WPF window, even a main window with no content, black bars flicker briefly during the drag. This produces a crummy feel- one that I don't want to inflict on customers:
It does get slower and heavier with a full UI on top of it as well. This doesn't even get into how ugly it looks when resizing using the top or left edges. Windows Forms- even with the heaviest UI I've built- never looks this bad right off the bat.
What can be done to make WPF window resizing performance comparable to win forms?
(I have Windows 7 x64 and a triple monitor system on an AT Radeon HD 7470.)
You could update your graphic card and try it out again but that wont change anything. The reason is pretty simple. We all get to see this sometimes based how fast/slow our computer is. Sometimes it runs smooth because we do not have many visuals to draw. The reason is no proper background color is found in graphic card at that moment in redrawing process. Your drivers are fine, and its not just because you use Wpf. Other techniques use the same mechanism behind redrawing.
The first thing WPF will do is clear out the dirty region that is going to redraw. The purpose of dirty regions is to reduce the amount of pixels sent to the output merger stage of the GPU pipeline. Here is where we see the black color. Window itself at that point has no background color or its background color is set to transparent and so to us the GPU draws the black background. Things run async in wpf which is good so.
To fix this you could set a fix color such as "White" to the Window. Then the WPF system will clean out the dirty region but fill it automatically with white color instead of black. This usually helps.
Match the window color or the color of top most layer. Dont let GPU use black and you should do fine. Btw Wpf is faster than WinForm so dont worry.
The look is crummy indeed, especially when using the top or left border.
Which exact problem your screen shot is showing depends on how long your app is taking to render as well as a couple of background related settings that you might be able to tweak to get better resize. Plus part of the ugly resize is specific to Aero.
While I can't address the specific crazy slowness of WPF redraw, I can at least give some insight on why you see black, where that is coming from, and whether you can change to a less annoying fill-in color.
It turns out there are multiple different sources of the black and the bad resize behavior from different Windows versions that combine together. Please see this Q&A which explains what is going on and provides advice for what to do (again, not specific to making WPF faster but just seeing what you can do given the speed you have):
How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?

How do I remove the border of a WPF window in the Design view/tab?

I am trying to remove the border of my WPF window in the design view/tab in Visual Studio. Please don't mistake this as a request to create a border less WPF window. I did that and it is working fine. What bothers me is that even if you have set WindowStyle = None, ResizeMode = NoResize, the design view/tab still shows a border around your window in the preview.
Is there a way to remove said border and have a 1:1 preview of the border less window as in Windows Forms?
Every question I have found in regards to this only asks how to remove the border of the actual application. I would like to remove it in the preview.
Any help would be very much appreciated :)
here is a screenshot of my problem:
This cannot be done as this is just how Visual Studio renders a window in design view (I think the frame is probably there so that you can distinguish when you are editing a Window rather than a UserControl).
Rather than try and find a solution to this I would ask myself if this is something I need to be spending time figuring out - after all you say that your program works correctly when being run. I think your time will be better spent writing code for your program rather than trying to play with the design time environment.
Update: In response to you comment, consider that the window frame will be different on every users machine depending on their operating system version (XP vs. Win7) or the theme the user has installed.
My computer has XP installed so the side borders are a lot thinner than those shown in design time so any content will be smaller (but only my a few pixels - 4 in my case; does your user interface design really depend on 4 pixels?).
When using a technology such as WPF you should not be designing your UI to fit to exact pixel sizes; you should be designing with min / max values or using layout containers that adjust to the size of the window as set by the user. Any regions in your UI (E.G. sidebar and main content) should be expressed as a ratio or percentage of one another; instead of saying "The side bar is 150 pixels wide and the main content area is 350 pixels wide" you should be saying "The side bar takes up a third of the window width and the main content takes two thirds".
Although the question is very old and have already been answered (kind of), I just realized: if you set WindowStyle="None", your undesired border is gone.

Resources