[英]Problem with WPF mouse event routing
構建一個本質上是滾動樹視圖的自定義控件。 稍后,它將不得不以某種方式監控它的項目並與之交互。 (例如,它必須找到與某種名稱路徑相對應的元素)。 由於所有項目都具有相同的大小,因此這也是虛擬化的主要情況;)
不足為奇的是,這些元素應該支持基本的交互,比如響應(視覺上)讓鼠標懸停在它們上面。
實際的樹 class 有一個 Items 屬性(它是一個 IList)和兩個滾動條作為它的可視子項並覆蓋測量、排列和渲染(一些 - 希望 - 不相關的功能被遺漏):
public sealed partial class SyncTree : Control
{
ScrollBar verticalScrollbar = new ScrollBar();
ScrollBar horizontalScrollbar = new ScrollBar();
private VisualCollection visualchildren;
protected override int VisualChildrenCount
{
get
{
return visualchildren.Count + Items.Count;
}
}
protected override Visual GetVisualChild(int index)
{
if (index < visualchildren.Count)
return visualchildren[index];
else
return Items[index - visualchildren.Count];
}
private Size MeasuredSize = new Size(0, 0);
protected override Size MeasureOverride(Size availableSize)
{
UpdateMeasuredSize();
return availableSize;
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
ArrangeItems(ArrangeScrollbars(arrangeBounds));
return arrangeBounds;
}
private void ArrangeItems(Size arrangeBounds)
{
var y = Padding.Top - verticalScrollbar.Value;
foreach (var item in Items)
{
item.Arrange(new Rect(Padding.Left - horizontalScrollbar.Value, y, item.Width, item.Height));
y += item.Height;
y += RootNodeSpacing;
}
}
protected override void OnRender(DrawingContext context)
{
//kill the ugly background-colored patch between the scrollbars
if (verticalScrollbar.IsVisible && horizontalScrollbar.IsVisible) context.DrawRectangle(verticalScrollbar.Background, null, new Rect(RenderSize.Width - verticalScrollbar.Width, RenderSize.Height - horizontalScrollbar.Height, verticalScrollbar.Width, horizontalScrollbar.Height));
context.DrawRectangle(Background, null, new Rect(RenderSize));
}
}
這些項目有自己的類型:
public class SyncTreeItem : Control
{
protected override Size MeasureOverride(Size constraint)
{
var size = new Size(FormattedText.Width + FormattedText.Height + 5, FormattedText.Height);
if (IsExpanded)
{
foreach (var item in Items)
{
item.Measure(new Size(constraint.Width - Indent, constraint.Height - size.Height));
size.Height += item.DesiredSize.Height;
size.Width = Math.Max(size.Width, item.DesiredSize.Width + Indent);
}
}
Height = size.Height;
Width = size.Width;
return size;
}
protected override Size ArrangeOverride(Size arrangeBounds)
{
if (IsExpanded)
{
var y = FormattedText.Height;
foreach (var item in Items)
{
item.Arrange(new Rect(Indent, y, item.Width, item.Height));
y += item.Height;
}
return new Size(arrangeBounds.Width, y);
}
else return new Size(arrangeBounds.Width, FormattedText.Height);
}
protected override void OnRender(DrawingContext context)
{
context.DrawRectangle(Brushes.Transparent, null, new Rect(new Point(0, 0), RenderSize));
context.PushClip(new RectangleGeometry(new Rect(RenderSize)));
var ft = FormattedText;
context.DrawImage(Icon, new Rect(0, 0, ft.Height, ft.Height));
context.DrawText(ft, new Point(ft.Height + 5, 0));
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
Console.WriteLine("got mousedown!");
}
}
我的主要問題是子元素似乎根本沒有獲得 mousedown 事件(或其他鼠標事件)。 也許我沒有得到整個 Visual Tree 和 Routed Event 的東西 - 但當我單擊該項目時我從未看到控制台 output。 在樹上,我可以獲得 previewmousdown 和 mousedown 事件......
此外,內部控件不會剪輯到內部顯示區域(嗯,為什么要剪輯),我可以通過在排列滾動條和覆蓋 GetLayoutClip 之前排列它們來解決這個問題。 或者,我可以嘗試讓孩子們知道他們的水平偏移,並簡單地將他們排列在一個不適合的矩形中。 但是,我確實想知道是否有更好的方法:)
確保為每個子視覺對象調用AddVisualChild
。 僅僅覆蓋 VisualChildrenCount 和 GetVisualChild 是不夠的; 框架也需要了解這種關系。 (也許你在“不相關的功能”中這樣做。)
虛擬化真的很難實現,與其嘗試從頭開始構建它,不如嘗試使用VirtualizingPanel的功能。 看起來您正在垂直堆疊元素並在每個深度縮進子元素。 您可以使用邏輯將分層數據展平為單個列表,該列表存儲原始節點和嵌套級別。 然后,您可以將該列表綁定到帶有 VirtualizingStackPanel 的 ItemsControl,並根據嵌套級別設置每個容器的左邊距。
如果您最終實現了自己的虛擬化控件,您可能希望直接從VirtualizingPanel繼承。 這是一系列博客文章,描述了實現虛擬化面板所需要做的一切: 鏈接
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.