Is there a way to get the margins previously set by `DwmExtendFrameIntoClientArea`? - c#

Is there a way to retrieve the margins that were set by DwmExtendFrameIntoClientArea?
I've scanned the documentation. I would have expected DwmGetWindowAttribute to be the most logical place to look it up.
(What I would like to do is discern what portion of an inserted child should be painted black to allow Aero glass effects through and what portion should be painted the parent's background colour.)

There is no way to do this. The developer is expected to know the size of the frame they extended into the client area.
MARGINS g_Margins;
g_Margins.cxLeftWidth = 0;
g_Margins.cxRightWidth = 0;
g_Margins.cyTopHeight = 15*fontHeight;
g_margins.cyBottomHeight = 7*fontHeight;
DwmExtendFrameIntoClientArea(g_hwnd, g_margins);
Note: Any code released into public domain. No attribution required.

Related

Set MinimumSize according the controls residing inside form

Title of my question could be make it look like a duplicate but please read ahead as my problem is a bit different.
I am trying to replicate the minimum size functionality of some popular media players like MPC-HC or VLC where when you try to make it small the minimum size it achieves is when only MenuStrip and Player Controls are visible.
The code I've written to attain this is:
public NewMain()
{
InitializeComponent();
int ClientTop = RectangleToScreen(ClientRectangle).Top;
int height = menuStrip1.Height + panel1.Height + ClientTop - Top;
label4.Text = height.ToString();
MinimumSize = new Size(373, height);
}
The problem is that when it runs, its not working perfectly and the menuStrip1 is still getting blocked a little at bottom from the panel1 (Docked at bottom) where the player controls will be placed.
Below is the Image of what I was able to attain with above code.
Next Image is what I expected:
Note that label on left updates when resize the form and the label on the right is the determined height via code.
My Idea was to add the difference of Form's Top and the Top of total rectangle visible on the screen i.e. the height of the title bar otherwise the resulting height will be even smaller and hide the menuStrip1 completely. I don't want to hardcode any values because it'll make the interface less adaptable to the changes that I might be doing later on.
To correctly determine the minimum height in this case is to keep the calculations relative which can be attained by:
int height = Height - (panel1.Top - menuStrip1.Bottom);
All credit goes to Hans Passant who provided this code. I'm just posting it as an answer to mark my question solved. Thank you.

How to properly resize app to visible bounds on UWP? (VisibleBoundsMode doesn't seem to work)

I have two UWP apps and after testing them out with Continuum I noticed the app bar of the OS (the bar with the Start button) at the bottom of the screen (it can be at each of the 4 edges of the screen, of course) was covering part of my app.
Now, I'm already using ApplicationView.GetForCurrentView().SetDesiredBoundsMode(ApplicationViewBoundsMode.UseVisible) before calling Window.Current.Activate(), but that doesn't seem to solve the issue.
1) Why is it that setting the DesiredBoundsMode property doesn't seem to work here? Shouldn't that automatically resize the window
content to the visible bounds (ie. excluding system overlays like the
navigation bar or the app bar)?
The workaround I'm using for now on Windows 10 Mobile devices is to subscribe to the VisibleBoundsChanged event and then manually adjust the margins of my Window.Current.Content item to make sure it doesn't show anything behind covered areas of the screen.
Basically, I use the Window.Current.Bounds property and the ApplicationView.VisibleBounds property to calculate the occluded areas on the different edges of the app window, and increase the margins from there.
2) Is there a proper/better way to do this?
I mean, I'm quite sure there's another method that should be used to avoid this issue (considering there are tons of different situations like Continuum, navigation bar etc... that I don't think are supposed to be manually handled one by one).
Thank you for your help!
Use the subscription to the event VisibleBoundsChanged. This is the best solution that I found.
var curr = ApplicationView.GetForCurrentView();
if (curr.IsFullScreenMode == true)
{
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.FullScreen;
curr.FullScreenSystemOverlayMode = FullScreenSystemOverlayMode.Minimal;
}
else
{
ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.Auto;
curr.FullScreenSystemOverlayMode = FullScreenSystemOverlayMode.Standard;
}

Changing stroke color dynamically

I am using stylus input to draw lines in a canvas. I want to change the color of the stroke with the pen pressure. so I used:
DrawingAttributes dattribute = new DrawingAttributes();
inkcan.EditingMode = InkCanvasEditingMode.Ink;
if (stylusInput.pressureFactor < 0.5)
dattribute.Color = Colors.Red;
else
dattribute.Color = Colors.Blue;
inkcan.DefaultDrawingAttributes = dattribute;
but I have found that the color changes only when I lift off and retouch the pen to tablet surface. I am not sure how to fix that problem.
Any help would be greatly appreciated.
Look at this question: InkCanvas Eraser
In the MSDN it states:
If you change the EraserShape, the cursor rendered on the InkCanvas is
not updated until the next EditingMode change.
The effect you are experiencing might be caused by the EditingMode being changed when you pull the pen off the canvas and put it back down.
If so, you could toggle the EditingMode property as I suggested in the linked answer.
EDIT
Have a look at this a 3rd down it says:
Off course, life is never as simple as that, so there is one more
little issue to handle. Apparently, the InkCanvas uses different
renderers for the final result compared to while the strokes are being
drawn. To show a transparency based on the pressure while the
drawing action is still in progress, we need to use the protected
property called DyamicRenderer which gets/sets the object used to
render the strokes on a drawing context while the stroke is being
drawn. This rendering object must be a descendent of DynamicRenderer.
All you need to do here is override the OnDraw method and change the
brush that is used. When you assign a new value to this property, the
InkCanvas actually changes an internal 'PlugIn list' which is called
whenever data is entered using the stylus.
This might be it.
The if condition is evaluated only once, so there is no reason for the colour to change while you are drawing. Unfortunately, there seems to be no "onpressurechanged" event, so you would probably have to set up a loop that checks the pressure every x milliseconds and adjusts the colour accordingly.
Since you don't want to freeze the UI, you would either need to run it in another thread and delegate the colour change back to the UI thread, or you can queue the pressure checks onto the window dispatcher with "applicationIdle" priority. This would look something like:
void checkPressure(InkCanvas inkcan)
{
//return if touch is lifted code here
DrawingAttributes dattribute = new DrawingAttributes();
if (stylusInput.pressureFactor < 0.5)
dattribute.Color = Colors.Red;
else
dattribute.Color = Colors.Blue;
inkcan.DefaultDrawingAttributes = dattribute;
this.Dispatcher.BeginInvoke(new MyPressureDelegate(checkPressure), DispatcherPriority.ApplicationIdle, inkcan);
}
assuming your stylusInput shares scope with the function, of course. Otherwise you'd need to pass it in along with the canvas in an object array.

C# WinForms - Real (including non-client) FixedToolWindow height and width

I'm trying to place a fixed tool window in the bottom right portion of my primary screen just above the start menu bar.
I'm using the following code
this.Top = Screen.PrimaryScreen.WorkingArea.Bottom - this.Height;
this.Left = Screen.PrimaryScreen.WorkingArea.Right - this.Width;
The form aligns the edge of the screen to the client rectangle so I see the non client bottom edge bleed into the start menu bar and the right non client edge bleed into the second monitor.
I need to get the full form width and height including all the non client borders
I notice it works when the form is SizableToolWindow but not with FixedToolWindow
Thanks everyone!
The Bounds property will give you a Rectangle from which you can get the full size.
The bounds of the control include the nonclient elements such as scroll bars, borders, title bars, and menus.
Documentation
Hans got this answer right but posted it as comment instead of an answer.
The answer is here:
https://social.msdn.microsoft.com/Forums/windows/en-US/af2608c1-1159-444f-bb21-b2cf0228f1c1/strange-issues-with-formborderstyle-on-vista-between-fixed-and-sizable-modes?forum=winforms
– Hans Passant May 19 at 21:07
Copied from site:
Strange issues with FormBorderStyle on Vista between fixed and sizable modes
Question:
The problem lies on all types of fixed border styles like FixedDialog, Fixed3D, FixedSingle and FixedToolWindow. It does not happen on the sizable ones. This problem, like I said, also happens only on Vista.
Let's say you have a form with any of the fixed border styles and set the starting location to 0,0. What you want here is for the form to be snapped to the top left corner of the screen. This works just fine if the form border style is one of the sizable options, if it's fixed, well, the form will be a little bit outside of the screen working area both to the left and top.
What's more strange about this is that the form location does not change, it sill is 0,0, but a few pixels of the form are drawn outside of the working screen area.
I tested this on XP and it didn't happen, the problem is Vista specific. On XP, the only difference was the border size that change a bit between any of the styles. But the form was always perfectly snapped to position 0,0.
If possible, without finding how many pixels are being drawn outside of the working area and then add that to the form location, is there a possible way to fix or workaround this?
Answer:
The problem is specific to Aero, it doesn't happen when you turn it off. It is one of those famous app-compat fixes. Windows actually lies to you when you P/Invoke GetWindowRect(). It gives window position and size, relative from the client area, that the window would have if it were running on an earlier version of Windows, like XP.
That makes sure that older programs still behave somewhat predictably, they'll have their all-important client area in the same location, even though they have much fatter borders. But gets in the way at times, like your case.
To fix that, you have to convince Vista that you are really aware how Vista works. That requires patching the .exe header. Start the Visual Studio Command Prompt, navigate to your build directory and type this command:
editbin /subsystem:windows,6.0 yourprogram.exe
Yes, the comment from Hans correctly told us why the form's .Height and .Width are not correct. I think the following should work to give you the real height and width, whether or not Aero is enabled.
(in the code for a Form)
[DllImport("dwmapi.dll", PreserveSig = false)]
public static extern bool DwmIsCompositionEnabled();
// When Aero is enabled, and our FormBorderStyle is FixedToolWindow,
// Windows will lie to us about our size and position.
public bool AeroIsMessingWithUs()
{
bool ret = false;
// check for other Fixed styles here if needed
if (FormBorderStyle == System.Windows.Forms.FormBorderStyle.FixedToolWindow)
{
if (Environment.OSVersion.Version.Major >= 6 && DwmIsCompositionEnabled())
{
// Aero is enabled
ret = true;
}
}
return ret;
}
public int MyWindowHeight()
{
int height = Height;
if (AeroIsMessingWithUs())
{
// there are actually 5 more pixels on the top and bottom
height += 10;
}
return height;
}
public int MyWindowWidth()
{
int width = Width;
if (AeroIsMessingWithUs())
{
// there are 5 more pixels on the left and right
width += 10;
}
return width;
}

Taking screenshots in Windows Vista, Windows 7, with transparent areas outside the app region

I am trying to take a screenshot of an application and I would like to make the parts of the rectangle that are not part of the applications region be transparent. So for instance on a standard windows application I would like to make the rounded corners transparent.
I wrote a quick test application which works on on XP (or vista/windows 7 with aero turned off):
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics g = e.Graphics;
// Just find a window to test with
IntPtr hwnd = FindWindowByCaption(IntPtr.Zero, "Calculator");
WINDOWINFO info = new WINDOWINFO();
info.cbSize = (uint)Marshal.SizeOf(info);
GetWindowInfo(hwnd, ref info);
Rectangle r = Rectangle.FromLTRB(info.rcWindow.Left, info.rcWindow.Top, info.rcWindow.Right, info.rcWindow.Bottom);
IntPtr hrgn = CreateRectRgn(info.rcWindow.Left, info.rcWindow.Top, info.rcWindow.Right, info.rcWindow.Bottom);
GetWindowRgn(hwnd, hrgn);
// fill a rectangle which would be where I would probably
// write some mask color
g.FillRectangle(Brushes.Red, r);
// fill the region over the top, all I am trying to do here
// is show the contrast between the applications region and
// the rectangle that the region would be placed in
Region region = Region.FromHrgn(hrgn);
region.Translate(info.rcWindow.Left, info.rcWindow.Top);
g.FillRegion(Brushes.Blue, region);
}
When I run this test app on XP (or Vista/Windows 7 with Aero off), I get something like this, which is great because I can eek an xor mask out of this that can be used later with BitBlt.
removed dead Imageshack link - Screenshot
Here is the problem, on Vista or Windows 7 with Aero enabled, there isn't necessarily a region on the window, in fact in most cases there isn't. Can anybody help me figure out some way to build a mask like this on these platforms?
Here are some of the approaches I have already tried...
1. Using the PrintWindow function: This doesn't work because it gives back a screenshot taken of the window with Aero off and this window is a different shape from the window returned with Aero on
2 Using the Desktop Window Manager API to get a full size thumbnail: This didn't work because it draws directly to the screen and from what I can tell you can't get a screenshot directly out of this api. Yeah, I could open a window with a pink background, show the thumbnail, take a screenshot then hide this temporary window but thats a horrible user experience and a complete hack I would rather not have my name on.
3. Using Graphics.CopyFromScreen or some other pinvoke variant of this: This doesn't work because I can't assume that the window I need information from is at the top of the z-order on the screen.
Right now, the best solution I can think of is to special case Aero on Windows 7 and Vista to manually rub out the corners by hard coding some graphics paths I paint out but this solution would suck since any application that performs custom skinning will break this.
Can you think of another or better solution?
If you are here, thanks for taking time to read this post, I appreciate any help or direction that you can offer!
If you are looking for a finished application, there is 7capture, which captures also the translucency, so images can be saved to PNG format for later compositing.
EDIT:
The original question and comments indicate you are looking to produce a region on Windows Vista/7 that you can then use to mask out parts the captured image, as is done with Windows XP and non-Aero UIs. Using a region is not going to give you the result you are looking for, since the window outline is not computed as a region, but as an image with variable transparency - RGBA. The Alpha channel in that image is your mask, but it's not an on-off mask like a region, but a gradual mask with a range of values from pixels being fully included to being fully masked out.
Although it uses undocumented APIs, the code at http://spazzarama.wordpress.com/2009/02/12/screen-capture-with-vista-dwm/ will capture to a RGBA buffer which you can then use to render or save the image with the shadow and other translucency effects intact.
In DwmCapture.cs Change
BackBufferFormat = Format.X8R8G8B8
to
BackBufferFormat = Format.A8R8G8B8
(X8->A8)
And you should then be able to access both the usual RGB data plus transparency from the captured buffer. This can then be saved as a PNG or other format with alpha-channel for composing.
Removed idea that is terrible but would have been awesome back in the '90s
You say that using the DWM API only allows you to capture directly to the screen... could you create a window offscreen (say, X = -100000px, Y = -100000px) but visible (maybe even hidden?) and draw the screenshot to it? Since when using the DWM each window has a backing texture, I'm thinking it might still get drawn fine even though the target isn't directly onscreen.
Also, if you want to go the DirectX route and access the actual DX texture backing the window, I found a few leads that might help (especially the first link):
http://spazzarama.wordpress.com/2009/02/12/screen-capture-with-vista-dwm/
http://channel9.msdn.com/forums/TechOff/251261-Help-Getting-the-shared-window-texture-out-of-DWM-/
http://web.archive.org/web/20080625183653/http://www.aeroxp.org/board/index.php?showtopic=6286
http://www.youtube.com/watch?v=hRTgFTMnT_U
Using Graphics.CopyFromScreen or some other pinvoke variant of this:
This doesn't work because I can't
assume that the window I need
information from is at the top of the
z-order on the screen.
If you know which window you need the information from, can you bring it to the front, call Graphics.CopyFromScreen, and then reset its z-index? I know from experience that Aero does odd things when items are in the background in order to make their glass interface work correctly (partial rendering etc). This may not be great UX; however, it would be a special case and used only when Aero is turned on.
You can take a look at the source code of AeroShot, as described on the main page, it can capture rounded edges and with the Aero Glass transparency effect and save it to a PNG file. It's written in C#.

Categories