【问题标题】:Bring element forward (Z Index) in Silverlight/WPF在 Silverlight/WPF 中将元素前移(Z 索引)
【发布时间】:2009-03-10 12:53:44
【问题描述】:

我在网上找到的所有用于设置 Z-Index 以在 Silverlight 中将元素向前移动的文档和示例都使用 Canvas 元素作为容器。

我的项目是 DataTemplate 中 ItemsControl 容器内的 Border 元素。我正在使用 MouseEnter 和 MouseLeave 事件来触发 ScaleTransform.ScaleX 和 ScaleTransform.ScaleY 上的动画,因此它们在悬停时会增长。由于它们已调整大小并与容器中的其他项目占用相同的空间,因此最近添加的项目与旧项目重叠(与当前调整大小的项目相反)。有没有一种干净的方法可以在我触发动画的代码中将当前项目向前移动,以便它们在调整大小时与所有其他项目重叠?

【问题讨论】:

    标签: c# wpf silverlight xaml


    【解决方案1】:

    我不得不处理这个问题。

    假设您有一个 ItemsControl,其 ItemTemplate 设置为自定义控件的实例。在该控件中,您执行 Canvas.SetZIndex(this, 99)。它不起作用,因为“this”不是 ItemsControl 的 ItemsPanel 的直接子级。 ItemsControl 为每个项目创建一个 ContentPresenter,将其放入 ItemsPanel,然后在 ContentPresenter 中呈现 ItemTemplate。

    所以,如果你想在你的控件中改变 ZIndex,你必须找到它的 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 );
    

    【讨论】:

    • 除了语法错误之外,这可行。我尝试从 Silverlight Spy 3 设置 ZIndex(我试图检查这一切),但这显然没有设置 ZIndex(它一直回到 0)。使用这个助手找到 ContentPresenter 就可以了。
    • 在找到这篇文章之前已经为此工作了几天。与 Bart 不同的是,代码对我来说几乎是一样的。 PS:您可以将 FindVisualParent 函数更改为普通函数,如果您不想使用扩展方法,请将其函数原型更改为 "public static T FindVisualParent(DependencyObject obj) where T : DependencyObject" 然后调用新方法通过调用 ContentPresenter foo = this.FindVisualParent(this);
    • 我几乎遵循了这种模式,我可以看到我的自定义图钉的内容呈现器正在更新为更高的 ZIndex。但是,我仍然没有看到我的图钉 z 排序正确。我正在使用 Silverlight 4.0。我将 ZIndex 设置为 99,然后根据鼠标进入/鼠标离开设置回 0。是否发生了一些变化导致这不起作用?
    【解决方案2】:

    在 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>
    

    ContentPresenterStyle 中,当IsMouseOvertrue 时,我们将Panel.ZIndex 设置为99999。它必须在ContentPresenter 而不是Border 上,因为ContentPresenters 是ItemsControl 面板的子代。

    不幸的是,我认为这个属性还没有进入 Silverlight...

    【讨论】:

    • 是的...我也看到了,我使用 ItemsControl 作为派生自 Panel 的容器,但该道具在 SL 中不可用。谢谢
    • 不错的答案——这对我帮助很大。
    【解决方案3】:

    对于使用 Grid 作为 LayoutRoot 的控件,您只需在控件本身内执行一些简单的操作:

    Canvas.SetZIndex(this, 999);
    

    【讨论】:

      【解决方案4】:

      先找到其所有子元素的最大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());
      

      【讨论】:

        【解决方案5】:

        首先,附加属性 Zindex 在Canvas 中定义,因此在 Panel 的其他衍生产品中不可用。

        ItemsControl 根据列表的顺序对子元素进行排序。堆栈底部的第一个项目和顶部的最后一个项目。鉴于此,您所要做的就是确保所选项目位于列表的底部。

        首先为排序创建一个接口。像这样:

        interface IOrderable
            {
                int theZOrder{get;set;}
            }
        

        现在在你展示的类中实现它。

        当你想把一个项目带到最前面时,给它一个大的数字,给所有其他的小数字。

        剩下的就是实际的排序。添加这样的东西,你就设置好了:

        ItemsCont.ItemsSource = 
                    ItemsCont.Items.OrderByDesc(t=>((IOrderable)t).theZOrder);
        

        【讨论】:

        • 我一开始也是这么想的,但是为了让元素相互重叠,它们都必须是一个大画布的子元素。我不希望一个画布中的 z-index 会对另一个画布的 z-index 产生影响。但是如果我使用一个大画布,那么这些元素无论如何都不是它的直接子元素
        • 实际上,ZIndex 确实适用于其他面板类型。来试试吧。
        【解决方案6】:

        HTML 的工作方式相同。如果要分层布局,所有列表项都必须是同级的。

        我扩展了上面的代码,以便它在找到(例如)一个 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;
        }
        

        【讨论】:

          【解决方案7】:

          以这种方式设置元素的 zIndex

          var zIndex = 
              ((Panel) element.Parent)
              .Children.Cast<UIElement>()
              .Max(child => Canvas.GetZIndex(child))
              ;
          
          zIndex++;
          
          Canvas.SetZIndex(element, zIndex);
          
          • 也许您需要检查 zIndex 是否等于 int.MaxValue 然后重置“ZIndex”。
          • 但这几乎不会发生

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-09-11
            • 1970-01-01
            • 1970-01-01
            • 2016-07-13
            • 1970-01-01
            • 2017-10-25
            • 2013-02-15
            相关资源
            最近更新 更多