我想出了第二个问题的答案。我需要一个支持滚动的 ItemsControl,并且我需要将项目放在网格上而不是默认的 StackPanel 上。为了满足这两个要求,我使用了 ControlTemplate:
<!--In the resources...-->
<ControlTemplate x:Key="GraphTemplate" TargetType="ItemsControl">
<ScrollViewer Name="ScrollViewer"
Padding="{TemplateBinding Padding}"
HorizontalScrollBarVisibility="Auto">
...
<Grid Name="Panel" IsItemsHost="True"
Background="{TemplateBinding ItemsControl.Background}"/>
...
</ScrollViewer>
</ControlTemplate>
<!--Later...-->
<ItemsControl x:Name="_itemsControl"
ItemsSource="{Binding Items}"
Template="{StaticResource GraphTemplate}"
Background="LightYellow"/>
为了获得具有有意义的鼠标坐标(即可滚动空间中的坐标)的鼠标事件,有必要使用一个奇怪的咒语来获得对网格的引用:
Grid grid = (Grid)_itemsControl.Template.FindName("Panel", _itemsControl);
然后将事件处理程序附加到网格,并在鼠标事件处理程序内,使用网格获取鼠标坐标
Point p = e.GetPosition((IInputElement)sender);
为了在整个表面上获取鼠标事件,控件(实际上是网格)必须有背景,所以我在上面设置了 Background="LightYellow",它通过 ControlTemplate 中的绑定传播到网格。