True size of my MDI Client area - height - 14? - c#

I am trying to center an MDI Child window in the center of my parent window's client area. By trial and error, I had found the following to work:
((parent.ClientSize.Width - 4)-centerchild.Width)/2, ((parent.ClientSize.Height - 14)-centerchild.Height)/2
I figured the 4 that I subtract from the width is the size of the border on either side, and I am able to get that number by either getting the MDIClient control within the parent (thanks to stack overflow for my finding that), or by subtracting SystemInformation.Border3DSize.Width * 2. This makes sense to me.
I am having a much harder time figuring out why I need to subtract 14 from the height. Assuming the bottom border is 2, that still leaves a number of 12 which I cannot reconcile with anything. (If there's also a top border of 2, then t hat still leaves 10 unexplained.) I have looked into the following:
Getting the MDIClient control within the parent does not get me that exact height as it does the width. Rather, it gets me parent.ClientSize.Height - 28, which is double the difference I worked out by trial and error. 28 might represent a bottom border (2) + a top border (2) + the height of the menu strip (24), but that does not explain the 14 that I have found to be perfect.
The Menu Strip height - as I said above, that's 24.
Various other window-element measures in SystemInformation. Perhaps I overlooked one, but nothing seems to explain it to me.
I could certainly use the code with my trial and error numbers, but I hate to not understand. Does anyone have any ideas that would explain the (total) height difference of 14?

It seems none of the framework functions will give the correct dimensions for the MDIClient, nor will API functions like GetWindowRect or GetWindowInfo. Derive from NativeWindow to subclass the MDIClient window and peek at WM_NCCALCSIZE when it comes across with wparam==true in which case lparam points to a NCCALCSIZE_PARAM struct that will have the right values.
struct NCCALCSIZE_PARAMS
{
public RECT rcNewWindow;
public RECT rcOldWindow;
public RECT rcClient;
IntPtr lppos;
}

Related

Windows desktop coordinate system is off for certain windows

I've run into an issue when developing some software for windows, an issue related around moving windows on the screen.
Basically, I've found that specific windows will consitantly not follow the coordinate system. I have a function that takes a window and a ratio, then scales up the window to the max size it can be (in that ratio) and still be within the working area of the monitor (not going over the edges or the taskbar).
Example 1: The function works as intended, sizes the window (VSCode) to be the max size it can be in 4:3, and puts it in the corner of the screen (using SetWindowPos and passing in 0,0 for the coords).
Example 2: The exact same function is run on a different window (Firefox) and it is again resized to 4:3 and set to 0,0. However, this time you can see the window isn't quite in the corner, off by 8 pixels to be exact. If I fetch the window position with the api call for that, it returns 0,0, even though it clearly is not. If I manually snap it into the corner myself, then it returns -8,0.
I have no idea what's wrong or how to fix it. It always happens on the same windows, not exactly sure but I think it has something to do with the type of titlebar the window has.
The only other person I've ever seen with this problem is here: https://answers.microsoft.com/en-us/insider/forum/insider_wintp-insider_desktop/desktop-coordinate-system-is-broken/9e6fd9ab-6d27-45e0-bb55-4c868cd6ac45
Unfortunately, he never got a real answer, so I'm back to square one.
So, any ideas of some way to consistently set windows to actually be in the corner? I've come up with some janky solutions by like trying to dynamically find how much a window offsets from the corner but it's not really something I want to be relying on. Is this just a bug with windows and I'm stuck?
Thanks, let me know if you have any questions!
This is the design of windows system.
I saw similar questions long ago.
Windows 10 has thin invisible borders on left, right, and bottom, it is used to grip the mouse for resizing. The borders might look like this: 7,0,7,7 (left, top, right, bottom).
The borders are invisible in Windows 10, so it appears that the window is in the wrong place. Visual Studio IDE has its own tool-windows, when tool-window is docked, it uses no borders or custom NC_PAINT; and when its tool-window is floating, it uses default borders.
A workaroud:
RECT rect, frame;
GetWindowRect(hwnd, &rect);
DwmGetWindowAttribute(hwnd, DWMWA_EXTENDED_FRAME_BOUNDS, &frame, sizeof(RECT));
//rect should be `0, 0, 1280, 1024`
//frame should be `7, 0, 1273, 1017`
RECT border;
border.left = frame.left - rect.left;
border.top = frame.top - rect.top;
border.right = rect.right - frame.right;
border.bottom = rect.bottom - frame.bottom;
//border should be `7, 0, 7, 7`
Then offset the rectangle like so:
rect.left -= border.left;
rect.top -= border.top;
rect.right += border.left + border.right;
rect.bottom += border.top + border.bottom;
//new rect should be `-7, 0, 1287, 1031`
For more information, please refer #Barmak Shemirani's answer.
Similar case:
Retrieve Window Size without Windows Shadows
GetWindowRect returns a size including “invisible” borders
Note: DwmGetWindowAttribute() returns physical coordinates, but GetWindowRect() returns logical coordinates. So the border width will be wrong for a non-DPI aware application on a screen scaled to anything other than 100%.

Positioning Unity Admob Banner at the Bottom on a Device with a Display Cutout

I'm trying to position my ad banner (generated via the official Admob package for Unity) at the very bottom of the screen, but it doesn't seem to work very well with devices with a notch.
bv = new BannerView(adUnitId, adSize, AdPosition.Bottom);
This line of code positions the banner at the very bottom perfectly on notch-less devices (such as Pixel, Pixel XL, Pixel 2 & 2 XL), but look like this when the device have a notch at the top:
(The demo photo is taken from this Github Issue. Ignore the visible navigation bar.)
the space between the banner and the bottom of the screen is exactly the height of the notch. I tested it with multiple notch heights.
In order to position the banner at the very bottom, I think the best solution is to get the height of the notch and then use the following line of code:
new BannerView(adUnitId, adSize, 0, (int)ConvertPixelsToDP(Screen.height - adHeight + notchHeight));
But I couldn't find any way to get the cutout height. I tried using Screen and Display classes properties, but none of the ways helped me get the real resolution of the screen, including the notch, so I could subtract the safeArea height - and get the exact height of the notch.
Is there any way to get this real screen resolution? or any other possible solution to this problem?
From the main GitHub issue thread about this the AdMob Unity plugin developers stated "Will be prioritizing notch compatibility for the next release." back in October, indicating they are aware of the bug, and they posted on January 22nd saying "This work got pushed back but will be making it into the next release. No ETAs yet but should be soon." So who knows when the issue will ever get fixed.
But to anyone who is wrestling with this issue: I found a workaround. In my case it's a workaround for getting the ad to appear at the bottom of the screen, but I will also mention how you can probably get it to display at the top properly. I still hope for a release that just fixes this soon, but here is an option for those who don't want to wait around...
I discovered this workaround because I first started with the obvious solution: compute the y position manually via the obvious formula: int yDP = DensityIndependentPixels(Mathf.RoundToInt(Screen.safeArea.y + Screen.safeArea.height) - ScreenPixels(adSize.Height)); This would work, except for the fact that as of Unity 2018.3.5 safeArea always has 0, 0 for x and y, and while the width and height are usable to detect that there's a notch when compared to Display.main.systemHeight (although as far as I can tell, Screen.height == safeArea.height and same for width, so safeArea is entirely useless right now, since it only conveys information you can get from Screen.height and Screen.width), you can't actually detect where the notch is... this means in this workaround I assume that the notch is at the top of the screen (a safe assumption, but some phones have two notches, one on top and one on bottom, and for those devices my workaround will leave the ad partially covered by the bottom notch... if safeArea ever gets useful values, my workaround can account for that... anyway, I digress.)...
With that assumption made we can use a slightly modified formula: int yDP = DensityIndependentPixels(Screen.height - ScreenPixels(adSize.Height));
But frustratingly while I was logging values I'd expect for the y position... it was still showing up in the same weird offset position! I started manually increasing the y position from 0 incrementally and confirmed that at a certain y coordinate, even if you increase the value, the position it would appear in would be the same! And then I discovered that if you add a fair bit more to the y value, it would inexplicably pop back up at the top of the screen and you could continue incrementing the y position to bring it back down the screen, except this time it didn't get stuck at the weird offset position! After much experimentation I found that the formula for this inexplicable extra wrap-around y offset is: (DensityIndependentPixels(Display.main.systemHeight) + adSize.Height) and when added to intendedDensityIndependentPixelPosition... you now have the banner in the y position you were expecting!
Once you simplify the math, the manual y position that is a functional workaround for placing the banner at the bottom on a notched device is the shockingly simple:
int yDP = DensityIndependentPixels(Screen.height + Display.main.systemHeight);
I've tested this with all the simulated notches in Android 9 and it works (with the caveat that the double notch partially covers the ad still, but that's better than the ad covering the UI of the app), but be warned that I haven't yet tested this on actual notched devices!
Note that you only need to do this when Screen.height != Display.main.systemHeight... otherwise you're on a notchless device and should use AdPosition.BOTTOM.
For those who want your banner to appear at the top, the manual computation for y position is even simpler. Under the same assumption that notches are always on top, the y position of the ad should be set to DensityIndependentPixels(Display.main.systemHeight - Screen.height) with no bizarre inexplicable wrap-around offset necessary. Note that I haven't tested displaying the banner at the top given that the banner in my app is always displayed at the bottom.
The x position manual formula to make sure the ad is centered is exactly as you'd expect: int xDP = Mathf.RoundToInt(DensityIndependentPixels(Display.main.systemWidth - ScreenPixels(adSize.Width)) / 2f); Nothing particularly weird there.
One final note, I noticed that after I started manually specifying the ad position, I couldn't call Show() after calling Hide() on the BannerView... it would get in a weird state where it was invisible yet clickable; to fix this simply Destroy() and create a new AdRequest instead of calling Show()... I've seen some statements on Google support forums that say this is better practice anyway.
I hope the day I lost diving into this bizarre issue and finding this workaround helps someone here.
Unfortunately this is an issue that is caused by admob not accounting correctly for the top notch on Android. Note on iOS the ads code seems to acknowledge the existence of the notch and add extra 10 units of padding under it, which is weird.
I just spent a day or so trying to figure out very similar issue with the banner.
There are several ways to fix this:
Option 1. Create new java plugin and add the code below or from C# call the equivalent of this code:
public int getDisplayRealHeightPixels() {
Point realSize = new Point();
Display display = getWindowManager().getDefaultDisplay();
if (Build.VERSION.SDK_INT >= 17) {
display.getRealSize(realSize);
}
else {
display.getSize(realSize);
}
return realSize.y;
}
Then use getDisplayRealHeightPixels() to position the banner. That's what I currently do.
Option 2. Enable painting in cutout area: create a new java plugin, in onCreate add:
protected void onCreate(Bundle savedInstanceState) {
...
// uncomment to allow painting in notch area;
WindowManager.LayoutParams attributes = getWindow().getAttributes();
attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(attributes);
...
I'll use this option once I adapt my app to support cutouts properly.
Option 3: Wait for Unity 2018.3 to support notches properly
According to https://unity3d.com/unity/beta/2018.3:
"Android: Added notch support for Android."
Gotchas:
None of the above fixes are perfect (perhaps option 3? but I haven't tried it.) Make sure to test on iOS (iPhone X) because Ads behaves differently there than Android.
On Android Ads, y==0 is the top of the screen, while on iPhone X y==0 renders the banner about 10 units below the notch.
For iOS I just hardcode the "notch size" to 30 and draw my app in the notch area.
Android split screen is a pain - I haven't tested completely there, my banner flies around the screen in this case. I just decided to fix it later.

C# Printing Offset

I'm trying to print some A5 pages from a C# application but I'm getting unexpected results. The output is vertically offset by about 110mm so the output starts halfway down the page and is offset horizontally by about 20mm. The output starts off the left hand side of the page (so it's hard to measure the exact offset). The output is also clipped horizontally and vertically.
For example, a
dc.DrawRectangle (0, 0, 100, 100)
draws a box halfway down the page and half the box is missing due to being off the left hand edge. The size seems OK though.
I must be missing something really obvious but I can't quite see it myself. Does anyone know what might be causing these offsets?
This may provide a hint:
// Rectangle describes page minus margins.
Rect rectPage = new Rect(marginPage.Left, marginPage.Top,
dlg.PrintableAreaWidth - (marginPage.Left + marginPage.Right),
dlg.PrintableAreaHeight - (marginPage.Top + marginPage.Bottom));
// Draw rectangle to reflect user's margins.
dc.DrawRectangle(null, pn, rectPage);
From : Charles Petzold's Apps = Code With Markup

Cropping the borders of image based on color in windows phone

Above is the image i am using. What i am trying to achieve is removing the red portion of the border from the image. How can I achieve this programmatically in windows phone? I found WriteableBitmapExtensions.Crop() method, but I am confused with the arguments (how i can find the x,y position of the image, as well as the size and the width?)
Also another issue I am facing is: I will get the images with differently sized borders, so I can't hardcode the x or y values.
Can anyone suggest a solution, or guide me to solve the issue?
This is not such a trivial thing and you haven't shared any code with us, so I can give you a few suggestions. Every WriteableBitmap has width and height defined. You should be able to access it via
wb.PixelWidth;
wb.PixelHeight;
where wb is your WriteableBitmap (the picture)
Having said that, it's trivial to crop a WriteableBitmap using WriteableBitmapEx library
var croppedBmp = wb.Crop(10, 10, 300, 220);
If your wb was 320x240 and the border was of width 10, then the above Crop call will do the trick - you will take the inner rectangle starting from point (10,10) and ending at (310, 230)
Now to your second issue - not knowing the width of the border. It would help if you know that
Border is of the same thickness on every side of the picture
Border is always in one color only
Assuming that's true, you could think of a simple algorithm (that may not be correct every time, but you can test it and adjust) which would take a few random points, for example
(0,randNumber < wb.PixelHeight), (randNumber < wb.PixelWidth, 0), (wb.PixelWidth, randNumber < wb.PixelHeight), (randNumber < wb.PixelWidth, wb.PixelHeight)
and then move towards the inner part of the picture as long as the neighbour pixel is the same color as the starting pixel. The more points you take randomly, the better chances you have of getting it right. The obvious problem with this is that it may happen that something on the picture is the same color as the border (exactly the same) which will make it seem like the border is wider than it really is. That's why you should take more points.
If you showed some code, I'd be happy to expand the answer.

change c# form application width in left direction

So I wanted to change my form size on a button click and also wanted it to look like a little animation so this code help me
while (desiredWidht < this.Width)
{
this.Width += 5;
Thread.Sleep(4);
}
but there is one problem. this code expands form in right direction. how can I do the same thing in left direction.. I tried expanding the form then moving it left and moving all controls to the right but the problem was that after form reached certain width controls disappeared for some reason and when the expansion method was done they appeared back again. I cannot figure out what is the problem. What is the proper way I do this.
Try -
this.Refresh();
after moving the controls.
const int MAX_STEP = 5;
while (Width < desiredWidth)
{
int step = Math.Min(MAX_STEP, desiredWidth - Width);
SetBounds(Left - step, Top, Width + step, Height);
Refresh();
Application.DoEvents();
Thread.Sleep(4);
}
Here I've done a couple of things, first was defining a maximum step and setting the step so you'll get the exact width you desire. The big change here from the over answer was the SetBounds which will set all the properties atomically, I didn't have anything else going on in my sample program so I was seeing the right side gitter back and form as I was setting the width then the left (previously when testing a solution). You probably have this in some other process so you'll need to raise the event Application.DoEvents() and like mentioned by #banana you can do a Refresh(), that should be automatically called but it shouldn't hurt calling it again.
Also, I spelled Width correctly in desiredWidth, it was driving me crazy!
The "this.Property" or "this.Method" is a redundant qualifier of "this" so I've removed it from my example.
**Note, look into ClientSize.Width as it is typically what people want, you may want to also look to SetDesktopBounds

Categories

Resources