对本例的一些说明,例如下面的一个例子,首先是一个记载数据信息的类,
namespace WpfApplication33
{
public class GreekGod
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private string description;
public string Description
{
get { return description; }
set { description = value; }
}
private string romanName;
public string RomanName
{
get { return romanName; }
set { romanName = value; }
}
public GreekGod(string name, string description, string romanName)
{
this.name = name;
this.description = description;
this.romanName = romanName;
}
}
}
然后用一个ObservableCollection集合这些数据,
namespace WpfApplication33
{
public class GreekGods : ObservableCollection<GreekGod>
{
public GreekGods()
{
this.Add(new GreekGod("Aphrodite", "Goddess of love, beauty and fertility", "Venus"));
this.Add(new GreekGod("Apollo", "God of prophesy, music and healing", "Apollo"));
this.Add(new GreekGod("Ares", "God of war", "Mars"));
this.Add(new GreekGod("Artemis", "Virgin goddess of the hunt", "Diana"));
this.Add(new GreekGod("Athena", "Goddess of crafts and the domestic arts", "Athena"));
this.Add(new GreekGod("Demeter", "Goddess of agriculture", "Ceres"));
this.Add(new GreekGod("Dionysus", "God of wine", "Bacchus"));
this.Add(new GreekGod("Hephaestus", "God of fire and crafts", "Vulcan"));
this.Add(new GreekGod("Hera", "Goddess of marriage", "Juno"));
this.Add(new GreekGod("Hermes", "Messenger of the Gods", "Mercury"));
this.Add(new GreekGod("Poseidon", "God of the sea, earthquakes and horses", "Neptune"));
this.Add(new GreekGod("Zeus", "Supreme God of the Olympians", "Jupiter"));
}
}
}
然后再xaml中把这些数据绑定到ListBox上,
<Window x:Class="WpfApplication33.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
xmlns:local="clr-namespace:WpfApplication33">
<Window.Resources>
<local:GreekGods x:Key="greekGods"/>
<DataTemplate x:Key="itemTemplate">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</Window.Resources>
<Border BorderBrush="RoyalBlue" BorderThickness="2" Margin="25" Padding="5">
<StackPanel>
<ListBox ItemsSource="{StaticResource greekGods}" ItemTemplate="{StaticResource itemTemplate}"
IsSynchronizedWithCurrentItem="true" Width="200" Name="listBox"/>
<Button Click="Button_Click" Margin="5">Get ListBoxItem</Button>
</StackPanel>
</Border>
</Window>
在代码中写的button click事件如下,
private void Button_Click(object sender, RoutedEventArgs e)
{
var obj = this.listBox.Items[0];
DependencyObject dobj = this.listBox.ItemContainerGenerator.ContainerFromIndex(0);
}
在debug状态下我们查看obj和dobj的值和类型,如下:
((ListBoxItem)dobj).Content.GetType() {Name = "GreekGod" FullName = "WpfApplication33.GreekGod"}
obj.GetType() {Name = "GreekGod" FullName = "WpfApplication33.GreekGod"}
可以看到,Items[0]是GreekGod,而Items[0]的container是ListBoxItem类型,dobj的Content也是GreekGod。
对于本文例子中的ItemsControl,当鼠标点击一个图片的时候,是这样确定点击的对象的,
首先,注册了ItemsControl的PMLBD事件:
private void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{ this.sourceItemsControl = (ItemsControl)sender;
Visual visual = e.OriginalSource as Visual;
this.topWindow = (Window)Utilities.FindAncestor(typeof(Window), this.sourceItemsControl);
this.initialMousePosition = e.GetPosition(this.topWindow);
this.sourceItemContainer = Utilities.GetItemContainer(this.sourceItemsControl, visual);
if (this.sourceItemContainer != null)
{
this.draggedData = this.sourceItemContainer.DataContext;
}
}
注意这里e.Source其实就是这里的sender,对应的是逻辑树中触发事件的元素,而e.OriginalSource对应的是视觉树中的触发事件的元素,所以后面会用VisualTreeHelper操作它。
其中GetItemContainer方法如下:
// In order to make sure it works with any control that derives from ItemsControl, this method makes no assumption about the type of that container.(it will get a ListBoxItem if it's a ListBox, a ListViewItem if it's a ListView)
这里的意思是说因为我们不知道这个容器的类型,所以不知道对于它的每一个Item是用什么container来装的(例如ListBox的每一个Item的容器是ListBoxItem等等)。实际上这里的container是一个ContentPresenter,它的Content(这里也是DataContext)就是程序中的Picture数据。这里我们通过第一个Item得到container的类型,然后再通过在视觉树中找到用来呈现bottomMostVisual的container,它要满足也是这个类型的就可以了。
public static FrameworkElement GetItemContainer(ItemsControl itemsControl, Visual bottomMostVisual)
{
FrameworkElement itemContainer = null;
if (itemsControl != null && bottomMostVisual != null && itemsControl.Items.Count >= 1)
{
var firstContainer = itemsControl.ItemContainerGenerator.ContainerFromIndex(0);
if (firstContainer != null)
{
Type containerType = firstContainer.GetType();
itemContainer = FindAncestor(containerType, bottomMostVisual);
// Make sure that the container found belongs to the items control passed as a parameter.
if (itemContainer != null && itemContainer.DataContext != null)
{
FrameworkElement itemContainerVerify = itemsControl.ItemContainerGenerator.ContainerFromItem(itemContainer.DataContext) as FrameworkElement;
if (itemContainer != itemContainerVerify)
{
itemContainer = null;
}
}
}
}
return itemContainer;
}
下面是根据类型和自己找container的方法,
public static FrameworkElement FindAncestor(Type ancestorType, Visual visual)
{
while (visual != null && !ancestorType.IsInstanceOfType(visual))
{
visual = (Visual)VisualTreeHelper.GetParent(visual);
}
return visual as FrameworkElement;
}
namespace WpfApplication33
{
public class GreekGod
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private string description;
public string Description
{
get { return description; }
set { description = value; }
}
private string romanName;
public string RomanName
{
get { return romanName; }
set { romanName = value; }
}
public GreekGod(string name, string description, string romanName)
{
this.name = name;
this.description = description;
this.romanName = romanName;
}
}
}
然后用一个ObservableCollection集合这些数据,
namespace WpfApplication33
{
public class GreekGods : ObservableCollection<GreekGod>
{
public GreekGods()
{
this.Add(new GreekGod("Aphrodite", "Goddess of love, beauty and fertility", "Venus"));
this.Add(new GreekGod("Apollo", "God of prophesy, music and healing", "Apollo"));
this.Add(new GreekGod("Ares", "God of war", "Mars"));
this.Add(new GreekGod("Artemis", "Virgin goddess of the hunt", "Diana"));
this.Add(new GreekGod("Athena", "Goddess of crafts and the domestic arts", "Athena"));
this.Add(new GreekGod("Demeter", "Goddess of agriculture", "Ceres"));
this.Add(new GreekGod("Dionysus", "God of wine", "Bacchus"));
this.Add(new GreekGod("Hephaestus", "God of fire and crafts", "Vulcan"));
this.Add(new GreekGod("Hera", "Goddess of marriage", "Juno"));
this.Add(new GreekGod("Hermes", "Messenger of the Gods", "Mercury"));
this.Add(new GreekGod("Poseidon", "God of the sea, earthquakes and horses", "Neptune"));
this.Add(new GreekGod("Zeus", "Supreme God of the Olympians", "Jupiter"));
}
}
}
然后再xaml中把这些数据绑定到ListBox上,
<Window x:Class="WpfApplication33.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
xmlns:local="clr-namespace:WpfApplication33">
<Window.Resources>
<local:GreekGods x:Key="greekGods"/>
<DataTemplate x:Key="itemTemplate">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</Window.Resources>
<Border BorderBrush="RoyalBlue" BorderThickness="2" Margin="25" Padding="5">
<StackPanel>
<ListBox ItemsSource="{StaticResource greekGods}" ItemTemplate="{StaticResource itemTemplate}"
IsSynchronizedWithCurrentItem="true" Width="200" Name="listBox"/>
<Button Click="Button_Click" Margin="5">Get ListBoxItem</Button>
</StackPanel>
</Border>
</Window>
在代码中写的button click事件如下,
private void Button_Click(object sender, RoutedEventArgs e)
{
var obj = this.listBox.Items[0];
DependencyObject dobj = this.listBox.ItemContainerGenerator.ContainerFromIndex(0);
}
在debug状态下我们查看obj和dobj的值和类型,如下:
((ListBoxItem)dobj).Content.GetType() {Name = "GreekGod" FullName = "WpfApplication33.GreekGod"}
obj.GetType() {Name = "GreekGod" FullName = "WpfApplication33.GreekGod"}
可以看到,Items[0]是GreekGod,而Items[0]的container是ListBoxItem类型,dobj的Content也是GreekGod。
对于本文例子中的ItemsControl,当鼠标点击一个图片的时候,是这样确定点击的对象的,
首先,注册了ItemsControl的PMLBD事件:
private void DragSource_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{ this.sourceItemsControl = (ItemsControl)sender;
Visual visual = e.OriginalSource as Visual;
this.topWindow = (Window)Utilities.FindAncestor(typeof(Window), this.sourceItemsControl);
this.initialMousePosition = e.GetPosition(this.topWindow);
this.sourceItemContainer = Utilities.GetItemContainer(this.sourceItemsControl, visual);
if (this.sourceItemContainer != null)
{
this.draggedData = this.sourceItemContainer.DataContext;
}
}
注意这里e.Source其实就是这里的sender,对应的是逻辑树中触发事件的元素,而e.OriginalSource对应的是视觉树中的触发事件的元素,所以后面会用VisualTreeHelper操作它。
其中GetItemContainer方法如下:
// In order to make sure it works with any control that derives from ItemsControl, this method makes no assumption about the type of that container.(it will get a ListBoxItem if it's a ListBox, a ListViewItem if it's a ListView)
这里的意思是说因为我们不知道这个容器的类型,所以不知道对于它的每一个Item是用什么container来装的(例如ListBox的每一个Item的容器是ListBoxItem等等)。实际上这里的container是一个ContentPresenter,它的Content(这里也是DataContext)就是程序中的Picture数据。这里我们通过第一个Item得到container的类型,然后再通过在视觉树中找到用来呈现bottomMostVisual的container,它要满足也是这个类型的就可以了。
public static FrameworkElement GetItemContainer(ItemsControl itemsControl, Visual bottomMostVisual)
{
FrameworkElement itemContainer = null;
if (itemsControl != null && bottomMostVisual != null && itemsControl.Items.Count >= 1)
{
var firstContainer = itemsControl.ItemContainerGenerator.ContainerFromIndex(0);
if (firstContainer != null)
{
Type containerType = firstContainer.GetType();
itemContainer = FindAncestor(containerType, bottomMostVisual);
// Make sure that the container found belongs to the items control passed as a parameter.
if (itemContainer != null && itemContainer.DataContext != null)
{
FrameworkElement itemContainerVerify = itemsControl.ItemContainerGenerator.ContainerFromItem(itemContainer.DataContext) as FrameworkElement;
if (itemContainer != itemContainerVerify)
{
itemContainer = null;
}
}
}
}
return itemContainer;
}
下面是根据类型和自己找container的方法,
public static FrameworkElement FindAncestor(Type ancestorType, Visual visual)
{
while (visual != null && !ancestorType.IsInstanceOfType(visual))
{
visual = (Visual)VisualTreeHelper.GetParent(visual);
}
return visual as FrameworkElement;
}