views:

11080

answers:

7

All the documentation and examples I'm finding online for setting Z-Index to bring an element forward in Silverlight are using a Canvas element as a container.

My items are Border elements inside of an ItemsControl container in a DataTemplate. I'm using the MouseEnter and MouseLeave events to trigger an animation on the ScaleTransform.ScaleX and ScaleTransform.ScaleY so they grow when hovered. As they're resized and occupying the same space as other items in the container(s), the most recently added items are overlapping the older items (as opposed to the currently resizing item). Is there a CLEAN way to bring the current item forward in code where I trigger my animation so that they overlap all other items when they're resized?

A: 

First, the attached property Zindex is defined in Canvas and thus is not available in other derivatives of Panel.

The ItemsControl orders the subelements according to the order of the list. The first Item at the bottom of the stack and the last on top. With that given, all you have to do is making sure the selected item is on bottom of the list.

First create an interface for the ordering. Like this:

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

Now implement this in the class you're showing.

When you want to bring a item to the front, give it a high number and give all others a low number.

Al there's left is the actual ordering. Add something like this and you're set:

ItemsCont.ItemsSource = 
            ItemsCont.Items.OrderByDesc(t=>((IOrderable)t).theZOrder);
Sorskoot
I thought of that at first, but in order for the elements to overlap each other, they'd all have to be children of one big canvas. I wouldn't expect a z-index in one canvas to have an effect on the z-index of another. But if I use one big canvas, the elements aren't direct children of it anyway
Rich
Actually, ZIndex does work for other Panel types. Go ahead and try it.
ptoinson
A: 

In WPF there is the Panel.ZIndex property that you can set in a trigger:

<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
    <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>

In the Style for ContentPresenter we set the Panel.ZIndex to 99999 when IsMouseOver is true. It must be on the ContentPresenter and not the Border because the ContentPresenters are children of the ItemsControl's panel.

Unfortunately I don't think this property has made it to Silverlight yet...

Robert Macnee
Yeah...I saw that too, and I'm using ItemsControl as my container which derives from Panel, but the prop isn't available in SL. Thx
Rich
+5  A: 

Canvas.ZIndex works in any Panel, not just Canvas.

The property has this somewhat confusing name due to the fact that Silverlight 1 only had Canvas, no other Panel.

KeithMahoney
If that's the case, maybe I should re-state my question. The container that I'm animating is of type Border which sits inside an ItemsControl which itself sits inside another ItemsControl. Can I use this attached property on the Border object to refer to the z order of the outermost ItemsControl?
Rich
No, this is not possible; you could set Canvas.ZIndex on both the Border and the containing ItemsControl.
KeithMahoney
I like that idea. Not that I like it...I'd much prefer that the containers I'm using support ZIndex, but this sounds like the only thing that would work. I'll try it out and post the results
Rich
A: 

I ran into the same issue. My application was simple and I had a single window (user control) that always appeared on top of the rest of the content. I put it's declaration at the bottom of the XAML file. Since Silverlight zorder is based on position in the page, putting it at the bottom of the XAML forced it to be "on top" (ZOrder wise) of hte rest of the content. I'm then moving that user control into view or out of view as I need to show it.

--Matt

Matthew Timbs
+8  A: 

I've had to deal with this.

Say you have an ItemsControl with an ItemTemplate set to an instance of a custom control. Within that control you do Canvas.SetZIndex(this, 99). It won't work, because "this" is not the immediate child of the ItemsControl's ItemsPanel. The ItemsControl creates a ContentPresenter for each item, drops that into the ItemsPanel, and renders the ItemTemplate within the ContentPresenter.

So, if you want to change the ZIndex within your control, you have to find its ContentPresenter, and change the ZIndex on that. One way is...

        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 );
Apart from the syntax errors, this works. I tried setting the ZIndex from Silverlight Spy 3 (which I tried to inspect this all), but that apparently doesn't set the ZIndex (it keeps going back to 0). Finding the ContentPresenter using this helper did the trick.
Bart Roozendaal
Legendary, this did the trick for my window layering issues.
TreeUK
A: 

I am using VS 2010, I have a main grid with 2 Rows. Each row have 2 grids. Each grid have buttons. For example Grid 1 yellow have color buttons, Grid 2 have blue color buttons. I have written the following style for the buttons.

<Style TargetType="{x:Type Buttons}"> 
    <Style.Triggers> 
        <Trigger Property="IsMouseOver" Value="True"> 
            <Setter Property="RenderTransform">
                <Setter.Value>
                    <ScaleTransform ScaleX="1" ScaleY="2" />
                </Setter.Value>
            </Setter>
            <Setter Property="Panel.ZIndex" Value="99999"/> 
        </Trigger> 
    </Style.Triggers> 
</Style>

Problem: When the mouse is over on a yellow color button in the Grid 1. But the yellow color button is not above the blue color buttons on the Grid 2. Panel.ZIndex is not working on the two different Grids. Please let me know how to solve this issue.

ksvimal
This is a question, not an answer.
Blakomen
A: 

Html works the same way. All the list items must be siblings if you want to lay them out in layers.

I extended the code above so that it will stop if it finds (for example) an ItemsPresenter. If the ContentPresenter doesn't show itself between here and the ItemsPresenter, then fall back on my original code.

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;
}
charlierlee