Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DPI scaling support for .NET Framework|Core WinForms apps #563

Closed
Ichibot200 opened this issue Sep 25, 2020 · 15 comments
Closed

DPI scaling support for .NET Framework|Core WinForms apps #563

Ichibot200 opened this issue Sep 25, 2020 · 15 comments

Comments

@Ichibot200
Copy link

Plot draggables are not DPI aware:
I have had a little problem with draggable lines on my 4K screen, with 144 DPI.
The cursor handle was offset to the left from the vertical draggable line exactly 1.5 times - the ratio of 144 and 96 DPI.

In terms of a fix:
There was a little start in the FormsPlot.cs, but was not enough to support draggables on higher DPI screens.

So I dug in to the code and here is my quick fix for this situation:

IDraggable plottableBeingDragged = null;
private bool isMovingDraggable { get { return (plottableBeingDragged != null); } }
private double dpiScale = Settings.DPIScale; //Heres hoping that WinForms becomes DPI aware

---

private void PbPlot_MouseDown(object sender, MouseEventArgs e)
        {
            var mousePixel = e.Location;
            plottableBeingDragged = plt.GetDraggableUnderMouse(mousePixel.X / dpiScale, mousePixel.Y / dpiScale);

---

public PointF mouseCoordinates { get { return plt.CoordinateFromPixel(mouseLocation); } }
        Point mouseLocation;

        private void PbPlot_MouseMove(object sender, MouseEventArgs e)
        {

---

private void MouseMovedToMoveDraggable(MouseEventArgs e)
        {
            plottableBeingDragged.DragTo(
                plt.CoordinateFromPixelX(e.Location.X / dpiScale),
                plt.CoordinateFromPixelY(e.Location.Y / dpiScale),
                isShiftPressed, isAltPressed, isCtrlPressed);
            OnMouseDragPlottable(EventArgs.Empty);
            Render(true, lowQuality: lowQualityWhileDragging);
        }

---

private void MouseMovedWithoutInteraction(MouseEventArgs e)
        {
            if (showCoordinatesTooltip)
            {
                double coordX = plt.CoordinateFromPixelX(e.Location.X);
                double coordY = plt.CoordinateFromPixelY(e.Location.Y);
                tooltip.Show($"{coordX:N2}, {coordY:N2}", this, e.Location.X + 15, e.Location.Y);
            }

            // set the cursor based on what's beneath it
            var draggableUnderCursor = plt.GetDraggableUnderMouse(e.Location.X/dpiScale, e.Location.Y/dpiScale);
            var spCursor = (draggableUnderCursor is null) ? Config.Cursor.Arrow : draggableUnderCursor.DragCursor;
            pbPlot.Cursor = GetCursor(spCursor);
        }

PS! Talking already about DPI awareness... There is also an issue when I try to drag the WinForms window from one screen to another (from 96 to 144 and visa-versa), the plot does not rescale to fit the bounds of the parent container. The resizing works once I maximize the window to full screen.

@StendProg
Copy link
Contributor

Hi @citizen3942,
ScottPlott.WPFControl already have dpi scale support.
I think that it does not exist for ScottPlot.WinFormsControl is due to the poor support of dpi scaling by WinForms itself.

@swharden
Copy link
Member

swharden commented Sep 25, 2020

poor support of dpi scaling by WinForms itself

WinForms traditionally has had terrible support for DPI scaling which has caused many of these types of problems in the past.

@citizen3942 are you using a .NET Core WinForms application? I wonder if .NET Core WinForms has better support for DPI scaling, which may mean this behavior/bug is new and different depending on which platform is being used.

Present State of DPI Scaling in FormsPlot

DPI scaling factor is defined here. It's initialized to 1 and never assigned to. Perhaps this should be detected and assigned to in the control constructor?

https://github.com/swharden/ScottPlot/blob/2c37643fb9994d75937169978d8200c01637d39a/src/ScottPlot.WinForms/FormsPlot.cs#L249

dpiScale is used to determine where (in coordinate space) the mouse is:

https://github.com/swharden/ScottPlot/blob/2c37643fb9994d75937169978d8200c01637d39a/src/ScottPlot.WinForms/FormsPlot.cs#L325-L330

Regarding switching monitors, I haven't looked into this yet, but I wonder if there's an event that fires when DPI scale changes? If so, we could use that event to update the dpiScale value

@swharden
Copy link
Member

swharden commented Sep 26, 2020

I was right! This is a difference between .NET Framework and .NET Core WinForms apps.

This is Program.cs from a fresh .NET Core WinForms application:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp6
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.SetHighDpiMode(HighDpiMode.SystemAware); // <-- THIS IS NEW
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

If you comment-out the line to make the WinForms application DPI-aware, ScottPlot's mouse tracking works as expected.

I'll see what I can do to improve WinForms DPI scaling support in .NET Core applications though...

@swharden swharden changed the title Plot draggables DPI awareness DPI scaling support for .NET Core WinForms apps Sep 26, 2020
swharden added a commit that referenced this issue Sep 26, 2020
swharden added a commit that referenced this issue Sep 26, 2020
improves support for .NET Core WinForms apps (.NET Framework WinForms does not support DPI scaling) #563
@swharden
Copy link
Member

swharden commented Sep 26, 2020

@citizen3942 This issue just affected .NET Core WinForms apps, but I think it's been fixed now 3530434 (in just two lines)

swharden added a commit that referenced this issue Sep 26, 2020
@Ichibot200
Copy link
Author

are you using a .NET Core WinForms application?
I am developing a WinForms .NET Framework 4.7.2 application.

Perhaps line 250 can be moved after line 17.
https://github.com/swharden/ScottPlot/blob/b337f9354dd631908a64873be964b15a75045edc/src/ScottPlot.WinForms/FormsPlot.cs#L250

https://github.com/swharden/ScottPlot/blob/b337f9354dd631908a64873be964b15a75045edc/src/ScottPlot.WinForms/FormsPlot.cs#L17

I still want to believe that those functions that I pointed out in my first posts should have this dpiScale division for XY Locations.

swharden added a commit that referenced this issue Sep 27, 2020
@swharden
Copy link
Member

line 250 can be moved

Good suggestion! It also doesn't need a default value anymore.

https://github.com/swharden/ScottPlot/blob/00cf7647b2783c15be5a768f0703eaf5522e4ce5/src/ScottPlot.WinForms/FormsPlot.cs#L18

those functions that I pointed out in my first posts should have this dpiScale division for XY Locations.

Dang, I messed-up here. I thought this problem was fully resolved by 3530434 but I overlooked the fact that in my Program.cs SetHighDpiMode was using DpiUnawareGdiScaled (instead of SystemAware, the default for new .NET Core WinForms apps). When I use this setting I find that draggables are still buggy when using DPI scaling. Thank you for pointing this out @citizen3942!

@swharden swharden reopened this Sep 27, 2020
swharden added a commit that referenced this issue Sep 27, 2020
This is the default DPI mode for new .NET Core WinForms apps #563
@swharden
Copy link
Member

swharden commented Sep 27, 2020

This issue is thornier than I thought. DPI scaling support for draggables is currently broken in the WPF control too. The most elegant fix (always handling scaling at the control level) is one that breaks the highlightable scatter plot demo... but that breaking change is probably the least impactful, since it's confined to a single plottable, and the best solution is to make this change and to improve the demo.

swharden added a commit that referenced this issue Sep 27, 2020
DPI state is no longer stored in the plot settings module, but instead handled at the control level.

This change fixes draggables which were previously broken.

WPF users wanting to use ScatterPlotHighlight must get coordinates using wpfPlot1.GetMouseCoordinates() as seen in the demo application.
@swharden
Copy link
Member

I think it's fixed now, and interestingly the fix was achieved only by deleting code... 319f94d

I just uploaded version 4.0.42 to NuGet (it might take a couple minutes to show up) but @citizen3942 I'd appreciate your assessment as to whether it works as expected in your hands!

@Ichibot200
Copy link
Author

The V Line and H Line positioning are now correct in v4.0.42, with different DPI settings.
Though switching between two separate monitors with different DPI-s does not automatically scale plots. Yet they do scale correctly after I resize the form (maximize) on either monitor. This is some really bizarre behavior on behalf of .NET FW I guess. I cannot figure out which event takes care of subsequent resizing after the DpiChanged Event has fired, so I could force invoke some event to resize and render plots.
My plots are set up in a TableLayoutPanel cell with property - Dock: Fill.
TableLayoutPanel - AutoSize: True; AutoSizeMode: GrowAndShrink; Dock: Fill

PS! I downloaded 4.0.42, but the package version was still 4.0.41.

@swharden
Copy link
Member

swharden commented Oct 8, 2020

switching between two separate monitors with different DPI-s does not automatically scale plots.

This sounds like something we should fix! If there is a DpiChanged event, perhaps we can make it invoke the same method that SizeChanged calls.

PS! I downloaded 4.0.42, but the package version was still 4.0.41.

I'm a little confused by this. Can you clarify what the issue is?

Thanks for following up!

@swharden swharden reopened this Oct 8, 2020
@Ichibot200
Copy link
Author

Ichibot200 commented Oct 8, 2020

I'm a little confused by this. Can you clarify what the issue is?

0fdc5f5 fixed it but
The latest release 4.0.42 source I downloaded had an old commit
319f94d
that did not have those changes.

@Ichibot200
Copy link
Author

switching between two separate monitors with different DPI-s does not automatically scale plots.

To visualize the issue

Before (the initial monitor with higher DPI):
image

After (the second monitor with lower DPI):
image

Dragging the window back to the initial monitor:
image

they do scale correctly after I resize the form (maximize) on either monitor
is still valid.

@Ichibot200
Copy link
Author

Ichibot200 commented Oct 30, 2020

I found a fix for this.

I added AutoScaleMode = AutoScaleMode.Inherit; to FormsPlot.cs InitializeScottPlot function:

 private void InitializeScottPlot(bool applyDefaultStyle = true)
        {
            AutoScaleMode = AutoScaleMode.Inherit;
            
            ContextMenuStrip = DefaultRightClickMenu();

            pbPlot.MouseWheel -= PbPlot_MouseWheel; // unsubscribe previous handler
            pbPlot.MouseWheel += PbPlot_MouseWheel;
...

It will help to Inherit the scaling properties of the parent control(s), that are set to DpiAware.

@Ichibot200 Ichibot200 changed the title DPI scaling support for .NET Core WinForms apps DPI scaling support for .NET Framework|Core WinForms apps Oct 30, 2020
@swharden
Copy link
Member

Hey @citizen3942 thanks for following up on this! Looks like a great solution. This might be a nice way to totally disable display scaling for ScottPlot controls (and delete a lot of that display scale checking code) 🤔

I'm a bit embarrassed how long it's taken me to get to this issue. I got really sucked in to #605 and am still working on that... I hope to get it merged soon so I can clear out these issues!

@swharden swharden removed the Patch/PR label Nov 18, 2020
@swharden
Copy link
Member

I'm actually having a hard time recreating this issue. I tried:

  • Set display scaling to 125% and 100% on my two monitors
  • Create a new .NET Framework 4.7.2 project
  • NuGet install ScottPlot.WinForms (4.0.42) and drag/drop formsPlot1 onto the window
  • F5 (run) and drag the window back and forth between the two monitors

I can't get it to display improperly 🤔 If you can help me recreate the issue I can work toward resolving it, but otherwise I suspect it's working pretty well as it is!

Ironically all this will get thrown out the window as people slowly migrate to .NET Core with time 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants