1

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;

如何获得可见区域内的控件?

4

2 回答 2

0

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

  1. 丢弃任何对您的代码不感兴趣的视觉效果(例如,您可能只关心控件)
  2. 使用 获取每个 Visual 的边界框new Rect(0, 0, visual.ActualWidth, visual.ActualHeight)。这将为您提供 Visual 坐标系中的边界框。
  3. 使用返回的visual.TransformToAncestor(viewer)变换将边界框变换到查看器的坐标系中。
  4. 检查转换后的视觉对象的边界框是否与查看者的边界框相交。可以通过获取视觉边界框角的最小和最大 X 和 Y 并一次只比较一个轴来进行粗略检查。这比全矩形相交更容易,并且应该适用于大多数目的。

这将告诉您可见区域中的所有视觉效果。如果您想映射到FrameworkContentElements 等<Paragraph>,只需检查ContentPresenter您在 tree walk 中遇到的 s 的 Content 属性。

于 2009-11-16T17:51:55.033 回答
0

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

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 的每个控件。如果这是一个实用的解决方案,如果有人对此发表评论,我将不胜感激。

于 2009-11-17T12:07:02.970 回答