在上一篇所介绍的方式中,主要的技术点其实就是那个作为ItemsPanel的自定义Panel。然而这种实现方式有两个主要缺点。
1. 没有了Virtualizing的效果。虽然没有不可见项。
2. 不便于添加动画效果。
这里将向大家介绍另一种实现方式。就是用Blend 3中非常火爆的Behavior来实现,并且可以很容易地添加上动画效果。
这个Behavior原理很简单,就是动态地计算ListBox里Item的高度。这就要求:
1. 不能为ListBox的Item指定各不相同的高度。
2. 要为每个Item指定一个默认的收缩时的高度。
在Rooijakkers的博客上介绍了类似的用高度的方法。不过上面的方法没有充分利用WPF的特性,写了一些不必要的逻辑,比如控件Expander的IsExpanded属性。而且没有动画的支持。
这里使用Behavior简单步骤就是,添加一个用于ListBox的自定义Behavior,然后在ListBox的SelectionChanged事件中去设置每个Item的高度。用Storyboard就可以很容易地实现动画效果。
这个Behavior有两个自定义属性。一个是DefaultHeight,一个是AnimationDuration。顾名思义,不解释了。核心代码如下所示。
Core Logic
private void OnAssociatedObjectSelectionChanged(object sender, SelectionChangedEventArgs e)
{
double selectedItemFinalHeight = AssociatedObject.ActualHeight;
Storyboard storyBoard = new Storyboard();
for (int i = 0; i < AssociatedObject.Items.Count; i++)
{
ListBoxItem item = AssociatedObject.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
if (!item.IsSelected)
{
selectedItemFinalHeight -= DefaultHeight;
DoubleAnimation heightAnimation = new DoubleAnimation()
{
To = DefaultHeight,
Duration = new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration))
};
Storyboard.SetTarget(heightAnimation, item);
Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(FrameworkElement.HeightProperty));
storyBoard.Children.Add(heightAnimation);
}
}
// The Padding of the ListBox.
selectedItemFinalHeight -= 4;
if (AssociatedObject.SelectedIndex >= 0)
{
ListBoxItem selectedItem = AssociatedObject.ItemContainerGenerator.ContainerFromIndex(AssociatedObject.SelectedIndex) as ListBoxItem;
DoubleAnimation fillheightAnimation = new DoubleAnimation()
{
To = selectedItemFinalHeight,
Duration = new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration))
};
Storyboard.SetTarget(fillheightAnimation, selectedItem);
Storyboard.SetTargetProperty(fillheightAnimation, new PropertyPath(FrameworkElement.HeightProperty));
storyBoard.Children.Add(fillheightAnimation);
}
storyBoard.Begin(AssociatedObject);
}
{
double selectedItemFinalHeight = AssociatedObject.ActualHeight;
Storyboard storyBoard = new Storyboard();
for (int i = 0; i < AssociatedObject.Items.Count; i++)
{
ListBoxItem item = AssociatedObject.ItemContainerGenerator.ContainerFromIndex(i) as ListBoxItem;
if (!item.IsSelected)
{
selectedItemFinalHeight -= DefaultHeight;
DoubleAnimation heightAnimation = new DoubleAnimation()
{
To = DefaultHeight,
Duration = new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration))
};
Storyboard.SetTarget(heightAnimation, item);
Storyboard.SetTargetProperty(heightAnimation, new PropertyPath(FrameworkElement.HeightProperty));
storyBoard.Children.Add(heightAnimation);
}
}
// The Padding of the ListBox.
selectedItemFinalHeight -= 4;
if (AssociatedObject.SelectedIndex >= 0)
{
ListBoxItem selectedItem = AssociatedObject.ItemContainerGenerator.ContainerFromIndex(AssociatedObject.SelectedIndex) as ListBoxItem;
DoubleAnimation fillheightAnimation = new DoubleAnimation()
{
To = selectedItemFinalHeight,
Duration = new Duration(new TimeSpan(0, 0, 0, 0, AnimationDuration))
};
Storyboard.SetTarget(fillheightAnimation, selectedItem);
Storyboard.SetTargetProperty(fillheightAnimation, new PropertyPath(FrameworkElement.HeightProperty));
storyBoard.Children.Add(fillheightAnimation);
}
storyBoard.Begin(AssociatedObject);
}
这个示例看截图是和上一篇中介绍的是一样。要看动画效果还是要自己试一下的。在这个代码里,也包含了上次的示例。