I have WindowsFormHost with a RichTextBox in my WPF form, i have given ScrollViewer for
We are using multiple ScrollViewers and also a ViewBox so none of the mentioned solutions worked for us. So here is our soloution which can also deal with different DPI settings.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms.Integration;
using System.Windows.Media;
using System.Windows.Threading;
namespace XYZ
{
public class ClippingWindowsFormsHost : WindowsFormsHost
{
private readonly DispatcherTimer _updateTimer;
private Rect _bounds;
private PresentationSource _source;
public ClippingWindowsFormsHost()
{
PresentationSource.AddSourceChangedHandler(this, _sourceChangedEventHandler);
_updateTimer = new DispatcherTimer(DispatcherPriority.Render);
_updateTimer.Tick += _updateTick;
_updateTimer.Interval = TimeSpan.FromMilliseconds(100);
}
private void _updateTick(object sender, EventArgs e)
{
_updateTimer.Stop();
if (_source == null)
return;
// Get the Rect of the scrollviewer on screen.
Rect scrollRect = _getScrollRect();
// apply dpi settings
scrollRect = _scaleDpi(scrollRect);
if (scrollRect.Width > 0 && scrollRect.Height > 0) // if the rect is valid...
{
int x1 = (int) Math.Ceiling(scrollRect.X);
int y1 = (int) Math.Ceiling(scrollRect.Y);
int x2 = (int) Math.Ceiling(scrollRect.Right);
int y2 = (int) Math.Ceiling(scrollRect.Bottom);
SetWindowRgn(Handle, CreateRectRgn(x1, y1, x2, y2), true);
}
else
SetWindowRgn(Handle, CreateRectRgn(0, 0, 0, 0), true);
}
private Rect _scaleDpi(Rect rect)
{
if (_source.CompositionTarget != null)
{
Matrix transformToDevice = _source.CompositionTarget.TransformToDevice;
if (!transformToDevice.IsIdentity)
{
Point scaledSize = transformToDevice.Transform(new Point(rect.Width, rect.Height));
rect = new Rect(rect.X, rect.Y, scaledSize.X, scaledSize.Y);
}
}
return rect;
}
[DllImport("User32.dll", SetLastError = true)]
private static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
[DllImport("gdi32.dll")]
private static extern IntPtr CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
protected override void OnWindowPositionChanged(Rect rcBoundingBox)
{
base.OnWindowPositionChanged(rcBoundingBox);
_updateClipping(rcBoundingBox);
}
private void _updateClipping(Rect bounds)
{
if (_source == null || _bounds == bounds)
return;
_bounds = bounds;
// Only update clipping in certain intervals, otherwise splitpanels can create huge cpu load
_updateTimer.Stop();
_updateTimer.Start();
}
private Rect _getScrollRect()
{
ScrollViewer scrollViewer = _getTopScrollViewer();
// Get the screenposition of the scrollviewer
Point topLeft = scrollViewer.PointToScreen(new Point(0, 0));
Point bottomRight = scrollViewer.PointToScreen(new Point(scrollViewer.ViewportWidth, scrollViewer.ViewportHeight));
Rect scrollRect = new Rect(topLeft, bottomRight);
// Get "this" position and use it to offset the scrollrect
// because that is basically the scrolled distance
Point myPosition = PointToScreen(new Point());
scrollRect.Offset(-myPosition.X, -myPosition.Y);
return scrollRect;
}
private ScrollViewer _getTopScrollViewer()
{
DependencyObject parent = this;
ScrollViewer lastViewer = null;
while ((parent = VisualTreeHelper.GetParent(parent)) != null)
{
ScrollViewer viewer = parent as ScrollViewer;
if (viewer != null)
lastViewer = viewer;
}
return lastViewer;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_updateTimer.Stop();
PresentationSource.RemoveSourceChangedHandler(this, _sourceChangedEventHandler);
}
}
private void _sourceChangedEventHandler(object sender, SourceChangedEventArgs e)
{
_updateTimer.Stop();
_source = e.NewSource;
}
}
}