web-dev-qa-db-fra.com

Avancer l'élément (Index Z) dans Silverlight / WPF

Toute la documentation et les exemples que je trouve en ligne pour configurer Z-Index pour faire avancer un élément dans Silverlight utilisent un élément Canvas comme conteneur.

Mes éléments sont des éléments Border à l'intérieur d'un conteneur ItemsControl dans un DataTemplate. J'utilise les événements MouseEnter et MouseLeave pour déclencher une animation sur ScaleTransform.ScaleX et ScaleTransform.ScaleY afin qu'ils grandissent lorsqu'ils sont survolés. Comme ils sont redimensionnés et occupent le même espace que les autres éléments du ou des conteneurs, les derniers éléments ajoutés chevauchent les anciens éléments (par opposition à l'élément actuellement redimensionné). Existe-t-il un moyen PROPRE de faire avancer l'élément actuel dans le code où je déclenche mon animation afin qu'ils chevauchent tous les autres éléments lorsqu'ils sont redimensionnés?

36
Rich

J'ai dû faire face à cela.

Supposons que vous ayez un ItemsControl avec un ItemTemplate défini sur une instance d'un contrôle personnalisé. Dans ce contrôle, vous faites Canvas.SetZIndex (this, 99). Cela ne fonctionnera pas, car "ceci" n'est pas l'enfant immédiat du ItemsPanel de ItemsControl. Le ItemsControl crée un ContentPresenter pour chaque élément, le dépose dans le ItemsPanel et rend le ItemTemplate dans le ContentPresenter.

Donc, si vous voulez changer le ZIndex sous votre contrôle, vous devez trouver son ContentPresenter et changer le ZIndex à ce sujet. Une façon est ...

        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 );
34
user127111

Dans WPF, il y a la propriété Panel.ZIndex que vous pouvez définir dans un déclencheur:

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

Dans le Style pour ContentPresenter nous définissons le Panel.ZIndex à 99999 lorsque IsMouseOver est true. Il doit être sur le ContentPresenter et non sur le Border car les ContentPresenters sont des enfants du panneau de ItemsControl.

Malheureusement, je ne pense pas que cette propriété soit encore arrivée à Silverlight ...

15
Robert Macnee

Pour un contrôle qui utilise la grille comme LayoutRoot, vous pouvez simplement faire quelque chose d'aussi simple que cela dans un contrôle lui-même:

Canvas.SetZIndex(this, 999);
3
Michał Suliga

Recherchez d'abord le ZIndex maximum de tous ses éléments enfants, puis définissez le nouveau 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;
}

Attribuez ensuite

Canvas.SetZIndex(child, MaxZIndex());
3
Kishore Kumar

Le HTML fonctionne de la même manière. Tous les éléments de la liste doivent être frères et sœurs si vous souhaitez les disposer en couches.

J'ai étendu le code ci-dessus pour qu'il s'arrête s'il trouve (par exemple) un ItemsPresenter. Si le ContentPresenter ne se montre pas entre ici et le ItemsPresenter, alors retombez sur mon code d'origine.

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

Premièrement, la propriété attachée Zindex est définie dans Canvas et n'est donc pas disponible dans d'autres dérivés de Panel.

Le ItemsControl ordonne les sous-éléments selon l'ordre de la liste. Le premier objet en bas de la pile et le dernier en haut. Avec cela, tout ce que vous avez à faire est de vous assurer que l'élément sélectionné est en bas de la liste.

Créez d'abord une interface pour la commande. Comme ça:

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

Maintenant, implémentez cela dans la classe que vous montrez.

Lorsque vous souhaitez amener un article au premier plan, attribuez-lui un numéro élevé et attribuez un numéro bas à tous les autres.

Il ne reste que la commande réelle. Ajoutez quelque chose comme ça et vous êtes prêt:

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

définir zIndex de votre élément de cette manière

var zIndex = 
    ((Panel) element.Parent)
    .Children.Cast<UIElement>()
    .Max(child => Canvas.GetZIndex(child))
    ;

zIndex++;

Canvas.SetZIndex(element, zIndex);
  • vous devez peut-être vérifier si zIndex est égal à int.MaxValue puis réinitialiser les "ZIndex".
  • mais cela n'arrive presque jamais
1
Omid-RH