I have been having trouble trying to implement an event using monogame and c#. Currently when my window is resized i call an event:
this.Window.ClientSizeChanged += new EventHandler<EventArgs>(Window_ClientSizeChanged);
void Window_ClientSizeChanged(object sender, EventArgs e)
{
int width = Window.ClientBounds.Width;
int height = Window.ClientBounds.Height;
if (width > 0 && height > 0)
{
graphics.PreferredBackBufferWidth = width;
graphics.PreferredBackBufferHeight = height;
graphics.ApplyChanges();
}
}
This is all fine and resizes my window correctly and updates the width/height. However it is not triggered in the event that my window is resized via a user clicking the maximize/minimize button on the window frame.
After spending some time looking into some solutions the only suggestion to implement a maximize event was from here:
Form form = (Form)Control.FromHandle(Window.Handle);
form.WindowState = FormWindowState.Maximized;
However this is a solution im not particularly fond of as it relies on the inclusion of System.Windows.Forms and since i may one day want to build for other platforms id rather not include any specific windows libraries.
Is there any monogame functionality that handles the maximize/minimize event calls for each platform or is it up to the end user to implement such functionality?
I've also had this issue and haven't found any 'built in' solution for that in the MonoGame API.
The solution I came up with was performing a check at the start of each frame on whether the width and height of Window.ClientBounds were changed.
It detects all window resizes similarly to the Window.ClientSizeChanged event, but also detects resizes made by maximizing/restoring the window.
Related
I currently have a way to do exactly what im asking, but the results are very in-efficient for some users.
Some users reported it making it as if it minimized (no window showing ever but still in taskbar), some reported for example in an ultrawide 21:9 it would only maximize from the left of a 1080p 16:9 width, yet I have a 32:9 super-ultrawise and have had no issues.
My current flow:
Get the Screen Dimensions excluding the taskbar on-load:
MaximizedBounds = Screen.FromHandle(mForm.Handle).WorkingArea; (mForm = Application.OpenForms[0], for support from any thread and such)
From another thread/function/class run:
Form1.mForm.Invoke(new MethodInvoker(() => Form1.mForm.WindowState = FormWindowState.Maximized));
This should result in a maximized window with the proper resolution, but it doesn't :/
What I would prefer:
NOT require to get the screen dimensions on-load, so use something from official Microsoft Code, either DllImport's, .NET Framework Code, or msapi to get the PROPER MaximizedBounds for Borderless Forms. (formborderstyle.none)
I set MaximizedBounds because if I don't, the application will fullscreen the entire screen, not "maximize" like traditional apps but would end up doing more of a Video Player style fullscreen.
Using my 32:9 screen and my 4k 16:9 laptop's screen on Extend Display mode, I managed to re-create the issue
Re-production Steps:
Open the application, leave it on the screen it first started in
Maximize the application (will work fine)
Unmaximize and move it to the other screen
Click maximize, your result should be like above.
This means, the on-load Maximize Bounds only gets the active bounds once which is expected, but due to me executing the Form Style change on a different class and different thread, I cant actually edit the MaximizedBounds property on it everytime I want to maximize, due to property not being public.
Thanks to HansPassant!
Found out exactly whats needed.
Problem:
When moving application to other monitor, it would use the same bounds. Causing it to not properly Maximize
When updating bounds via WndProc(), it would still try and maximize on Monitor 1 but outside its actual bounds, making it look like the application was hidden >:(
Updating the Bounds (put this in Form1):
protected override void WndProc(ref Message m) {
if(m.Msg == 0x02E0) {
Rectangle b = Screen.FromControl(mForm).WorkingArea; //where mForm is Application.OpenForms[0], this is specific to my applications use case.
b.X = 0; //The bounds will try to base it on Monitor 1 for example, 1920x1080, to another 1920x1080 monitor on right, will set X to 1080, making it show up on the monitor on right, outside its bounds.
b.Y = 0; //same as b.X
MaximizedBounds = b; //Apply it to MaximizedBounds
m.Result = (IntPtr)0; //Tell WndProc it did stuff
}
base.WndProc(ref m); //If any other messages were called, do the defaults this method does. Required even if you edited a msg.
}
Updating DPI Awareness for the WndProc message to fire (put this in app.manifest, make it if it doesnt exist):
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness> //Windows 10
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware> //Old-Compatibility
</windowsSettings>
</application>
Now it no longer shows up out of bounds, and is finally updating the MaximizeBounds when changing monitors!
Thanks #HansPassant for the tips!
I would like to create an application that have a small window displayed at the bottom corner of desktop. On startup, the window shall be very small and ideally, just a couple of pixels in width.
Here is the code I used to do it:
public partial class DurationT64 : Form
{
private Size fullSize;
private Point fullPos;
private Point compactPos;
public DurationT64()
{
InitializeComponent();
var workingArea = Screen.PrimaryScreen.WorkingArea;
this.MinimumSize = new Size(0, this.Height);
this.MaximumSize = new Size(this.Width, this.Height);
// fullPos: the window location when it is in full size form.
fullPos = new Point(workingArea.Right - this.Width, workingArea.Bottom - this.Height);
this.Location = fullPos;
// fullSize: the size of the windown when it is in full size form.
fullSize = new Size(this.Width, this.Height);
this.Size = fullSize;
// compactPos: the window location when it is in compact size form.
compactPos = new Point(workingArea.Right - 30, fullPos.Y);
this.Width = 1;
this.Location = compactPos;
}
}
As you can see that in this example, I intended to create a window of just 1 pixel in width, placed closed to the right edge of the primary monitor.
However, I realized that the window doesn't go as small as I was expected. It goes down to 20 pixels wide but no less than that. Please refer to this screen capture image below for example:
an image shows that the window is wider than it suppose to be
I did some research regards to this problem and noticed that there was a solution proposed by Zach Johnson (#zach-johnson) back in 2009. Here is the link to it Overcome OS Imposed Windows Form Minimum Size Limit.
However, nether methods proposed in that link (the intercepting WM_ message one proposed by Zach and the SetBoundsCore one proposed by #Ace) works for me.
Can anyone please give me some solution to this question? Preferably, a solution purely based on C#/Winform and does not rely on native Win32 window message loop, if possible.
Many thanks!
It is rather straight-forward, Winforms ensures that the window cannot be made smaller than the system-imposed minimum size of a window, exposed as the SystemInformation.MinWindowTrackSize property in .NET. This is a "safety" setting, it ensures that the user cannot make the window too small when he resizes it, thus losing control over it. Same consideration applies to code.
Bypassing this limit requires no magic, you need to do two things:
Set the FormBorderStyle property to None so the user cannot resize the window.
Set the size after the window is created. The Load event is best.
Some comments about your existing code: be careful about tinkering with the Width/Height/Size properties, you are doing too much of it in your constructor and it cannot work correctly. In the constructor they don't yet match the actual size of the window. And will not be close at all on modern machines with high-resolution monitors, auto-scaling to match the DPI of the video adapter is important today. You have to postpone until the window is created and scaling is complete, the Load event is the proper place for code like this. One of the few reasons to actually use Load.
And note that your Location property calculation is inadequate, it does not consider the location of the taskbar. It doesn't work on my machine, I like the taskbar on the right.
Minimum repro:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
}
protected override void OnLoad(EventArgs e) {
this.Width = 1;
base.OnLoad(e);
}
}
Do keep in mind that you'll need hawk-eyes to find it back on the screen :)
I have made a GUI app (WIN FORM) which is running fine on 12 inch screen(no cropping of the form) but on other Laptops having screen > 12 inches Win Form is going beyond the taskbar and some portion of the Form is not visible to the user.I have fixed it currently by squeezing certain UI boxes on the Form .But why this is happening?How can I auto-rectify it for all the PC models.
You may want to have a look at Form.AutoSize property. Also, in the link, look at AutoSizeMode.
However, to be able to fix this you may need to rewrite the whole form.
It sounds like a fixed sized dialog (at least I suppose it wouldn't be a problem with a resizable form).
You should adjust your form height to the desktop size (and maybe width too). The code below will shrink the form if it is too large and displays the scrollbar to make possible to access the remaining area.
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
AdjustHeight();
}
private void AdjustHeight()
{
Rectangle screen = Screen.FromControl(this).WorkingArea;
int screenHeight = screen.Height;
if (screenHeight < Height)
{
Height = screenHeight;
AdjustFormScrollbars(true); // not needed if AutoScroll property is true
}
}
I switched to VS 2012 yesterday from VS 2010 and all seemed to go well except this.
I have a button on my form that when pressed extends the width of the form to show additional controls. Press the button again and it reduces the width to hide those controls. Now all of this worked great in VS 2010 and also works fine when I debug in VS 2012 but as soon as I publish or compile the project and open the .exe when you click on the button it adds like 5 to the width instead of the 100+ it needs to. I click it again and it will then change it to 372 like it should and shows all my controls. I click it again to hide the controls and it hides the controls partially (goes to the 188 + the mysterious 5) I hope all of this makes sense and am hoping there is a better way to run the process I need to.
Here is the code I am currently working with and I didn't change anything between switching from 2010 to 2012. In fact, if I open this same solution in 2010 and publish everything works fine.
private void button1_Click(object sender, EventArgs e)
{
if (this.Width == 188)
{
this.Width = 372;
this.Height = 540;
progressBar.Value = 100;
copied_status.Text = ("Output View Enabled");
}
else
{
progressBar.Value = 100;
copied_status.Text = ("Output View Disabled");
this.Width = 188;
this.Height = 540;
}
if (this.Width == 372)
{
button1.Text = "<<";
}
else
button1.Text = ">>";
}
The width of your form hasn't been 188 pixels in a long time. Now with VS2012, Windows finally stops lying about it.
At issue are the fat window borders in Aero. They were an extreme appcompat problem when the feature was introduced in Vista. Very necessary because those two pixels where getting hard to hit with a mouse. But drastically incompatible with the way an application creates a window. It asks for a specific window size, the outer size, the nWidth and nHeight arguments of the CreateWindow() function. But what really matters is the size of the client area, the part of the window inside the borders. If Microsoft wouldn't have done something about it, old applications would have ended up with a client area that was too small. Which looks very bad, the window content would not fit anymore. A control towards the bottom or right side of the form would not be completely displayed for example.
So, sneakily, Aero makes the window larger by the extra width of the fat borders. And when the app asks for the window size, it sneakily says that it is smaller by the same added width. The app doesn't know any better than it is still running with the same window size it had on XP. This works pretty well, but is not exactly ideal. Hard to get window edges to align properly with that lie for example.
Whether or not Aero will lie about the window size is based on the target operating system recorded in the EXE header. When it sees a version older then 6.00, the Vista version number, then it will assume that your EXE is a legacy program that doesn't know about the fat border feature. So needs to be lied to. You've been running with that target version number set to 4.00 for a long time, it is written by the .NET compiler when it builds your program. You can see it with dumpbin.exe /headers yourapp.exe.
This finally changed in VS2012 and .NET 4.5. Which is a .NET version that is not available in XP. The compiler can finally make the hard assumption that XP is history and you are going to run on a version of Windows that supports Aero. So it sets the target Windows version in the EXE header to 6.00. Correspondingly, Aero will now stop lying about the window size. You get the real one, not the faked one.
So a quick fix is to change the target .NET framework version to 4.0. That's available on XP so you'll get lied to again.
Of course it is better to fix your code. Never use the Size, Width or Height property, they'll inevitably depend on the border and caption size. Use the ClientSize property instead, that's the stable one and the one you really care about. But be careful with that property as well, a form may rescale when it runs on a machine that has its video adapter set to more than 96 dots per inch. Another feature that's very accessible in Vista and up. Rescaling changes the ClientSize proportionally by the DPI setting.
The real fix is to use a bool field instead that keeps track of the window state. And set the ClientSize property based on the position of a control you want to hide or reveal. So roughly:
private bool enlarged;
private void button1_Click(object sender, EventArgs e)
{
enlarged = !enlarged;
int width = someControl.Left - 5;
if (enlarged) width = someControl.Right + 5;
this.ClientSize = new Size(width, this.ClientSize.Height);
}
Replace someControl in this code with the name of your controls.
I would like to know if it's possible to create nice form effects on the compact framework.
My plan is that when a user selects an image on the main form this is opened in a new form, this currently works. What I now want to do is make the form that contains the fullsize picture to load off the edge (left or right) of the screen at around 4 pixels high then slide into view. Once the form is fully on the screen then expand the height until it hits the max for the screen.
On close i would like to reduce the height back down to the 4 pixels high and the slide off the edge again before disposing the form.
I've tried the code below when instantiating the form and the dp.Top property was always 0 regardless of dp.Width == 240
DisplayPicture dp = new DisplayPicture(ImageUrl);
dp.WindowState = FormWindowState.Normal;
dp.Left = dp.Width * -1;
dp.Top = (dp.Height / 2) - 2;
dp.Height = 4;
dp.ShowDialog();
Within the DisplayPicture form i also have the following to try and move the form but as it isn't setting the Top property this code doesn't matter yet.
void t_Tick(object sender, EventArgs e)
{
if (this.Left < 0)
this.Left += 5;
if (this.Left > -1)
{
this.Left = 0;
if (this.Height < pictureBox1.ClientRectangle.Height)
{
this.Height += 4;
this.Top -= 2;
}
if ((this.Left == 0) && (this.Top == 0))
t.Enabled = false;
}
}
Any help would be greatly appreciated!
TIA
OneSHOT
To do this, start with a PictureBox control that has your image loaded. Set the Height to 4, the Width to the width of your form, and (very important) set the SizeMode of the PictureBox to StretchImage.
Next, position the PictureBox off the screen by setting Top to 0 and Left to -Width. Put a Timer control on your form with an interval of 100 (or whatever), and have its event gradually move the PictureBox to the right until its Left property is 0. Once you reach that point, have the timer event gradually increase the Height until it reaches the height of the form.
You'll probably have to deal with flicker, but this should get you started.
Update: I just read your question a little closer, and realized that you actually want to move the form itself from offscreen to full screen. This is not possible if you want the entire form (including the title bar at the top) to animate in this way, but you can sort of do this by setting the form's FormBorderStyle (or I think it's just called BorderStyle in the Compact Framework) to None. With the BorderStyle set to None, changing the Height, Width, Top and Left properties will actually have a visible effect on the form (although the form will be borderless). These properties are otherwise ignored completely in Windows Mobile, which is probably why your code didn't appear to be doing anything.
Update 2: here is my answer to a similar WM question, which may help you make your animated window look like a real window.