簡體   English   中英

WPF 鼠標事件路由問題

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM