Skip to content

Commit

Permalink
Fix maximized window padding
Browse files Browse the repository at this point in the history
  • Loading branch information
Kinnara committed Mar 29, 2020
1 parent da60c77 commit dcae0da
Show file tree
Hide file tree
Showing 11 changed files with 320 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ void UpdateFlowsFromAndFlowsTo()
// Ensure the SizeOfSet and PositionInSet automation properties
// for the primary commands and the MoreButton account for the
// potential MoreButton.
#if NETCOREAPP3_0
#if NETCOREAPP
EnsureAutomationSetCountAndPosition();
#endif

Expand Down Expand Up @@ -615,7 +615,7 @@ void UpdateTemplateSettings()
}
}

#if NETCOREAPP3_0
#if NETCOREAPP
void EnsureAutomationSetCountAndPosition()
{
var moreButton = m_moreButton;
Expand Down
2 changes: 1 addition & 1 deletion ModernWpf.Controls/Common/UnsafeNativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace ModernWpf
{
internal class UnsafeNativeMethods
internal static class UnsafeNativeMethods
{
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern IntPtr GetActiveWindow();
Expand Down
6 changes: 3 additions & 3 deletions ModernWpf.Controls/RadioButtons/RadioButtons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ void OnRepeaterElementPrepared(ItemsRepeater sender, ItemsRepeaterElementPrepare
Select(args.Index);
}
}
#if NETCOREAPP3_0
#if NETCOREAPP
var repeater = m_repeater;
if (repeater != null)
{
Expand Down Expand Up @@ -462,7 +462,7 @@ void OnRepeaterElementIndexChanged(ItemsRepeater sender, ItemsRepeaterElementInd
var element = args.Element;
if (element != null)
{
#if NETCOREAPP3_0
#if NETCOREAPP
element.SetValue(AutomationProperties.PositionInSetProperty, args.NewIndex + 1);
#endif
// When the selected item's index changes, update selection to match
Expand All @@ -478,7 +478,7 @@ void OnRepeaterElementIndexChanged(ItemsRepeater sender, ItemsRepeaterElementInd

void OnRepeaterCollectionChanged(object sender, object args)
{
#if NETCOREAPP3_0
#if NETCOREAPP
var repeater = m_repeater;
if (repeater != null)
{
Expand Down
9 changes: 8 additions & 1 deletion ModernWpf.SampleApp/ModernWpf.SampleApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@
<OutputType>WinExe</OutputType>
<TargetFrameworks>netcoreapp3.1;net45;net462</TargetFrameworks>
<UseWPF>true</UseWPF>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>Assets\AppIcon.ico</ApplicationIcon>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' == 'net45'">
<ApplicationManifest>app.net45.manifest</ApplicationManifest>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' != 'net45'">
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>

<ItemGroup>
<Page Update="Properties\DesignTimeResources.xaml">
Expand Down
76 changes: 76 additions & 0 deletions ModernWpf.SampleApp/app.net45.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Specifying requestedExecutionLevel element will disable file and registry virtualization.
Remove this element if your application requires this virtualization for backwards
compatibility.
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->

<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />

<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />

<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />

<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />

<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />

</application>
</compatibility>

<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->

<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>


<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->

</assembly>
138 changes: 137 additions & 1 deletion ModernWpf/Controls/Primitives/WindowHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
using System.ComponentModel;
using System;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

namespace ModernWpf.Controls.Primitives
{
Expand Down Expand Up @@ -74,5 +79,136 @@ private static void OnUseModernWindowStyleChanged(DependencyObject d, Dependency
}

#endregion

#region IsAutoPaddingEnabled

public static readonly DependencyProperty IsAutoPaddingEnabledProperty =
DependencyProperty.RegisterAttached(
"IsAutoPaddingEnabled",
typeof(bool),
typeof(WindowHelper),
new PropertyMetadata(false, OnIsAutoPaddingEnabledChanged));

public static bool GetIsAutoPaddingEnabled(Window window)
{
return (bool)window.GetValue(IsAutoPaddingEnabledProperty);
}

public static void SetIsAutoPaddingEnabled(Window window, bool value)
{
window.SetValue(IsAutoPaddingEnabledProperty, value);
}

private static void OnIsAutoPaddingEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is Window window)
{
if ((bool)e.NewValue)
{
#if NETCOREAPP || NET462
window.DpiChanged += OnDpiChanged;
window.SetValue(DpiProperty, VisualTreeHelper.GetDpi(window));
#endif
var binding = new MultiBinding
{
Bindings =
{
new Binding { Path = new PropertyPath(Window.WindowStateProperty), Source = window },
new Binding("(SystemParameters.WindowResizeBorderThickness)"),
#if NETCOREAPP || NET462
new Binding { Path = new PropertyPath(DpiProperty), Source = window },
#endif
},
Converter = new PaddingConveter()
};
window.SetBinding(Control.PaddingProperty, binding);
}
else
{
#if NETCOREAPP || NET462
window.DpiChanged -= OnDpiChanged;
window.ClearValue(DpiProperty);
#endif
window.ClearValue(Control.PaddingProperty);
}
}
}

#endregion

#if NETCOREAPP || NET462
private static readonly DependencyProperty DpiProperty =
DependencyProperty.RegisterAttached(
"Dpi",
typeof(DpiScale),
typeof(WindowHelper),
new PropertyMetadata(new DpiScale(1, 1)));

private static void OnDpiChanged(object sender, DpiChangedEventArgs e)
{
((Window)sender).SetValue(DpiProperty, e.NewDpi);
}
#endif

private class PaddingConveter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length >= 2 &&
values[0] is WindowState windowState)
{
if (windowState == WindowState.Maximized)
{
#if NETCOREAPP || NET462
if (values.Length == 3 &&
values[1] is Thickness windowResizeBorderThickness &&
values[2] is DpiScale dpi)
{
Size frameSize = new Size(NativeMethods.GetSystemMetrics(SM_CXSIZEFRAME),
NativeMethods.GetSystemMetrics(SM_CYSIZEFRAME));
Size frameSizeInDips = Standard.DpiHelper.DeviceSizeToLogical(frameSize, dpi.DpiScaleX, dpi.DpiScaleY);

int borderPadding = NativeMethods.GetSystemMetrics(SM_CXPADDEDBORDER);
Size borderPaddingSize = new Size(borderPadding, borderPadding);
Size borderPaddingSizeInDips = Standard.DpiHelper.DeviceSizeToLogical(borderPaddingSize, dpi.DpiScaleX, dpi.DpiScaleY);

double leftRight = frameSizeInDips.Width + borderPaddingSizeInDips.Width;
double topBottom = frameSizeInDips.Height + borderPaddingSizeInDips.Height;
return new Thickness(leftRight, topBottom, leftRight, topBottom);
}
#else
if (values.Length == 2 &&
values[1] is Thickness windowResizeBorderThickness)
{
const int borderPadding = 4;
return new Thickness(windowResizeBorderThickness.Left + borderPadding,
windowResizeBorderThickness.Top + borderPadding,
windowResizeBorderThickness.Right + borderPadding,
windowResizeBorderThickness.Bottom + borderPadding);
}
#endif
}
}

return new Thickness();
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}

#if NETCOREAPP || NET462
private static class NativeMethods
{
[DllImport("user32.dll")]
public static extern int GetSystemMetrics(int nIndex);
}

private const int SM_CXSIZEFRAME = 32;
private const int SM_CYSIZEFRAME = 33;
private const int SM_CXPADDEDBORDER = 92;
#endif
}
}
}
1 change: 1 addition & 0 deletions ModernWpf/Controls/Primitives/WindowPaddingConveter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

namespace ModernWpf.Controls.Primitives
{
[Obsolete]
public class WindowPaddingConveter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
Expand Down
85 changes: 85 additions & 0 deletions ModernWpf/Helpers/DpiHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.



namespace Standard
{
using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows;
using System.Windows.Media;

internal static class DpiHelper
{
[ThreadStatic]
private static Matrix _transformToDevice;
[ThreadStatic]
private static Matrix _transformToDip;

/// <summary>
/// Convert a point in device independent pixels (1/96") to a point in the system coordinates.
/// </summary>
/// <param name="logicalPoint">A point in the logical coordinate system.</param>
/// <returns>Returns the parameter converted to the system's coordinates.</returns>
public static Point LogicalPixelsToDevice(Point logicalPoint, double dpiScaleX, double dpiScaleY)
{
_transformToDevice = Matrix.Identity;
_transformToDevice.Scale(dpiScaleX, dpiScaleY);
return _transformToDevice.Transform(logicalPoint);
}

/// <summary>
/// Convert a point in system coordinates to a point in device independent pixels (1/96").
/// </summary>
/// <param name="logicalPoint">A point in the physical coordinate system.</param>
/// <returns>Returns the parameter converted to the device independent coordinate system.</returns>
public static Point DevicePixelsToLogical(Point devicePoint, double dpiScaleX, double dpiScaleY)
{
_transformToDip = Matrix.Identity;
_transformToDip.Scale(1d / dpiScaleX, 1d / dpiScaleY);
return _transformToDip.Transform(devicePoint);
}

[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
public static Rect LogicalRectToDevice(Rect logicalRectangle, double dpiScaleX, double dpiScaleY)
{
Point topLeft = LogicalPixelsToDevice(new Point(logicalRectangle.Left, logicalRectangle.Top), dpiScaleX, dpiScaleY);
Point bottomRight = LogicalPixelsToDevice(new Point(logicalRectangle.Right, logicalRectangle.Bottom), dpiScaleX, dpiScaleY);

return new Rect(topLeft, bottomRight);
}

public static Rect DeviceRectToLogical(Rect deviceRectangle, double dpiScaleX, double dpiScaleY)
{
Point topLeft = DevicePixelsToLogical(new Point(deviceRectangle.Left, deviceRectangle.Top), dpiScaleX, dpiScaleY);
Point bottomRight = DevicePixelsToLogical(new Point(deviceRectangle.Right, deviceRectangle.Bottom), dpiScaleX, dpiScaleY);

return new Rect(topLeft, bottomRight);
}

[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
public static Size LogicalSizeToDevice(Size logicalSize, double dpiScaleX, double dpiScaleY)
{
Point pt = LogicalPixelsToDevice(new Point(logicalSize.Width, logicalSize.Height), dpiScaleX, dpiScaleY);

return new Size { Width = pt.X, Height = pt.Y };
}

public static Size DeviceSizeToLogical(Size deviceSize, double dpiScaleX, double dpiScaleY)
{
Point pt = DevicePixelsToLogical(new Point(deviceSize.Width, deviceSize.Height), dpiScaleX, dpiScaleY);

return new Size(pt.X, pt.Y);
}

public static Thickness LogicalThicknessToDevice(Thickness logicalThickness, double dpiScaleX, double dpiScaleY)
{
Point topLeft = LogicalPixelsToDevice(new Point(logicalThickness.Left, logicalThickness.Top), dpiScaleX, dpiScaleY);
Point bottomRight = LogicalPixelsToDevice(new Point(logicalThickness.Right, logicalThickness.Bottom), dpiScaleX, dpiScaleY);

return new Thickness(topLeft.X, topLeft.Y, bottomRight.X, bottomRight.Y);
}
}
}
Loading

0 comments on commit dcae0da

Please sign in to comment.