【问题标题】:Get visible controls by position按位置获取可见控件
【发布时间】:2009-11-16 12:46:36
【问题描述】:

我有一个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;

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

【问题讨论】:

    标签: c# wpf flowdocument flowdocumentscrollviewer


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多