이전 포스팅에서 WindowsFormsHost 사용시 Scroll 문제를 해결하는 방법에 대해서 알아봤습니다.
2024.02.06 - [코딩/C#] - WPF WindowsFormsHost 사용 시 Scroll 문제
그런데 이 내용대로 했는데 특정 컴퓨터에 일부가 잘리는 현상이 발생되었습니다.
확인해 보니 DPI 문제로 인해서 발생되는 문제였습니다.
이를 해결하기 위해서는 DpiScale 을 구해서 처리해야합니다.
아래는 이를 해결한 코드입니다.
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Media;
public class WindowsFormsHostEx : WindowsFormsHost
{
#region DllImports
[DllImport("User32.dll", SetLastError = true)]
static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
[DllImport("gdi32.dll")]
static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
#endregion
#region Events
public event EventHandler LocationChanged;
#endregion
#region Members
private PresentationSource presentationSource;
#endregion
#region Properties
private ScrollViewer ParentScrollViewer { get; set; }
private bool Scrolling { get; set; }
public bool Resizing { get; set; }
private Visual RootVisual
{
get
{
presentationSource = PresentationSource.FromVisual(this);
return presentationSource.RootVisual;
}
}
#endregion
#region Constructors
public WindowsFormsHostEx()
{
PresentationSource.AddSourceChangedHandler(this, SourceChangedEventHandler);
}
#endregion
#region Methods
protected override void OnWindowPositionChanged(Rect rcBoundingBox)
{
DpiScale dpiScale = VisualTreeHelper.GetDpi(this);
base.OnWindowPositionChanged(rcBoundingBox);
Rect newRect = ScaleRectDownFromDPI(rcBoundingBox, dpiScale);
Rect finalRect;
if (ParentScrollViewer != null)
{
ParentScrollViewer.ScrollChanged += ParentScrollViewer_ScrollChanged;
ParentScrollViewer.SizeChanged += ParentScrollViewer_SizeChanged;
ParentScrollViewer.Loaded += ParentScrollViewer_Loaded;
}
if (Scrolling || Resizing)
{
if (ParentScrollViewer == null)
return;
MatrixTransform tr = RootVisual.TransformToDescendant(ParentScrollViewer) as MatrixTransform;
var scrollRect = new Rect(new Size(ParentScrollViewer.ViewportWidth, ParentScrollViewer.ViewportHeight));
var c = tr.TransformBounds(newRect);
var intersect = Rect.Intersect(scrollRect, c);
if (!intersect.IsEmpty)
{
tr = ParentScrollViewer.TransformToDescendant(this) as MatrixTransform;
intersect = tr.TransformBounds(intersect);
finalRect = ScaleRectUpToDPI(intersect, dpiScale);
}
else
finalRect = intersect = new Rect();
int x1 = (int)Math.Round(finalRect.X);
int y1 = (int)Math.Round(finalRect.Y);
int x2 = (int)Math.Round(finalRect.Right);
int y2 = (int)Math.Round(finalRect.Bottom);
SetRegion(x1, y1, x2, y2);
this.Scrolling = false;
this.Resizing = false;
}
LocationChanged?.Invoke(this, new EventArgs());
}
private void ParentScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
this.Resizing = true;
}
private void ParentScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
{
this.Resizing = true;
}
private void ParentScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.VerticalChange != 0 || e.HorizontalChange != 0 || e.ExtentHeightChange != 0 || e.ExtentWidthChange != 0)
Scrolling = true;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
PresentationSource.RemoveSourceChangedHandler(this, SourceChangedEventHandler);
_presentationSource = null;
}
}
private void SourceChangedEventHandler(Object sender, SourceChangedEventArgs e)
{
if (ParentScrollViewer != null)
{
ParentScrollViewer.ScrollChanged -= ParentScrollViewer_ScrollChanged;
ParentScrollViewer.SizeChanged -= ParentScrollViewer_SizeChanged;
ParentScrollViewer.Loaded -= ParentScrollViewer_Loaded;
}
ParentScrollViewer = FindParentScrollViewer();
}
private ScrollViewer FindParentScrollViewer()
{
DependencyObject vParent = this;
ScrollViewer parentScroll = null;
while (vParent != null)
{
parentScroll = vParent as ScrollViewer;
if (parentScroll != null)
break;
vParent = LogicalTreeHelper.GetParent(vParent);
}
return parentScroll;
}
private void SetRegion(int x1, int y1, int x2, int y2)
{
SetWindowRgn(Handle, CreateRectRgn(x1, y1, x2, y2), true);
}
public static Rect ScaleRectDownFromDPI(Rect _sourceRect, DpiScale dpiScale)
{
double dpiX = dpiScale.DpiScaleX;
double dpiY = dpiScale.DpiScaleY;
return new Rect(new Point(_sourceRect.X / dpiX, _sourceRect.Y / dpiY), new System.Windows.Size(_sourceRect.Width / dpiX, _sourceRect.Height / dpiY));
}
public static Rect ScaleRectUpToDPI(Rect _toScaleUp, DpiScale dpiScale)
{
double dpiX = dpiScale.DpiScaleX;
double dpiY = dpiScale.DpiScaleY;
return new Rect(new Point(_toScaleUp.X * dpiX, _toScaleUp.Y * dpiY), new System.Windows.Size(_toScaleUp.Width * dpiX, _toScaleUp.Height * dpiY));
}
#endregion
}
위 코드를 이용하면 어떤 컴퓨터에서 사용하든 잘리는 현상이 나타나지 않습니다.
C# ExpandObject 사용하여 동적 데이터 다루기 (0) | 2024.02.23 |
---|---|
.NET 랜덤 한 X,Y 축 값 도출하기 (0) | 2024.02.18 |
TChart Legend 클릭 시 잘못된 Series Index 반환하는 버그 수정 (0) | 2024.02.16 |
TChart Legend 클릭 시 선택한 Series 강조하기 (0) | 2024.02.15 |
WPF WindowsFormsHost 사용 시 Scroll 문제 (0) | 2024.02.06 |