Получить видимые элементы управления по позиции - PullRequest
1 голос
/ 16 ноября 2009

У меня есть 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 голосов
/ 17 ноября 2009

Спасибо Рэй за ваш ответ. Вчера я следовал вашим советам, и вот рабочий код моей проблемы:

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. Буду признателен, если кто-нибудь прокомментирует это, если это практическое решение.

0 голосов
/ 16 ноября 2009

Один из способов - это рекурсивно спуститься в дерево визуалов, используя VisualTreeHelper.GetChildrenCount и VisualTreeHelper.GetChild(), и проверить каждый визуал, используя следующую процедуру:

  1. Удалите все визуальные элементы, которые не интересны вашему коду (например, вы можете заботиться только о элементах управления)
  2. Получите ограничивающую рамку каждого Visual, используя new Rect(0, 0, visual.ActualWidth, visual.ActualHeight). Это даст вам ограничительную рамку в системе координат Visual.
  3. Используйте преобразование, возвращаемое visual.TransformToAncestor(viewer), чтобы преобразовать ограничивающую рамку в систему координат зрителя.
  4. Проверьте, пересекает ли ограничивающий прямоугольник трансформируемого визуала ограничивающий прямоугольник зрителя. Грубую проверку можно выполнить, взяв минимальные и максимальные значения X и Y углов визуальной ограничительной рамки и сравнив по одной оси за раз. Это проще, чем полное прямоугольное пересечение, и должно служить для большинства целей.

Это скажет вам все визуальные эффекты в видимой области. Если вы хотите отобразить на FrameworkContentElement s, такие как <Paragraph>, просто проверьте свойство Content для ContentPresenter s, с которыми вы столкнулись во время обхода дерева.

...