dotnet / winforms

Windows Forms is a .NET UI framework for building Windows desktop applications.
MIT License
4.36k stars 967 forks source link

[MDI] Add possibility to align minimized child to default location (TOP LEFT) #4720

Closed kirsan31 closed 3 years ago

kirsan31 commented 3 years ago

Is your feature request related to a problem? Please describe. WinForms override standard MDI behavior without option to use original one. It's lead to all child windows layout (with minimized windows) are mess up after main window resize. This can happened after simple resize, or after resolution changed (for example if your connect by rdp from other device), etc... before res. change (left - WinForms, right - MFC): Snipaste_2021-03-24_09-44-51 after res. change (left - WinForms, right - MFC): Snipaste_2021-03-24_09-45-13

Video:

https://user-images.githubusercontent.com/17767561/112429754-26a14200-8d4e-11eb-83d5-393626ad23b6.mp4

For now we need to manually revert this move (with same technique) after each resize :(


Draft API Proposal

We need to add bool property to Form class, let it be MdiChildrenMinimizedAnchorTop:

namespace System.Windows.Forms
{
    public partial class Form : ContainerControl
    {
        /// <summary>
        /// true - anchoring minimized MDI children to the TopLeft of the parent form (windows default). false - anchoring minimized MDI children to the BottomLeft of the parent form (WinForms default)
        /// </summary>
        [SRCategory(nameof(SR.CatWindowStyle))]
        [DefaultValue(false)]
        [SRDescription(nameof(SR.FormMdiChildrenMinimizedAnchorTopDescr))]
        public bool MdiChildrenMinimizedAnchorTop { get; set; }
    }
}

To do.

  1. Discuss a name for the property.
  2. Discuss a description for the property. Translations?
  3. Category: Style or may be Layout?
  4. Implement trivial logic with this property in SetBoundsCore method of MdiClient class and do PR.
kirsan31 commented 3 years ago

Guys, I am bit confused here :thinking:

@RussKie why this is a bug? This is by WinForms design:

NOTE: This logic is to keep minimized MDI children anchored to the bottom left of the client area, normally they are anchored to the top right which just looks weird!

@dreddy-work And I don't understand how it's related to HDPI?

dreddy-work commented 3 years ago

@dreddy-work And I don't understand how it's related to HDPI?

From the attached images, we were looking at the nature of minimized MDI windows location being constant after windows resolution change that resized Form window to changed resolution/DPI. This is something that need to be handled when DPI changes. Correct us if we are not reading it right.

kirsan31 commented 3 years ago

@dreddy-work yes you are not reading this right :) This is absolutely not related to HDPI. As I wrote, this is WinForms decision - override default anchor (TopLeft) of minimized child mdi windows with (BottomLeft). I made a video with difference in WinForms (Bottom-Left anchor) vs MFC (Top-Left anchor):

https://user-images.githubusercontent.com/17767561/112429754-26a14200-8d4e-11eb-83d5-393626ad23b6.mp4

dreddy-work commented 3 years ago

@dreddy-work yes you are not reading this right :) This is absolutely not related to HDPI. As I wrote, this is WinForms decision - override default anchor (TopLeft) of minimized child mdi windows with (BottomLeft). I made a video with difference in WinForms (Bottom-Left anchor) vs MFC (Top-Left anchor):

mdi.mp4

Thanks for the GIF. Earlier images attached were not clear enough to understand the expected behavior around anchoring of minimized MDI windows. Did you try bypassing ‘Setbounds*’ method?

kirsan31 commented 3 years ago

Did you try bypassing ‘Setbounds*’ method?

Your mean SetBoundsCore of MDIClient? If yes, then - no, not directly. We are doing revert move in OnResize:

protected override void OnResize(EventArgs e)
{
    base.OnResize(e);
    if (_OldHeight != -1 && _OldHeight != this.Height)
    {
        int yDelta = _OldHeight - this.Height;
        foreach (var child in ChildrenW)
        {
            if (child.IsHandleCreated && child.WindowState == FormWindowState.Minimized && child.Visible)
            {
                WinApi.User32.GetWindowPlacement(child.Handle, out WinApi.User32.WINDOWPLACEMENT wp);
                wp.ptMinPosition.Y += yDelta;
                wp.flags = WinApi.User32.WPF.SETMINPOSITION;
                WinApi.User32.SetWindowPlacement(child.Handle, ref wp);
            }
        }
    }
    _OldHeight = this.Height;
}

And it's work well, except that this is a bit overhead :roll_eyes:

  1. System move window to correct place.
  2. WinForms move it to wrong place.
  3. We move it back to correct place.
dreddy-work commented 3 years ago

Yes. Wanted to check as comments around that code was saying the default behavior was top-right anchored and I observed MFC app was doing top-left.

kirsan31 commented 3 years ago

Yes. Wanted to check as comments around that code was saying the default behavior was top-right anchored and I observed MFC app was doing top-left.

Oh I ever didn't notice that :) Yes, It's typo in comment - Left everywhere.

merriemcgaw commented 3 years ago

I doubt that we can change the default behavior. Perhaps a property that enables the ability to set this. If someone has a proposal for how this might be done we'd be happy to consider it. I'll mark as up for grabs, but move it out to future.

kirsan31 commented 3 years ago

@merriemcgaw

If someone has a proposal for how this might be done we'd be happy to consider it.

The exact proposal in the first post ;)

merriemcgaw commented 3 years ago

We are looking for someone to propose (and implement) an API that would enable this to happen. It has to go through the full API review process, and we can help a bit with that. But it looks like there are more urgent fires to fight for .NET 6 so it doesn't look like we will have the resources to enable this feature just yet. If the community can, that would be something we'd totally take.

kirsan31 commented 3 years ago

Ok, lets try this slow way.

We need to add bool property to Form class, let it be MdiChildrenMinimizedAnchorTop.

Draft API Proposal

namespace System.Windows.Forms
{
    public partial class Form : ContainerControl
    {
        /// <summary>
        /// true - anchoring minimized MDI children to the TopLeft of the parent form (windows default). false - anchoring minimized MDI children to the BottomLeft of the parent form (WinForms default)
        /// </summary>
        [SRCategory(nameof(SR.CatWindowStyle))]
        [DefaultValue(false)]
        [SRDescription(nameof(SR.FormMdiChildrenMinimizedAnchorTopDescr))]
        public bool MdiChildrenMinimizedAnchorTop { get; set; }
    }
}

To do.

  1. Discuss a name for the property.
  2. Discuss a description for the property. Translations?
  3. Category: Style or may be Layout?
  4. Implement trivial logic with this property in SetBoundsCore method of MdiClient class and do PR.
merriemcgaw commented 3 years ago

Once we've figured out the name of the property and description (we can get the translations automatically) we may be able to bring this through the API review process. Can you copy the content above to the first comment in the issue? That's what the API folks look for.

kirsan31 commented 3 years ago

@merriemcgaw

Can you copy the content above to the first comment in the issue? That's what the API folks look for.

Done.

kirsan31 commented 3 years ago

I've created draft PR #5221. May be it's speed up things a bit? :)

merriemcgaw commented 3 years ago

@dreddy-work can you start a thread with @terrajobst to get approval for this API change? Let's get that started while @kirsan31 is kindly coding the new API so we know we can get this in .NET 6 for sure

dreddy-work commented 3 years ago

Sure.

terrajobst commented 3 years ago

Easiest is to file an issue in the winforms repo, put it in the 6.0.0 milestone, mark it as blocking and api-ready-for-review. We'll look at it first thing Tuesday morning then.

terrajobst commented 3 years ago
namespace System.Windows.Forms
{
    public partial class Form
    {
        [SRCategory(nameof(SR.CatWindowStyle))]
        [DefaultValue(true)]
        [SRDescription(nameof(SR.FormMdiChildrenMinimizedAnchorTopDescr))]
        public bool MdiChildrenMinimizedAnchorBottom { get; set; }
    }
}
dreddy-work commented 3 years ago
[SRDescription(nameof(SR.FormMdiChildrenMinimizedAnchorTopDescr))]

[SRDescription(nameof(SR.FormMdiChildrenMinimizedAnchorBottomDescr))]

Should be in the same property grid category as MdiChildren

All of them are in CatWindowStyle.

Jenna-Zheng commented 3 years ago

@kirsan31 Verified this function with .NET SDK 6.0.100-rc.1.21424.1, the results MdiChildrenMinimizedAnchorBottom=False and MdiChildrenMinimizedAnchorBottom=True are the same. the result as below. Issue_Test

This is my test project: WinFormsApp2.zip, What else needs to be set up in this project?

kirsan31 commented 3 years ago

@Jenna-Zheng Sadly I can't fully verify this :( I've downloaded and installed this sdk dotnet-sdk-6.0.100-rc.1.21426.23-win-x64.exe from here.

Your project build fine, but when I run it, it's fail with:

image 😲

Win10 20H2. dotnet --info: image

I think it's something related to WinFormsApp2.runtimeconfig.json:

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "frameworks": [
      {
        "name": "Microsoft.NETCore.App",
        "version": "6.0.0-rc.1.21425.15"
      },
      {
        "name": "Microsoft.WindowsDesktop.App",
        "version": "6.0.0-rc.1.21426.11"
      }
    ]
  }
}

But changing it to:

{
  "runtimeOptions": {
    "tfm": "net6.0",
    "framework": {
      "name": "Microsoft.WindowsDesktop.App",
      "version": "6.0.0"
    }
  }
}

have no affect :(

Beside this.

image

So, my guess is: something build wrong in this rc, or something is broken in other place...

Jenna-Zheng commented 3 years ago

@kirsan31 you can use this link https://vsdrop.corp.microsoft.com/file/v1/Products/DevDiv/VS/be0dbbd8fb834a2f05bd0d926c1f414a0ed97045/e9ec17f0-3ec5-0d17-956b-202ebb9e08a4;bootstrappers/Enterprise/vs_enterprise.exe to install VS, dotnet-sdk-rc.1.21424.1-win-x64.exe have been insert into this VS.

kirsan31 commented 3 years ago

@kirsan31 you can use this link https://vsdrop.corp.microsoft.com/file/v1/Products/DevDiv/VS/be0dbbd8fb834a2f05bd0d926c1f414a0ed97045/e9ec17f0-3ec5-0d17-956b-202ebb9e08a4;bootstrappers/Enterprise/vs_enterprise.exe to install VS, dotnet-sdk-rc.1.21424.1-win-x64.exe have been insert into this VS.

Thanks @Jenna-Zheng but I can't open your link (DNS_PROBE_FINISHED_NXDOMAIN) :( But any way I will not install other VS instance on my machine :( last time I done it (during this PR :)) this completely broke my working VS2019 :(

Jenna-Zheng commented 3 years ago

Verified this issue with .NET 5.0&3.1, it is still reproducing, verified it with .NET 6.0, it is fixed Whether mdichildrenminized anchor bottom is set to false or true, the result is the same. Is this expected? .NET 5.0 & 3.1 Issue_Test5 0

.NET 6.0 MdiChildrenMinimizedAnchorBottom=False and MdiChildrenMinimizedAnchorBottom=True Issue_Test

kirsan31 commented 3 years ago

@Jenna-Zheng .NET 6.0 with MdiChildrenMinimizedAnchorBottom=true (default) must work as .NET 5.0 & 3.1 (your 1 gif). .NET 6.0 with MdiChildrenMinimizedAnchorBottom=false must work as your 2 gif.

kirsan31 commented 3 years ago

With latest SDK from https://github.com/dotnet/installer. I have no error as above, but: image

So, we defiantly need some working sdk here...

RussKie commented 3 years ago

This is a known SDK issue, which was fixed. But the fix hasn't apparently flown through.

dreddy-work commented 3 years ago

Installing latest RC1 from here https://github.com/dotnet/installer should not have this issue. RC2 build, investigating.

kirsan31 commented 3 years ago

@dreddy-work

Installing latest RC1 from here https://github.com/dotnet/installer should not have this issue. RC2 build did not get this fix yet.

RC1: image

RC2: image

Jenna-Zheng commented 3 years ago

Verified this function with .NET SDK 6.0.100-rc.1.21424.1 and .NET SDK 6.0.100-rc.2.21429.3now, it is fixed that Customer can use MdiChildrenMinimizedAnchorBottom to revert for Windows default behavior. MdiChildrenMinimizedAnchorBottom=True Issue_Test5 0

MdiChildrenMinimizedAnchorBottom=False Issue_Test