英文:
WindowsFormsHost control is being overclipped when embedded inside a WPF ScrollViewer
问题
在我的代码中,我从WindowsFormsHostEx
而不是WindowsFormsHost
继承,因为这样做会在调整窗口大小时对Winform控件应用裁剪,以便label
内容始终可见。
如果我使用WindowsFormsHost
,则所有空白空间将被填满,但下方的label
内容会被覆盖。这也不是我想要的效果。
以下是你的代码的翻译部分:
我有一个嵌套在WPF ScrollViewer中的WindowsFormsHost,但由于某种原因,在应用裁剪后,WindowsFormsHost似乎没有填满所有可用空间,即控件已经过度裁剪。
这是它的外观 - 请注意,有很多白色空间,实际上应该填充为蓝色。
[![enter image description here][2]][2]
以下是我的完整代码:
public class DummyWinformControl : WindowsFormsHostEx /* WindowsFormsHost */
{
public DummyWinformControl()
{
var panel = new System.Windows.Forms.Panel();
panel.Dock = DockStyle.Fill;
panel.BackColor = System.Drawing.Color.Blue;
Child = panel;
}
}
/// <summary>
/// https://stackoverflow.com/a/18084481
/// </summary>
public class WindowsFormsHostEx : WindowsFormsHost
{
private PresentationSource _presentationSource;
public WindowsFormsHostEx()
{
PresentationSource.AddSourceChangedHandler(this, SourceChangedEventHandler);
}
protected override void OnWindowPositionChanged(Rect rcBoundingBox)
{
base.OnWindowPositionChanged(rcBoundingBox);
if (ParentScrollViewer == null)
return;
GeneralTransform tr = RootVisual.TransformToDescendant(ParentScrollViewer);
var scrollRect = new Rect(new Size(ParentScrollViewer.ViewportWidth, ParentScrollViewer.ViewportHeight));
var intersect = Rect.Intersect(scrollRect, tr.TransformBounds(rcBoundingBox));
if (!intersect.IsEmpty)
{
tr = ParentScrollViewer.TransformToDescendant(this);
intersect = tr.TransformBounds(intersect);
}
else
intersect = new Rect();
int x1 = (int)Math.Round(intersect.Left);
int y1 = (int)Math.Round(intersect.Top);
int x2 = (int)Math.Round(intersect.Right);
int y2 = (int)Math.Round(intersect.Bottom);
SetRegion(x1, y1, x2, y2);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
PresentationSource.RemoveSourceChangedHandler(this, SourceChangedEventHandler);
}
private void SourceChangedEventHandler(Object sender, SourceChangedEventArgs e)
{
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);
}
private Visual RootVisual
{
get
{
if (_presentationSource == null)
_presentationSource = PresentationSource.FromVisual(this);
return _presentationSource.RootVisual;
}
}
private ScrollViewer ParentScrollViewer { get; set; }
[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, nBottomRect);
}
这是MainWindow.XAML的内容:
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid ScrollViewer.CanContentScroll="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GroupBox Header="abc" Grid.Row="0" BorderThickness="1" Width="400" Height="600">
<local:DummyWinformControl />
</GroupBox>
<Label Content="Hello world" Grid.Row="1"/>
</Grid>
</ScrollViewer>
我希望这可以帮助你解决问题。
英文:
I have a WindowsFormsHost
nested inside a WPF ScrollViewer, for some reason, after applying clipping it seems that the WIndowsFormsHost
is not filling up all of the available spaces, ie: the control has been overclipped.
Here's how it looks like-- note that there are a lot of white space, which really should be filled with blue color.
Here's my code in totality:
public class DummyWinformControl : WindowsFormsHostEx /* WindowsFormsHost */
{
public DummyWinformControl()
{
var panel = new System.Windows.Forms.Panel();
panel.Dock = DockStyle.Fill;
panel.BackColor = System.Drawing.Color.Blue;
Child = panel;
}
}
/// <summary>
/// https://stackoverflow.com/a/18084481
/// </summary>
public class WindowsFormsHostEx : WindowsFormsHost
{
private PresentationSource _presentationSource;
public WindowsFormsHostEx()
{
PresentationSource.AddSourceChangedHandler(this, SourceChangedEventHandler);
}
protected override void OnWindowPositionChanged(Rect rcBoundingBox)
{
base.OnWindowPositionChanged(rcBoundingBox);
if (ParentScrollViewer == null)
return;
GeneralTransform tr = RootVisual.TransformToDescendant(ParentScrollViewer);
var scrollRect = new Rect(new Size(ParentScrollViewer.ViewportWidth, ParentScrollViewer.ViewportHeight));
var intersect = Rect.Intersect(scrollRect, tr.TransformBounds(rcBoundingBox));
if (!intersect.IsEmpty)
{
tr = ParentScrollViewer.TransformToDescendant(this);
intersect = tr.TransformBounds(intersect);
}
else
intersect = new Rect();
int x1 = (int)Math.Round(intersect.Left);
int y1 = (int)Math.Round(intersect.Top);
int x2 = (int)Math.Round(intersect.Right);
int y2 = (int)Math.Round(intersect.Bottom);
SetRegion(x1, y1, x2, y2);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
PresentationSource.RemoveSourceChangedHandler(this, SourceChangedEventHandler);
}
private void SourceChangedEventHandler(Object sender, SourceChangedEventArgs e)
{
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);
}
private Visual RootVisual
{
get
{
if (_presentationSource == null)
_presentationSource = PresentationSource.FromVisual(this);
return _presentationSource.RootVisual;
}
}
private ScrollViewer ParentScrollViewer { get; set; }
[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);
}
And here's the MainWindow.XAML:
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<Grid ScrollViewer.CanContentScroll="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<GroupBox Header="abc" Grid.Row="0" BorderThickness="1" Width="400" Height="600">
<local:DummyWinformControl />
</GroupBox>
<Label Content="Hello world" Grid.Row="1"/>
</Grid>
</ScrollViewer>
Note that in my code, I am inheriting from WindowsFormsHostEx
and not WindowsFormsHost
, because doing so will apply clipping on the Winformcontrols when I am resizing the Windows, so that the label
content will always remain visible.
If I use WindowsFormsHost
then all the spaces will be filled up, but the label
content below will be overlaid. Also not what I want.
The code for WindowsFormsHostEx
is obtained from here.
I'm not too sure what I do wrong with the above code; how can I fix it?
答案1
得分: 0
我找到了解决方案 - 思路是根据以下方式正确缩放DPI。
#region 使用声明
使用 System;
使用 System.Runtime.InteropServices;
使用 System.Windows;
使用 System.Windows.Controls;
使用 System.Windows.Forms.Integration;
使用 System.Windows.Media;
#endregion
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 事件
public 事件 EventHandler LocationChanged;
#endregion
#region 成员
private PresentationSource _presentationSource;
#endregion
#region 属性
私有滚动查看器 ParentScrollViewer { 获取; 设置; }
私有布尔滚动 { 获取; 设置; }
public 布尔调整大小 { 获取; 设置; }
私有视觉 RootVisual
{
获取
{
_presentationSource = PresentationSource.FromVisual(this);
返回 _presentationSource.RootVisual;
}
}
#endregion
#region 构造函数
public WindowsFormsHostEx()
{
PresentationSource.AddSourceChangedHandler(this, SourceChangedEventHandler);
}
#endregion
#region 方法
重写保护无效 OnWindowPositionChanged(矩形 rcBoundingBox)
{
DpiScale dpiScale = VisualTreeHelper.GetDpi(this);
基本.OnWindowPositionChanged(rcBoundingBox);
矩形 newRect = ScaleRectDownFromDPI(rcBoundingBox, dpiScale);
矩形 finalRect;
如果(ParentScrollViewer != null)
{
ParentScrollViewer.ScrollChanged += ParentScrollViewer_ScrollChanged;
ParentScrollViewer.SizeChanged += ParentScrollViewer_SizeChanged;
ParentScrollViewer.Loaded += ParentScrollViewer_Loaded;
}
如果(滚动 || 调整大小)
{
如果(ParentScrollViewer == null)
返回;
MatrixTransform tr = RootVisual.TransformToDescendant(ParentScrollViewer) as MatrixTransform;
var scrollRect = new 矩形(new 大小(ParentScrollViewer.ViewportWidth, ParentScrollViewer.ViewportHeight));
var c = tr.TransformBounds(newRect);
var intersect = 矩形.Intersect(scrollRect, c);
如果(!intersect.IsEmpty)
{
tr = ParentScrollViewer.TransformToDescendant(this) as MatrixTransform;
intersect = tr.TransformBounds(intersect);
finalRect = ScaleRectUpToDPI(intersect, dpiScale);
}
else
finalRect = intersect = new 矩形();
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);
这个.滚动 = false;
这个.调整大小 = false;
}
LocationChanged?.Invoke(this, new 事件参数());
}
私有空白 ParentScrollViewer_Loaded(对象发送器, RoutedEventArgs e)
{
这个.调整大小 = true;
}
私有空白 ParentScrollViewer_SizeChanged(对象发送器, SizeChangedEventArgs e)
{
这个.调整大小 = true;
}
私有空白 ParentScrollViewer_ScrollChanged(对象发送器, ScrollChangedEventArgs e)
{
如果(e.VerticalChange != 0 || e.HorizontalChange != 0 || e.ExtentHeightChange != 0 || e.ExtentWidthChange != 0)
滚动 = true;
}
重写保护无效 Dispose(布尔释放)
{
基本.Dispose(释放);
如果(释放)
{
PresentationSource.RemoveSourceChangedHandler(this, SourceChangedEventHandler);
_presentationSource = null;
}
}
私有空白 SourceChangedEventHandler(对象发送器, SourceChangedEventArgs e)
{
如果(ParentScrollViewer != null)
{
ParentScrollViewer.ScrollChanged -= ParentScrollViewer_ScrollChanged;
ParentScrollViewer.SizeChanged -= ParentScrollViewer_SizeChanged;
ParentScrollViewer.Loaded -= ParentScrollViewer_Loaded;
}
ParentScrollViewer = FindParentScrollViewer();
}
私有滚动查看器 FindParentScrollViewer()
{
依赖对象 vParent = this;
滚动查看器 parentScroll = null;
当(vParent != null)
{
parentScroll = vParent as ScrollViewer;
如果(parentScroll != null)
打破;
vParent = LogicalTreeHelper.GetParent(vParent);
}
返回 parentScroll;
}
私有空白 SetRegion(整数 x1, 整数 y1, 整数 x2, 整数 y2)
{
SetWindowRgn(Handle, CreateRectRgn(x1, y1, x2, y2), true);
}
public static 矩形 ScaleRectDownFromDPI(矩形 _sourceRect, DpiScale dpiScale)
{
双dpiX = dpiScale.DpiScaleX;
双dpiY = dpiScale.DpiScaleY;
返回 新矩形(new 点(_sourceRect.X / dpiX, _sourceRect.Y / dpiY), new System.Windows.Size(_sourceRect.Width / dpiX, _sourceRect.Height / dpiY));
}
public static 矩形 ScaleRectUpToDPI(矩形 _toScaleUp, DpiScale dpiScale)
{
双dpiX = dpiScale.DpiScaleX;
双dpiY = dpiScale.DpiScaleY;
返回 新矩形(new 点(_toScaleUp.X * dpiX, _toScaleUp.Y * dpiY), new System.Windows.Size(_toScaleUp.Width * dpiX, _toScaleUp.Height * dpiY));
}
#endregion
}
上面的代码需要 .Net Framework 4.7 以编译。这是我在相同问题上找到的答案。
英文:
I found the solution-- the idea is that you need to scale properly for DPI, as per below.
#region Using Declarations
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Media;
#endregion
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
}
The above code requires .Net framework 4.7 to compile. It is an answer that I found at the same question.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论