Strange behavior in FormBorderStyle between Fixed and Sizable - c#

I have created a simple test form with FormBorderStyle = FixedToolWindow by default and added a button that will switch between FixedToolWindow and SizableToolWindow on mouse press.
Switching the FormBorderStyle between these two seems to produce a weird effect that's causing a lot of issues on my application. The problem is that the window seems to change size and I can't have that. I just want to change the border, I need the form size to remain the same.
For instance, here's the button code:
private void button1_Click(object sender, System.EventArgs e) {
if(FormBorderStyle == FormBorderStyle.FixedToolWindow) {
System.Diagnostics.Debug.WriteLine("SWITCHING: FIXED -> SIZABLE");
FormBorderStyle = FormBorderStyle.SizableToolWindow;
} else {
System.Diagnostics.Debug.WriteLine("SWITCHING: SIZABLE -> FIXED");
FormBorderStyle = FormBorderStyle.FixedToolWindow;
}
}
And to debug I use this:
private void Settings_SizeChanged(object sender, System.EventArgs e) {
System.Diagnostics.Debug.WriteLine(this.Size);
}
And here's the output when I press the switch button:
SWITCHING: FIXED -> SIZABLE
{Width=373, Height=169}
{Width=383, Height=179}
SWITCHING: SIZABLE -> FIXED
{Width=383, Height=179}
{Width=373, Height=169}
How can I fix this behavior? And by "fix", I mean, prevent this from happening if possible. I want to be able to specify my form size and to remain like that, no matter the type of border style.
Also, a solution by subclassing the Form class would be the perfect solution for me in case anyone as any ideas to solve this problem with such a method.
EDIT:
I made a little video to demonstrate the problem. The first test shows that the form size doesn't actually change (visually), only the location of the form changes a little bit; but the values for the Size property do change, as you can see on the debug output. The second test you will see on the debug output that the form Size property values change and the window size itself will also change.
Please look here:
http://screencast.com/t/0vT1vCoyx2u

Your issue is with the Location changing, not the Size. This code solves the problem seen in the video.
private void Form1_DoubleClick(object sender, EventArgs e)
{
Point _location = this.PointToScreen(this.ClientRectangle.Location);
if (this.FormBorderStyle == FormBorderStyle.SizableToolWindow)
{
this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
}
else
{
this.FormBorderStyle = FormBorderStyle.SizableToolWindow;
}
Point _newloc = this.PointToScreen(this.ClientRectangle.Location);
Size _diff = new Size(_newloc) - new Size(_location);
this.Location -= _diff;
}
It appears to me that the issue of the rendered form moving when switching between those two borderstyles is a bug in the DWM.

I suspect what's happening is that Windows Forms is keeping the client size (i.e. inner area) the same while the border size changes. This is generally a good thing because it ensures that the window can still correctly fit the content that you've put on it.
If you want to maintain the same outer dimensions, you could work around it by saving the size to a variable before changing the border type, and then restoring it back. They'll probably a slight flicker, though.

This worked fine for me:
private Size _size;
private void Form1_DoubleClick(object sender, EventArgs e)
{
_size = this.Size;
if (this.FormBorderStyle == FormBorderStyle.SizableToolWindow)
{
this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
}
else
{
this.FormBorderStyle = FormBorderStyle.SizableToolWindow;
}
this.Size = _size;
}

I had this same problem. The client size changes when you set the FormBorderStyle but it does not seem to get put into the clientrectangle property right away. The above listed code almost worked for me, but rather than just store size, make sure to store clientsize. Of course, in the above example maybe "this" was a reference to clientsize -- I wasn't sure. Buy anyway, this worked for me:
_frmFilter.Height = 300
Dim _size As Size = .ClientSize
_frmFilter.FormBorderStyle = FormBorderStyle.FixedToolWindow
_frmFilter.ClientSize = _size

Related

c# Append Windows Forms application to the right side and change resolution of everything else

I am creating the application which should be always on top and not minimizable / maximizable.
For now I have the following code snippet:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.StartPosition = FormStartPosition.Manual;
}
private void Form1_Load( object sender, EventArgs e )
{
this.Location = new Point( Screen.PrimaryScreen.WorkingArea.Right - this.Width + 8 );
this.Height = Screen.PrimaryScreen.Bounds.Height;
this.MinimizeBox = false;
this.MaximizeBox = false;
this.TopMost = true;
}
}
Now this application is docked or placed on the right side of the screen. Now I need to adjust the resolution of everything else on the screen. I'll try to explain with the following image:
Explanation:
There is an application which is always on top. "My app" is the application which I am working on and my idea is to "share the same spot" on the screen like said earlier.
I tried to look for some solutions like this one:
https://www.c-sharpcorner.com/article/how-to-change-screen-resolution-in-C-Sharp/
but this only changes the resolution of the whole display. I need to leave resolution as it is in my application and resize everything else.
Is there a way to do it?
PS: If it is confusing in some way please let me know I will update the question.
Thanks

Contents of a panel won't visually update within a given time, but will clear completely afterwards

I have a form with a picturebox docked to fill the whole thing. On this form, I have a panel that is normally invisible and another picturebox; on the panel, I have a label and another panel with a label.
Here is what SHOULD happen when the user hovers over the second picturebox:
The picturebox's image changes and the first panel becomes visible, making the second panel and both labels visible too
The user clicks on the second label
The second label's OnClick handler makes the first label's text change and the second panel becomes invisible
A timer ticks for a few seconds
A code segment in the timer's OnTick handler causes the image in the second picturebox to change and the first panel to become invisible
Here is what DOES happen:
The picturebox's image changes and the first panel becomes visible, making the second panel and both labels visible too
The user clicks on the second label
The second label's OnClick handler sets the first label's text to a new string and sets the second panel's Visible property to false, BUT the second panel stays visible (although you can't interact with it) and the first label's text gets written on top of the old text
A timer ticks for a few seconds
A code segment in the timer's OnTick handler causes the image in the second picturebox to change and the first panel to become invisible
I've tried everything I can think of. I've called Invalidate, Update, and Refresh on every control in the form, I've called Application.DoEvents, I've reset the image in the background PictureBox to itself, nothing. The REALLY weird part is that in step 5, when the front picturebox resets itself and all panels are set invisible, nothing gets left behind - it's just for that brief few seconds between the OnClick handler terminating and the timer's OnTick cleaning up that there are problems. I can edit this for more information if needed, but does anyone have any ideas of what to do?
Edit: It's been pointed out to me that I should probably upload the code for this. Well, that code is a hacked-together mess, but okay. Also: there are some weird extra bits (in the enum types among others), they're for later parts of the project and irrelevant right now.
bool CountingHoverTime = false;
int HoverTime = 0;
int MasterTick = 0;
enum GhostState { Stand, Speak, Pet, Ask };
GhostState curState;
public enum TalkType { HoverGen, Petted, Spont, TimerMsg, Response };
private void Form1_Load(object sender, EventArgs e)
{
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
TopMost = true;
ControlBox = false;
Text = String.Empty;
WindowState = FormWindowState.Maximized;
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.TransparencyKey = Color.Transparent;
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//base.OnPaintBackground(e);
}
private void pictureBox2_MouseHover(object sender, EventArgs e)
{
if(curState == GhostState.Stand)
{
CountingHoverTime = true;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if((curState != GhostState.Ask) && (curState != GhostState.Stand))
{
MasterTick++;
if(MasterTick > 10)
{
SetToBasic();
}
}
else
{
MasterTick = 0;
}
if (CountingHoverTime)
{
HoverTime++;
if (HoverTime > 4)
{
HoverTime = 0;
curState = GhostState.Ask;
Say("What can I do for you?", TalkType.HoverGen);
}
}
}
public void SetToBasic()
{
curState = GhostState.Stand;
ghostBox.Image = Properties.Resources.stickStand1;
TalkPanel.Visible = false;
}
public void Say(String speak, TalkType type)
{
mainText.Text = speak;
if(type == TalkType.Response || type == TalkType.Spont)
{
curState = GhostState.Speak;
}
else
{
curState = GhostState.Ask;
}
ghostBox.Image = Properties.Resources.stickTalk;
if (type == TalkType.HoverGen)
OptionPanel.Visible = true;
else
OptionPanel.Visible = false;
TalkPanel.Visible = true;
backBox.Image = Properties.Resources.background;
ghostBox.Invalidate();
TalkPanel.Invalidate();
mainText.Invalidate();
backBox.Invalidate();
ghostBox.Update();
TalkPanel.Update();
mainText.Update();
backBox.Update();
Application.DoEvents();
}
private void op1label_Click(object sender, EventArgs e)
{
curState = GhostState.Speak;
OptionPanel.Visible = false;
Say("No can do yet.", TalkType.Response);
}
Edit 2: I've put together a gif visualization of what's happening, thank you HandbagCrab.
I've approached this from a different direction. I think the issue is to do with the picturebox you have docked on the form causing some issues.
To fix it and still keep the transparency, get rid of the backBox. Set the background colour of the form to a colour you're not going to use then set the transparency key to that colour. This will make those areas of the form transparent. Now you just need your hidden panel and your labels and whatever control it is that hosts your stick man image.
I've left the backgrounds of the labels as pink here but you should change them to your background colour so that they're not transparent.
When I run the form I get the transparency still and when clicking on the grey panel (I've used a panel to simulate your stick man) shows the hidden panel with the labels. Clicking label2 updates the text on label1 (the one that contains the text "longish text"), completely replacing the text.
Here it is in use (I've not done a gif as I wanted each step to be clearly visible)
Here's the application when open:
Here it is after clicking the grey box:
Here's the updated text when clicking label2:
I've left the application border style as Sizeable just so you can see where the border lies. I also took the screenshots over a black background so there was no visual clutter.
Here's me right clicking the desktop through the transparent section:

Resize winrt page when on on screen keyboard showing

I have a Windows 8.1 C# app that shows a page with a pretty big textbox (covering almost all of the page; its a writing app).
When the on screen keyboard shows up, it overlays half of the textbox.
I would like to resize the textbox (or even the whole page) so it isnt covered by the keyboard.
I am now thrying to achieve this using the static InputPane and subscribing to its showing and hiding events. I then try to change the margins of my textbox using the occuled rectangle provided in the eventargs. This works, but since my page is still the height of the screen it scrolls it halfway to the bottom.
public MainPage()
{
var inputPane = InputPane.GetForCurrentView();
inputPane.Showing += this.InputPaneShowing;
inputPane.Hiding += this.InputPaneHiding;
}
void InputPaneHiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
this.EditBox.Margin = new Thickness();
}
private void InputPaneShowing(InputPane sender, InputPaneVisibilityEventArgs args)
{
this.EditBox.Margin = new Thickness(0, 0, 0, args.OccludedRect.Height);
}
While trying this out I'm getting the feeling this isnt the ideal solution, but I havent got a better idea. Ideally I'd think it would be something like the vertical split when you have to apps opened, but then horizontally with the keyboard at the bottom and the app only the size available above the keyboard.
Any idea if this is possible?
Since you are handling the interface occlusion yourself you should set the property EnsuredFocusedElementInView from InputPane.Showing eventargs to true. It would prevent interface from automatically scrolling.
private void InputPaneShowing(InputPane sender, InputPaneVisibilityEventArgs args)
{
args.EnsuredFocusedElementInView = true;
this.EditBox.Margin = new Thickness(0, 0, 0, args.OccludedRect.Height);
};

Saving Location for next form

Alright so I'm doing something with next buttons that open new forms, annoying thing is that new forms pop up somewhere I don't want to on the desktop.
I'm trying to get the new form to spawn on the location of the old form with the code below, unfortunately for whatever reason it's not working at all, they still pop up the same way as before. And yes I have registered the events.
Form1:
System.Drawing.Point LocationPoint = new System.Drawing.Point(200,200);
private void Installer_template_LocationChanged(object sender, EventArgs e)
{
// Save the window location to the installer arts
LocationPoint = this.Location;
}
private void NextButton_Click(object sender, EventArgs e)
{
var NextForm = new Form2(LocationPoint);
NextForm.Show();
this.Hide();
}
Form2
public Form2(System.Drawing.Point LocationPoint)
{
InitializeComponent();
this.Location = LocationPoint;
}
The code is something along those lines
Have you tried setting the StartPosition of the new forms, i.e.
this.StartPosition = FormStartPosition.Manual;
or
this.StartPosition = FormStartPosition.CenterParent;
Alrighty I fixed it, it was a bunch of problems.
Wrong property, gotta use DesktopLocation instead of Location property
Second I had some issues with static member that couldn't be modified or whatever the error is, I just used the settings file to save my location instead
Having that done, it still didn't work because you can't just do this.DesktopLocation = something, you have to use this.SetDesktopLocation(X, Y)
Still didn't work, because it got overwritten by other code when loading the form so you had to use the Shown even of the form and run it in there..

C#.NET MDI bugs when programmatically hiding and showing again a maximized child form and when maximized, child form's icon cannot be changed

Basically I am having two problems with C#.NET MDI. You can download VS2010 solution which reproduces bugs here.
1) When programmatically hiding and showing again a maximized child form, it is not maximized properly again and becomes neither maximized or in normal state.
childForm = new Form();
childForm.Text = "Child Form";
childForm.MdiParent = this;
...
private void showButton_Click(object sender, EventArgs e)
{
childForm.Visible = true;
}
...
private void hideButton_Click(object sender, EventArgs e)
{
childForm.Visible = false;
}
When child form is maximized, then programicaly hidden and shown again, it becomes something like this (please notice the menu bar - child form's control box appears, but child form is not maximized):
At this stage, child form cannot be moved around. However, I found a workaround for that, simply by showing and hiding a dummy child form, which forces the actual child form to become properly maximized. But this makes MDI area to flicker. Tried Invalidate, Refresh, Update methods, but they don't help. Maybe there are other workarounds to overcome this bug and not to make MDI area flicker with dummy child form?
private void workaround1Button_Click(object sender, EventArgs e)
{
dummyForm.Visible = true;
dummyForm.Visible = false;
}
2) When child form is maximized, the icon of the child form is displayed on menu bar. However, if you have to change the icon while the child form is maximized, the icon on the menu bar is not being refreshed (see the image above). I found a workaround for that too, which basically hides and shows menu bar. Icon gets refreshed, but it makes everything below menu bar to flicker. Tried Invalidate, Refresh, Update methods, but they don't help. Is there any other way to make menu bar to refresh the child form's icon?
private void workaround2Button_Click(object sender, EventArgs e)
{
menuStrip.Visible = false;
menuStrip.Visible = true;
}
Also I noticed that when parent form is in normal window state mode (not maximized) and you change the width or height of the form by 1 pixel, child form becomes maximized as it should be and child form's icon on menu bar gets refreshed properly and you don't need other workaround I described above. If I change the size of the form programicaly, form flickers by 1 pixel and I cannot do that, when parent form is maximized. Is there any way how I could invoke the repaint/refresh functionality which is called when you resize a form and which makes child form become maximized properly and the icon on the menu bar refreshed?
There's a bug in the implementation of the internal MdiControlStrip class, the control that displays the icon and the min/max/restore glyphs in the parent window. I haven't characterized it as yet, the code isn't that easy. A classic side effect of the bug is that the glyphs get doubled up, you found some other side-effects. The fix is simple though, delay creating the child windows until after the constructor is completed. Like this:
public MainForm()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e) {
childForm = new Form();
childForm.Text = "Child Form";
childForm.MdiParent = this;
dummyForm = new Form();
dummyForm.MdiParent = this;
dummyForm.WindowState = FormWindowState.Maximized;
base.OnLoad(e);
}
Have you tired using Hide/Show instead of setting visible to true/false?
Try:
private void showButton_Click(object sender, EventArgs e)
{
childForm.Show();
}
private void hideButton_Click(object sender, EventArgs e)
{
childForm.Hide();
}
How about this workaround?
private void showButton_Click(object sender, EventArgs e)
{
childForm.Visible = true;
childForm.WindowState = (FormWindowState)childForm.Tag;
}
private void hideButton_Click(object sender, EventArgs e)
{
childForm.Visible = false;
childForm.Tag = childForm.WindowState;
childForm.WindowState = FormWindowState.Normal;
}
UPDATE
I just gave you the idea how you could do. A better solution using the same idea as above would be a new base form which saves the windows state. See below. Derive your forms from FixedForm instead of Form:
public partial class FixedForm : Form
{
private FormWindowState lastWindowState;
public FixedForm()
{
InitializeComponent();
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
if (Visible)
{
WindowState = lastWindowState;
}
else
{
lastWindowState = WindowState;
WindowState = FormWindowState.Normal;
}
}
}
Found a way how to come around those bugs.
First of all you need to suspend painting for a form and its children. I found a very helpful thread here, which describes how to do it.
After suspending painting, you need call UpdateBounds method of the control and increase ClientRectangle Width or Height by one and then decrease it back to the same value it was before. This invokes layout functionality which makes everything to update/repaint. Last step is to enable painting. Not very nice solution I guess, but it works.
StopDrawing();
UpdateBounds(Location.X, Location.Y, Width, Height, ClientRectangle.Width, ClientRectangle.Height + 1);
UpdateBounds(Location.X, Location.Y, Width, Height, ClientRectangle.Width, ClientRectangle.Height - 1);
StartDrawing();
I find suspending painting very helpful not only for working around those two bugs, but also in general to make GUI work more smoothly. I guess this can help to remove any kind of flickering. However, this solution requires P/Invokes, which should be avoided in general.
Why not just manually reset required icon in menuStrip items, after the creation of the window:
menuStripMain.Items[0].Image = null;

Categories