在Silverlight / WPF中将元素转发(Z索引)
我在网上find的所有设置Z-Index的文档和示例都使用了一个Canvas元素作为容器。
我的项目是在一个DataTemplate的ItemsControl容器内的边界元素。 我使用MouseEnter和MouseLeave事件来触发ScaleTransform.ScaleX和ScaleTransform.ScaleY上的animation,以便它们在徘徊时增长。 由于它们被resize并与容器中的其他项目占据相同的空间,所以最近添加的项目与旧项目(与当前resize的项目相对)重叠。 有没有一种清洁的方式,使我的animation在代码中向前移动当前项目,以便在resize时重叠所有其他项目?
我必须处理这个。
假设你有一个ItemsControl,其ItemTemplate设置为一个自定义控件的实例。 在这个控件中你可以做Canvas.SetZIndex(this,99)。 它不会工作,因为“this”不是ItemsControl的ItemsPanel的直接子元素。 ItemsControl为每个项目创build一个ContentPresenter,将其放入ItemsPanel中,并在ContentPresenter中呈现ItemTemplate。
所以,如果你想改变你的控制ZIndex,你必须find它的ContentPresenter,并改变ZIndex。 一种方法是…
public static T FindVisualParent<T>( this DependencyObject obj ) where T : DependencyObject { DependencyObject parent = VisualTreeHelper.GetParent( obj ); while ( parent != null ) { T typed = parent as T; if ( typed != null ) { return typed; } parent = VisualTreeHelper.GetParent( parent ); } return null; } ContentPresenter foo = this.FindVisualParent<ContentPresenter>(); Canvas.SetZIndex( foo, 99 );
在WPF中有可以在触发器中设置的Panel.ZIndex属性:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Grid.Resources> <x:Array x:Key="colors" Type="{x:Type Color}"> <Color>Green</Color> <Color>Red</Color> <Color>Blue</Color> <Color>Orange</Color> <Color>Yellow</Color> <Color>Violet</Color> </x:Array> <DataTemplate DataType="{x:Type Color}"> <Border x:Name="brd" Height="20" Width="20"> <Border.Background> <SolidColorBrush Color="{Binding}"/> </Border.Background> <Border.RenderTransform> <ScaleTransform CenterX="10" CenterY="10"/> </Border.RenderTransform> <Border.Style> <Style TargetType="{x:Type Border}"> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="BorderThickness" Value="2"/> <Setter Property="BorderBrush" Value="Black"/> </Trigger> </Style.Triggers> </Style> </Border.Style> <Border.Triggers> <EventTrigger RoutedEvent="Border.MouseEnter"> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:0.5" Storyboard.TargetName="brd" Storyboard.TargetProperty="RenderTransform.ScaleX" To="1.5"/> <DoubleAnimation Duration="0:0:0.5" Storyboard.TargetName="brd" Storyboard.TargetProperty="RenderTransform.ScaleY" To="1.5"/> </Storyboard> </BeginStoryboard> </EventTrigger> <EventTrigger RoutedEvent="Border.MouseLeave"> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:0.5" Storyboard.TargetName="brd" Storyboard.TargetProperty="RenderTransform.ScaleX" To="1"/> <DoubleAnimation Duration="0:0:0.5" Storyboard.TargetName="brd" Storyboard.TargetProperty="RenderTransform.ScaleY" To="1"/> </Storyboard> </BeginStoryboard> </EventTrigger> </Border.Triggers> </Border> </DataTemplate> <Style TargetType="{x:Type ContentPresenter}"> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Panel.ZIndex" Value="99999"/> </Trigger> </Style.Triggers> </Style> </Grid.Resources> <ItemsControl ItemsSource="{StaticResource colors}" Margin="20" Width="40"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <WrapPanel/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> </ItemsControl> </Grid>
在ContentPresenter
的Style
,当IsMouseOver
为true
时,我们将Panel.ZIndex
设置为99999。 它必须位于ContentPresenter
而不是Border
因为ContentPresenter
是ItemsControl
面板的子项。
不幸的是,我不认为这个属性已经使Silverlight尚未…
对于使用Grid作为LayoutRoot的控件,您可以在控件本身中执行一些简单的操作:
Canvas.SetZIndex(this, 999);
首先find所有子元素的最大ZIndex,然后设置新的ZIndex。
private int MaxZIndex() { int iMax = 0; foreach (UIElement element in JIMSCanvas.Children) { int iZIndex=Canvas.GetZIndex(element); if(iZIndex>iMax) { iMax = iZIndex; } } return iMax+1; }
然后分配
Canvas.SetZIndex(child, MaxZIndex());
首先,附加属性Zindex在Canvas中定义,因此在Panel的其他衍生产品中不可用。
ItemsControl按照列表的顺序排列子元素。 堆栈底部的第一个项目,最后一个在顶部。 有了这个,你所要做的就是确保选中的项目在列表的底部。
首先为sorting创build一个接口。 喜欢这个:
interface IOrderable { int theZOrder{get;set;} }
现在在你正在展示的课程中实现这一点。
当你想把一个项目放在前面,给它一个很高的数字,给所有其他人一个很低的数字。
剩下的是实际的sorting。 添加这样的东西,你设置:
ItemsCont.ItemsSource = ItemsCont.Items.OrderByDesc(t=>((IOrderable)t).theZOrder);
Html以同样的方式工作。 所有的列表项必须是兄弟姐妹,如果你想分层出来。
我扩展了上面的代码,如果它find(例如)一个ItemsPresenter它将停止。 如果ContentPresenter在这里和ItemsPresenter之间没有显示,那就回到原来的代码上。
void setZindex(MyLayeredType layeredObj, int index) { ContentPresenter sibbling = FindVisualParent<ContentPresenter, ItemsPresenter>(layeredObj); if (sibbling != null) { sibbling.SetValue(Canvas.ZIndexProperty, index); } else { layeredObj.SetValue(Canvas.ZIndexProperty, index); } } public static T FindVisualParent<T,C>(this DependencyObject obj) where T : DependencyObject where C : DependencyObject { DependencyObject parent = VisualTreeHelper.GetParent(obj); while (parent != null) { T typed = parent as T; if (typed != null) { return typed; } C ceiling = parent as C; if (ceiling != null) return null; parent = VisualTreeHelper.GetParent(parent); } return null; }
以这种方式设置元素的zIndex
var zIndex = ((Panel) element.Parent) .Children.Cast<UIElement>() .Max(child => Canvas.GetZIndex(child)) ; zIndex++; Canvas.SetZIndex(element, zIndex);
- 也许你需要检查zIndex是否等于int.MaxValue,然后重置“ZIndex”。
- 但是这几乎不会发生