简体   繁体   English

嵌入 WPF ScrollViewer 时,WindowsFormsHost 控件被过度剪辑

[英]WindowsFormsHost control is being overclipped when embedded inside a WPF 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.我有一个WindowsFormsHost嵌套在 WPF ScrollViewer 中,出于某种原因,在应用剪辑后,似乎WIndowsFormsHost没有填满所有可用空间,即:控件已被过度剪辑。

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:这是 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.请注意,在我的代码中,我是从WindowsFormsHostEx而不是WindowsFormsHost继承的,因为这样做会在我调整 Windows 大小时在 Winformcontrols 上应用剪辑,因此label内容将始终保持可见。

If I use WindowsFormsHost then all the spaces will be filled up, but the label content below will be overlaid.如果我使用WindowsFormsHost那么所有的空间都会被填满,但是下面的label内容会被覆盖。 Also not what I want.也不是我想要的。

The code for WindowsFormsHostEx is obtained from here . WindowsFormsHostEx的代码是从这里获得的。

I'm not too sure what I do wrong with the above code;我不太确定我对上面的代码做错了什么; how can I fix it?我该如何解决?

I found the solution-- the idea is that you need to scale properly for DPI, as per below.我找到了解决方案——这个想法是你需要为 DPI 正确缩放,如下所示。

#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.上面的代码需要 .Net framework 4.7 才能编译。 It is an answer that I found at the same question.这是我在同一问题中找到的答案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM