我的显示器上的dpi设置很高,因为该显示器是相当小的3840 x 2160显示器。
这将导致我正在编写的一个应用程序出现问题,因为我在主应用程序中托管了一个控件。我根据WPF-Samples中的一个很好的例子开发了它。
在我的屏幕上,当示例以默认状态运行时,输出看起来像
我能够通过使用
来说明列表控件的大小不正确//MainWindow
PresentationSource source = PresentationSource.FromVisual(this); //Account for any scaling on the screen7
_listControl = new ControlHost(ControlHostElement.ActualWidth, ControlHostElement.ActualHeight, source.CompositionTarget.TransformToDevice.M11, source.CompositionTarget.TransformToDevice.M22);
...
//ControlHost
public ControlHost(double height, double width, double dpXScale, double dpYScale)
{
_hostHeight = (int) (height * dpXScale);
_hostWidth = (int) (width * dpYScale);
}
但是,即使大小正确,托管的UI也不会像程序的其余部分那样缩放。
程序如何根据用户屏幕的DPI缩放托管的UI?
此示例构成了三个主要文件。
ControlHost.cs
// // Copyright (c) Microsoft. All rights reserved.
// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
#region Using directives
using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;
#endregion
namespace WPFHostingWin32Control
{
public class ControlHost : HwndHost
{
internal const int
WsChild = 0x40000000,
WsVisible = 0x10000000,
LbsNotify = 0x00000001,
HostId = 0x00000002,
ListboxId = 0x00000001,
WsVscroll = 0x00200000,
WsBorder = 0x00800000;
private readonly int _hostHeight;
private readonly int _hostWidth;
private IntPtr _hwndHost;
public ControlHost(double height, double width, double dpXScale, double dpYScale)
{
_hostHeight = (int) (height * dpXScale);
_hostWidth = (int) (width * dpYScale);
}
public IntPtr HwndListBox { get; private set; }
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
HwndListBox = IntPtr.Zero;
_hwndHost = IntPtr.Zero;
_hwndHost = CreateWindowEx(0, "static", "",
WsChild | WsVisible,
0, 0,
_hostHeight, _hostWidth,
hwndParent.Handle,
(IntPtr) HostId,
IntPtr.Zero,
0);
HwndListBox = CreateWindowEx(0, "listbox", "",
WsChild | WsVisible | LbsNotify
| WsVscroll | WsBorder,
0, 0,
_hostHeight, _hostWidth,
_hwndHost,
(IntPtr) ListboxId,
IntPtr.Zero,
0);
return new HandleRef(this, _hwndHost);
}
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
handled = false;
return IntPtr.Zero;
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
DestroyWindow(hwnd.Handle);
}
//PInvoke declarations
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(int dwExStyle,
string lpszClassName,
string lpszWindowName,
int style,
int x, int y,
int width, int height,
IntPtr hwndParent,
IntPtr hMenu,
IntPtr hInst,
[MarshalAs(UnmanagedType.AsAny)] object pvParam);
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
}
}
MainWindow.xaml
<Window x:Class="WPFHostingWin32Control.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFHostingWin32Control"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Loaded="On_UIReady">
<DockPanel Background="LightGreen">
<Border Name="ControlHostElement"
Width="200"
Height="200"
HorizontalAlignment="Right"
VerticalAlignment="Top"
BorderBrush="LightGray"
BorderThickness="3"
DockPanel.Dock="Right"/>
<StackPanel>
<Label HorizontalAlignment="Center"
Margin="0,10,0,0"
FontSize="14"
FontWeight="Bold">Control the Control</Label>
<TextBlock Margin="10,10,10,10" >Selected Text: <TextBlock Name="selectedText"/></TextBlock>
<TextBlock Margin="10,10,10,10" >Number of Items: <TextBlock Name="numItems"/></TextBlock>
<Line X1="0" X2="200"
Stroke="LightYellow"
StrokeThickness="2"
HorizontalAlignment="Center"
Margin="0,20,0,0"/>
<Label HorizontalAlignment="Center"
Margin="10,10,10,10">Append an Item to the List</Label>
<StackPanel Orientation="Horizontal">
<Label HorizontalAlignment="Left"
Margin="10,10,10,10">Item Text</Label>
<TextBox HorizontalAlignment="Left"
Name="txtAppend"
Width="200"
Margin="10,10,10,10" />
</StackPanel>
<Button HorizontalAlignment="Left"
Click="AppendText"
Width="75"
Margin="10,10,10,10">Append</Button>
<Line X1="0" X2="200"
Stroke="LightYellow"
StrokeThickness="2"
HorizontalAlignment="Center"
Margin="0,20,0,0"/>
<Label HorizontalAlignment="Center"
Margin="10,10,10,10">Delete the Selected Item</Label>
<Button Click="DeleteText"
Width="125"
Margin="10,10,10,10"
HorizontalAlignment="Left">Delete</Button>
</StackPanel>
</DockPanel>
</Window>
MainWindow.cs
// // Copyright (c) Microsoft. All rights reserved.
// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
namespace WPFHostingWin32Control
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
internal const int
LbnSelchange = 0x00000001,
WmCommand = 0x00000111,
LbGetcursel = 0x00000188,
LbGettextlen = 0x0000018A,
LbAddstring = 0x00000180,
LbGettext = 0x00000189,
LbDeletestring = 0x00000182,
LbGetcount = 0x0000018B;
private Application _app;
private IntPtr _hwndListBox;
private int _itemCount;
private ControlHost _listControl;
private Window _myWindow;
private int _selectedItem;
public MainWindow()
{
InitializeComponent();
}
private void On_UIReady(object sender, EventArgs e)
{
_app = Application.Current;
_myWindow = _app.MainWindow;
_myWindow.SizeToContent = SizeToContent.WidthAndHeight;
PresentationSource source = PresentationSource.FromVisual(this); //Account for any scaling on the screen7
_listControl = new ControlHost(ControlHostElement.ActualWidth, ControlHostElement.ActualHeight, source.CompositionTarget.TransformToDevice.M11, source.CompositionTarget.TransformToDevice.M22);
ControlHostElement.Child = _listControl;
_listControl.MessageHook += ControlMsgFilter;
_hwndListBox = _listControl.HwndListBox;
for (var i = 1; i <= 100; i++) //populate listbox
{
var itemText = "Item" + i;
SendMessage(_hwndListBox, LbAddstring, IntPtr.Zero, itemText);
}
_itemCount = SendMessage(_hwndListBox, LbGetcount, IntPtr.Zero, IntPtr.Zero);
numItems.Text = "" + _itemCount;
}
private void AppendText(object sender, EventArgs args)
{
if (txtAppend.Text != string.Empty)
{
SendMessage(_hwndListBox, LbAddstring, IntPtr.Zero, txtAppend.Text);
}
_itemCount = SendMessage(_hwndListBox, LbGetcount, IntPtr.Zero, IntPtr.Zero);
numItems.Text = "" + _itemCount;
}
private void DeleteText(object sender, EventArgs args)
{
_selectedItem = SendMessage(_listControl.HwndListBox, LbGetcursel, IntPtr.Zero, IntPtr.Zero);
if (_selectedItem != -1) //check for selected item
{
SendMessage(_hwndListBox, LbDeletestring, (IntPtr) _selectedItem, IntPtr.Zero);
}
_itemCount = SendMessage(_hwndListBox, LbGetcount, IntPtr.Zero, IntPtr.Zero);
numItems.Text = "" + _itemCount;
}
private IntPtr ControlMsgFilter(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
int textLength;
handled = false;
if (msg == WmCommand)
{
switch ((uint) wParam.ToInt32() >> 16 & 0xFFFF) //extract the HIWORD
{
case LbnSelchange: //Get the item text and display it
_selectedItem = SendMessage(_listControl.HwndListBox, LbGetcursel, IntPtr.Zero, IntPtr.Zero);
textLength = SendMessage(_listControl.HwndListBox, LbGettextlen, IntPtr.Zero, IntPtr.Zero);
var itemText = new StringBuilder();
SendMessage(_hwndListBox, LbGettext, _selectedItem, itemText);
selectedText.Text = itemText.ToString();
handled = true;
break;
}
}
return IntPtr.Zero;
}
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
int msg,
IntPtr wParam,
IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
int msg,
int wParam,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessage(IntPtr hwnd,
int msg,
IntPtr wParam,
string lParam);
}
}
答案 0 :(得分:0)
默认情况下,Winforms不能很好地处理高DPI设置。如评论中所述,您可以尝试一些可用的清单设置来调整其大小。如果这不适用于您的情况,那么您将需要自行扩展控件。大多数Winforms控件都有一个Scale方法,您可以在调整高度和宽度时调用它:
_listControl.Scale(dpXScale, dpYScale);
当然,由于您的实际代码使用了自定义控件,因此里程可能会有所不同。