Search code examples
wpfz-indexitemscontrol

How to give multiple ItemsControl knowledge of each other's z-index? WPF


I have two ItemsControls within my XAML, both binding to a list of shapes with different coordinates, height, width etc...

The shapes within the same list seem to have knowledge of each other's z-index - as in I can hover over the shape and it brings it to the front of the other shapes as intended.

I wish for the rectangles in List1 to have knowledge of the z-index of the rectangles in List2, which they don't seem to have at the minute. Is there an easy way of setting up my XAML to achieve this? Thanks in advance.

My XAML:

<Grid>
        <ItemsControl ItemsSource="{Binding List1}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Rectangle Height="{Binding Height}" Width="{Binding Width}" Fill="{Binding Fill}" ></Rectangle>
            </DataTemplate>
        </ItemsControl.ItemTemplate>

        <ItemsControl.ItemContainerStyle>
            <Style TargetType="{x:Type ContentPresenter}">
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Panel.ZIndex" Value="99" />
                    </Trigger>

                </Style.Triggers>
                <Setter Property="Canvas.Top" Value="{Binding YPos}"></Setter>
                <Setter Property="Canvas.Left" Value ="{Binding XPos}"></Setter>
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>

    <ItemsControl ItemsSource="{Binding List2}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Rectangle Height="{Binding Height}" Width="{Binding Width}" Fill="{Binding Fill}" ></Rectangle>
            </DataTemplate>
        </ItemsControl.ItemTemplate>

        <ItemsControl.ItemContainerStyle>
            <Style TargetType="{x:Type ContentPresenter}">
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Panel.ZIndex" Value="99" />
                    </Trigger>

                </Style.Triggers>
                <Setter Property="Canvas.Top" Value="{Binding YPos}"></Setter>
                <Setter Property="Canvas.Left" Value ="{Binding XPos}"></Setter>
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</Grid>

My ViewModel where I define my rectangles:

public class ViewModel {

        public List<Rect> List1 { get; set; }
        public List<Rect> List2 { get; set; }

        public ViewModel() {
            List1 = new List<Rect>();
            List2 = new List<Rect>();
            PopulateList1();
            PopulateList2();
        }

        private void PopulateList2() {
            List1.Add(new Rect { Width = 30, Height = 50, XPos = 40, YPos = 50, Fill = "Blue" });
            List1.Add(new Rect { Width = 70, Height = 120, XPos = 60, YPos = 100, Fill = "Green" });
            List1.Add(new Rect { Width = 70, Height = 120, XPos = 30, YPos = 45, Fill = "Yellow" });


        }

        private void PopulateList1() {
            List2.Add(new Rect { Width = 50, Height = 100, XPos = 40, YPos = 100, Fill = "Red" });
            List2.Add(new Rect { Width = 50, Height = 100, XPos = 300, YPos = 100, Fill = "Green" });
            List2.Add(new Rect { Width = 50, Height = 100, XPos = 320, YPos = 75, Fill = "Blue" });

        }


    }

Rectangle Model:

public class Rect {
        public decimal Width { get; set; }
        public decimal Height { get; set; }
        public decimal XPos { get; set; }
        public decimal YPos { get; set; }
        public string Fill { get; set; }
    }

EDIT: https://i.sstatic.net/pgPzX.jpg To clarify, here are the rectangles being shown. Currently when I hover over the shapes in their own list they come forward as expected, but overlapping shapes from other lists don't come forward, as they have no knowledge of each other's z-index presumably.


Solution

  • You may put different types of shapes into a single list and show them in a single ItemsControl.

    Use a view model like this:

    public class Shape
    {
        public double XPos { get; set; }
        public double YPos { get; set; }
        public Color Fill { get; set; }
    }
    
    public class Circle : Shape
    {
        public double Diameter { get; set; }
    }
    
    public class Rect : Shape
    {
        public double Width { get; set; }
        public double Height { get; set; }
    }
    
    public class ViewModel
    {
        public List<Shape> Shapes { get; } = new List<Shape>();
    
        public ViewModel()
        {
            Shapes.Add(new Rect { Width = 30, Height = 50, XPos = 40, YPos = 50, Fill = Colors.Blue });
            Shapes.Add(new Rect { Width = 70, Height = 120, XPos = 60, YPos = 100, Fill = Colors.Green });
            Shapes.Add(new Rect { Width = 70, Height = 120, XPos = 30, YPos = 45, Fill = Colors.Yellow });
    
            Shapes.Add(new Circle { Diameter = 50, XPos = 40, YPos = 100, Fill = Colors.Red });
            Shapes.Add(new Circle { Diameter = 50, XPos = 300, YPos = 100, Fill = Colors.Green });
            Shapes.Add(new Circle { Diameter = 50, XPos = 320, YPos = 75, Fill = Colors.Blue });
        }
    }
    

    and an ItemControl that has DataTemplate resources for the different shape types:

    <ItemsControl ItemsSource="{Binding Shapes}">
        <ItemsControl.Resources>
            <DataTemplate DataType="{x:Type local:Circle}">
                <Ellipse Width="{Binding Diameter}" Height="{Binding Diameter}">
                    <Ellipse.Fill>
                        <SolidColorBrush Color="{Binding Fill}"/>
                    </Ellipse.Fill>
                </Ellipse>
            </DataTemplate>
    
            <DataTemplate DataType="{x:Type local:Rect}">
                <Rectangle Width="{Binding Width}" Height="{Binding Height}">
                    <Rectangle.Fill>
                        <SolidColorBrush Color="{Binding Fill}"/>
                    </Rectangle.Fill>
                </Rectangle>
            </DataTemplate>
        </ItemsControl.Resources>
    
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="{x:Type ContentPresenter}">
                <Setter Property="Canvas.Top" Value="{Binding YPos}"/>
                <Setter Property="Canvas.Left" Value ="{Binding XPos}"/>
                <Style.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Panel.ZIndex" Value="10000"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>