WindowsFormsHost控件在嵌套在WPF ScrollViewer内部时被过度裁剪。

huangapple go评论71阅读模式
英文:

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.

WindowsFormsHost控件在嵌套在WPF ScrollViewer内部时被过度裁剪。

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;
}
}
/// &lt;summary&gt;
///  https://stackoverflow.com/a/18084481
/// &lt;/summary&gt;
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(&quot;User32.dll&quot;, SetLastError = true)]
static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
[DllImport(&quot;gdi32.dll&quot;)]
static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
}

And here's the MainWindow.XAML:

        &lt;ScrollViewer VerticalScrollBarVisibility=&quot;Auto&quot; HorizontalScrollBarVisibility=&quot;Auto&quot;&gt;
&lt;Grid ScrollViewer.CanContentScroll=&quot;True&quot;&gt;
&lt;Grid.RowDefinitions&gt;
&lt;RowDefinition Height=&quot;Auto&quot; /&gt;
&lt;RowDefinition Height=&quot;Auto&quot; /&gt;
&lt;/Grid.RowDefinitions&gt;
&lt;GroupBox Header=&quot;abc&quot; Grid.Row=&quot;0&quot; BorderThickness=&quot;1&quot;  Width=&quot;400&quot; Height=&quot;600&quot;&gt;
&lt;local:DummyWinformControl /&gt;
&lt;/GroupBox&gt;
&lt;Label Content=&quot;Hello world&quot; Grid.Row=&quot;1&quot;/&gt;
&lt;/Grid&gt;
&lt;/ScrollViewer&gt;

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(&quot;User32.dll&quot;, SetLastError = true)]
static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
[DllImport(&quot;gdi32.dll&quot;)]
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.

huangapple
  • 本文由 发表于 2020年1月6日 14:47:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/59607802.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定