【问题标题】: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.GetChildrenCount 和 VisualTreeHelper.GetChild() 递归地降低 Visual 树,并使用以下过程检查每个 Visual:
- 丢弃任何对您的代码不感兴趣的视觉对象(例如,您可能只关心控件)
- 使用
new Rect(0, 0, visual.ActualWidth, visual.ActualHeight) 获取每个视觉对象的边界框。这将为您提供 Visual 坐标系中的边界框。
- 使用
visual.TransformToAncestor(viewer) 返回的变换将边界框变换到查看器的坐标系中。
- 检查转换后的视觉对象的边界框是否与查看者的边界框相交。可以通过获取视觉边界框角的最小和最大 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 的每个控件。
如果这是一个实用的解决方案,如果有人可以对此发表评论,我将不胜感激。