按位置获取可见控件

时间:2009-11-16 12:46:37

标签: c# wpf flowdocument flowdocumentscrollviewer

我有一个内容很多的FlowDocument。我需要获取当前在可见区域的控件。

使用以下代码,我可以获得当前滚动位置。

DependencyObject obj = FlowDocumentScrollViewerCtrl;

do
{
    if (VisualTreeHelper.GetChildrenCount(obj) > 0)
    {
        obj = VisualTreeHelper.GetChild(obj as Visual, 0);
    }
}
while (!(obj is ScrollViewer));

ScrollViewer sv = obj as ScrollViewer;

如何在可见区域内获取控件?

2 个答案:

答案 0 :(得分:0)

一种方法是使用VisualTreeHelper.GetChildrenCountVisualTreeHelper.GetChild()以递归方式下降Visual树,并使用以下过程检查每个Visual:

  1. 丢弃任何对您的代码不感兴趣的视觉效果(例如,您可能只关心控件)
  2. 使用new Rect(0, 0, visual.ActualWidth, visual.ActualHeight)获取每个Visual的边界框。这将为您提供Visual坐标系中的边界框。
  3. 使用visual.TransformToAncestor(viewer)返回的转换将边界框转换为查看器的坐标系。
  4. 检查变换后的视觉边界框是否与观察者的边界框相交。通过获取可视边界框角的最小和最大X和Y并且一次只比较一个轴,可以进行粗略检查。这比完整的矩形交叉更容易,并且应该用于大多数目的。
  5. 这将告诉您可见区域中的所有视觉效果。如果您要映射到FrameworkContentElement,例如<Paragraph>,请查看您在树木漫步中遇到的ContentPresenter的内容属性。

答案 1 :(得分:0)

感谢Ray的回答。我昨天在某些方面遵循了你的提示,这就是我的问题的工作代码:

public class FrameworkElementInfo
{
    Point _position             = new Point();
    FrameworkElement _element   = null;

    public Point Position
    {
        get { return _position; }
        set { _position = value; }
    }

    public FrameworkElement FrameworkElement
    {
        get { return _element; }
        set { _element = value; }
    }
}

public class ScrollViewPositionManager
{
    ScrollViewer _scrollViewer              = null;
    List<FrameworkElementInfo> _elements    = new List<FrameworkElementInfo>();
    double _zoom                            = 100.0;

    public ScrollViewPositionManager(ScrollViewer scrollViewer, double zoom)
    {
        _scrollViewer = scrollViewer;
        _zoom = zoom;
    }

    public void RegisterElement(FrameworkElement element, Boolean registerOnly)
    {
        FrameworkElementInfo info = new FrameworkElementInfo();

        if (!registerOnly)  info.Position = CalculatePosition(element);
        info.FrameworkElement   = element;

        _elements.Add(info);
    }

    public void RecalculatePositions()
    {
        int Counter = 0;

        foreach(FrameworkElementInfo info in _elements)
        {
            Counter += 1;
            info.Position = CalculatePosition(info.FrameworkElement);
        }
    }

    public List<FrameworkElement> GetElementsInViewPort()
    {
        List<FrameworkElement> elements = new List<FrameworkElement>();

        double verticalOffsetHigh = _scrollViewer.ViewportHeight + _scrollViewer.VerticalOffset;

        foreach (FrameworkElementInfo info in _elements)
        {
            Point point = info.Position;

            if (point.Y >= _scrollViewer.VerticalOffset &&
                point.Y <= verticalOffsetHigh)
            {
                elements.Add(info.FrameworkElement);
            }
        }

        return elements;
    }

    private Point CalculatePosition(FrameworkElement element)
    {
        GeneralTransform elementTransform = element.TransformToAncestor(_scrollViewer);
        Point elementPoint = elementTransform.Transform(new Point(0, 0));
        Point transformedPoint = new Point(elementPoint.X, elementPoint.Y);

        transformedPoint = GetZoomedPoint(elementPoint, _zoom, _scrollViewer.HorizontalOffset, _scrollViewer.VerticalOffset);

        return transformedPoint;
    }

    static public Point GetZoomedPoint(Point unzoomedPoint, double zoom, double offsetX, double offsetY)
    {
        Point zoomedPoint = new Point();

        double zoomFactor = 100.0 / zoom;

        zoomedPoint.X = offsetX + unzoomedPoint.X * zoomFactor;
        zoomedPoint.Y = offsetY + unzoomedPoint.Y * zoomFactor;

        return zoomedPoint;
    }

    public int ElementCount
    {
        get { return _elements.Count; }
    }

    public FrameworkElement GetFirstElement()
    {
        FrameworkElement firstElement = null;

        if(_elements.Count > 0) firstElement = _elements[0].FrameworkElement;

        return firstElement;
    }

    public FrameworkElement GetLastElement()
    {
        FrameworkElement lastElement = null;

        if (_elements.Count > 0) lastElement = _elements[_elements.Count-1].FrameworkElement;

        return lastElement;
    }

    public FrameworkElement GetNextElement(FrameworkElement element)
    {
        FrameworkElement nextElement = null;
        int index = GetElementIndex(element);

        if(index != -1 && index != _elements.Count -1)
        {           
            nextElement = _elements[index + 1].FrameworkElement;
        }

        return nextElement;
    }

    public FrameworkElement GetPreviousElement(FrameworkElement element)
    {
        FrameworkElement previousElement = null;
        int index = GetElementIndex(element);

        if (index > 1)
        {
            previousElement = _elements[index - 1].FrameworkElement;
        }

        return previousElement;
    }

    public int GetElementIndex(FrameworkElement element)
    {
        return _elements.FindIndex(
                            delegate(FrameworkElementInfo currentElement)
                            {
                                if(currentElement.FrameworkElement == element) return true;
                                return false;
                            }
        );
    }
}

我对感兴趣的元素使用注册函数,并且仅对它们起作用。我认为只需要一个FlowDocument缩放。此代码应适用于使用ScrollViewer的每个控件。 如果这是一个实用的解决方案,如果有人可以发表评论,我将不胜感激。