我有WindowsFormHost在我的WPF的形式一个RichTextBox,我已经给ScrollViewer中为WindowsFormHost但其没有工作,WindowsFormHost去的ScrollViewer外...
我的XAML是..
<ScrollViewer Background="DarkOrange" VerticalScrollBarVisibility="Auto" Height="80" MaxHeight="85" Margin="11,243,12,218" Width="756">
<Canvas Height="100" Name="canvas1" Width="auto" >
<WindowsFormsHost ClipToBounds="True" Height="120" Width="715" Margin="10,5,0,0" Name="winHostTEst" Background="Gray">
<wf:RichTextBox BackColor="Cornsilk" Text="RichTextBox" x:Name="richTbTest" BorderStyle="None" Enabled="True" ForeColor="Black" Width="550" Multiline="True" ReadOnly="True" />
</WindowsFormsHost>
</Canvas>
</ScrollViewer>
下面是这个问题的解决方案两个环节,但我不能够实现的..请看看这些链接也并解决我的问题..
链接:
http://blogs.msdn.com/b/ryanvog/archive/2009/01/20/clipping-legacy-content-hosted-inside-a-wpf-scrolling-region.aspx
http://www.mycsharp.de/wbb2/thread.php?threadid=76625
提前致谢..
Answer 1:
最后得到了解决
在您对上述问题的解决方案创建这个类,并采取新的控制类(ScrollViewerWindowsFormsHost),而不是WindowsFormsHost
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms.Integration;
using System.Windows.Media;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
namespace WPFRichTextBox
{
class ScrollViewerWindowsFormsHost: WindowsFormsHost
{
protected override void OnWindowPositionChanged(Rect rcBoundingBox)
{
base.OnWindowPositionChanged(rcBoundingBox);
if (ParentScrollViewer == null)
return;
GeneralTransform tr = ParentScrollViewer.TransformToAncestor(MainWindow);
var scrollRect = new Rect(new Size(ParentScrollViewer.ViewportWidth, ParentScrollViewer.ViewportHeight));
scrollRect = tr.TransformBounds(scrollRect);
var intersect = Rect.Intersect(scrollRect, rcBoundingBox);
if (!intersect.IsEmpty)
{
tr = MainWindow.TransformToDescendant(this);
intersect = tr.TransformBounds(intersect);
}
SetRegion(intersect);
}
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
ParentScrollViewer = null;
var p = Parent as FrameworkElement;
while (p != null)
{
if (p is ScrollViewer)
{
ParentScrollViewer = (ScrollViewer)p;
break;
}
p = p.Parent as FrameworkElement;
}
}
private void SetRegion(Rect intersect)
{
using (var graphics = System.Drawing.Graphics.FromHwnd(Handle))
SetWindowRgn(Handle, (new System.Drawing.Region(ConvertRect(intersect))).GetHrgn(graphics), true);
}
static System.Drawing.RectangleF ConvertRect(Rect r)
{
return new System.Drawing.RectangleF((float)r.X, (float)r.Y, (float)r.Width, (float)r.Height);
}
private Window _mainWindow;
Window MainWindow
{
get
{
if (_mainWindow == null)
_mainWindow = Window.GetWindow(this);
return _mainWindow;
}
}
ScrollViewer ParentScrollViewer { get; set; }
[DllImport("User32.dll", SetLastError = true)]
public static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
}
}
XAML代码:
<Window x:Class="WPFRichTextBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
xmlns:swfh="clr-namespace:WPFRichTextBox"
Title="MainWindow" Height="600" Width="800" Background="LightBlue">
<Grid Loaded="Grid_Loaded">
<ScrollViewer Background="DarkOrange" VerticalScrollBarVisibility="Auto" Height="100" Margin="11,160,12,301" Width="756" Name="scrollViewer1">
<Canvas Height="200" Name="canvas1" Width="auto" >
<swfh:ScrollableWindowsFormsHost ClipToBounds="True" Height="194" Width="715" Margin="10,5,0,0" Background="Gray">
<wf:RichTextBox BackColor="Cornsilk" Text="RichTextBox" x:Name="richTbTest" BorderStyle="None" Enabled="True" ForeColor="Black" Width="550" Multiline="True" ReadOnly="True" />
</swfh:ScrollableWindowsFormsHost>
</Canvas>
</ScrollViewer>
</Grid>
Answer 2:
万一别人有我的优势的情况下,在那里我有一个WinForms用户控件承载一个WPF用户控件,这本身就是一个WinForms窗体中的托管(不要问...)内 - 阿维纳什提供没有解决我的剪辑类的问题。
但对论坛主题的修改版本的地方,它的伎俩 - 所以我想我会张贴在这里为方便。
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);
}
Answer 3:
我发现了马龙的回答是最好的,但是,如果用户有不同的DPI设置并没有在所有的工作。 这是马龙的回答,但解决了规模为DPI。 我还添加了位置改变的事件,因为我需要移动,这是上一前一后WindowsFormsHost内容与WindowsFormsHost在ScrollViewer中顶部的弹出窗口。
#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
}
Answer 4:
我们使用多ScrollViewers
,也是一个ViewBox
所以没有提到的解决方案为我们工作。 因此,这里是我们的soloution也可以处理不同的DPI设置。
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;
}
}
}
Answer 5:
如果您WindowsFormsHost被放置在用户控件里面,然后通过阿维纳什呈现答案可能无法正常工作。 所以,我必须调整ScrollViewerWindowsFormsHost类,如下所示。
public class ScrollViewerWindowsFormsHost : WindowsFormsHost
{
protected override void OnWindowPositionChanged(Rect rcBoundingBox)
{
base.OnWindowPositionChanged(rcBoundingBox);
if (ParentScrollViewer == null)
//return; // Instead, you set the ParentScrollViewr by calling the following method.
SetParentScrollViewer();
GeneralTransform tr = ParentScrollViewer.TransformToAncestor(MainWindow);
var scrollRect = new Rect(new Size(ParentScrollViewer.ViewportWidth, ParentScrollViewer.ViewportHeight));
scrollRect = tr.TransformBounds(scrollRect);
var intersect = Rect.Intersect(scrollRect, rcBoundingBox);
if (!intersect.IsEmpty)
{
tr = MainWindow.TransformToDescendant(this);
intersect = tr.TransformBounds(intersect);
}
SetRegion(intersect);
}
// This is new a new method. This is called from the above method.
private void SetParentScrollViewer()
{
if (ParentScrollViewer is ScrollViewer)
return; // that means its already set;
var p = Parent as FrameworkElement;
while (p != null)
{
if (p is ScrollViewer)
{
ParentScrollViewer = (ScrollViewer)p;
break;
}
p = p.Parent as FrameworkElement;
}
}
// Just comment out this method, you dont need this any more. You set the parent Scroll Viewer by calling SetParentScrollViewer Method.
//protected override void OnVisualParentChanged(DependencyObject oldParent)
//{
// base.OnVisualParentChanged(oldParent);
// ParentScrollViewer = null;
// var p = Parent as FrameworkElement;
// while (p != null)
// {
// if (p is ScrollViewer)
// {
// ParentScrollViewer = (ScrollViewer)p;
// break;
// }
// p = p.Parent as FrameworkElement;
// }
//}
private void SetRegion(Rect intersect)
{
using (var graphics = System.Drawing.Graphics.FromHwnd(Handle))
SetWindowRgn(Handle, (new System.Drawing.Region(ConvertRect(intersect))).GetHrgn(graphics), true);
}
static System.Drawing.RectangleF ConvertRect(Rect r)
{
return new System.Drawing.RectangleF((float)r.X, (float)r.Y, (float)r.Width, (float)r.Height);
}
private Window _mainWindow;
Window MainWindow
{
get
{
if (_mainWindow == null)
_mainWindow = Window.GetWindow(this);
return _mainWindow;
}
}
ScrollViewer ParentScrollViewer { get; set; }
[DllImport("User32.dll", SetLastError = true)]
public static extern int SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool bRedraw);
}
而已。 每一件事情保持不变。
Answer 6:
这是因为ScrollViewer中不知道它有滚动。 如果你的鼠标是RichTextBox的,它会拦截所有按键。 你也可以继承RichTextBox的(即的WndProc),并监听事件鼠标滚轮,然后用他们的RaiseEvent送的ScrollViewer。 不要忘了,上的WndProc一个单独的线程比WPF,所以你需要做的是这样运行的:
情况下WM_MOUSEWHEEL:Dispatcher.BeginInvoke(新的行动(()=> VisualHelper.FindParent(RichTextBox中).RaiseEvent(..鼠标滚轮事件与正确的参数..));
文章来源: ScrollViewer is not working in WPF WindowsFormHost