I have a high dpi setting on my monitor as the monitor is a fairly small 3840 x 2160 monitor.
This is causing issues with one of the applications I am writing as I am hosting a control in my main application. I developed it based off a nice example in the WPF-Samples.
On my screen, when the example is run in it's default state, the output looks like
I was able to account for the list control being the incorrect size by using
//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);
}
However, even when it is the correct size, the hosted UI is not scaled as the rest of the program is.
How could the program scale the hosted UI based on the DPI of the user's screen?
There are three main files that make up the this example.
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);
}
}
Winforms does not handle high-DPI settings very well by default. As stated in the comments, you can try some of the available manifest settings to get it to resize. If that won't work for your situation then you will need to scale the control yourself. Most Winforms controls have a Scale method you could call when you adjust the height and width:
_listControl.Scale(dpXScale, dpYScale);
of course, since your actual code uses a custom control your mileage may vary.
User contributions licensed under CC BY-SA 3.0