I have a ItemsControl which uses a Canvas as as as the ItemsPanel. The itemsTemplate hosts a Custom Control. When the control is clicked I would like it to "move to the front". I know I can do this with the ZIndex in my control like so:
private void MyControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if(((FrameworkElement)sender).TemplatedParent==null) return;
ContentPresenter presenter = (ContentPresenter)((FrameworkElement) sender).TemplatedParent;
int currentValue = Panel.GetZIndex(presenter);
Console.WriteLine(currentValue);
Panel.SetZIndex(presenter, 99);
}
This works and sets the ZIndex just fine. But when I click on a different control in the ItemsControl it will get the exact same ZIndex so it will not "move in front" of the other control.
Basically I just want the same behavior as windows exhibit.
I've tried setting the ZIndex back to 0 in the PreviewMouseUp event, and that ensures that the clicked control is always at the top, but then since all other controls have the same ZIndex of 0 then will assume default order based on there position in the list.
An example:
开发者_如何学GoLets say I have 3 controls all with a ZIndex of 0. (so they use the default list position.)
If I click on control 3, then 2 then 1. I would expect it to have 3 at the top of the ZOrder, then 2 then 1. But since I'm setting the ZIndex to zero on MouseUp it would have the default order of 1,2,3.
Is there an easy way to handle this or do I need to keep track of the order and increment decrement an arbitrary value such as 99 on my own?
I would do something along these lines:
Upon mouse down set the Z-index on the selected control to a very high number. Have a static int Zindex initialized to 0. Upon mouse up set the Z-index equal to Zindex++
private static int Zindex = 0;
private void MyControl_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if(((FrameworkElement)sender).TemplatedParent==null) return;
ContentPresenter presenter = (ContentPresenter)((FrameworkElement) sender).TemplatedParent;
int currentValue = Panel.GetZIndex(presenter);
Console.WriteLine(currentValue);
Panel.SetZIndex(presenter, Int32.MaxValue);
}
private void MyControl_PreviewMouseUp(object sender, MouseButtonEventArgs e)
{
if(((FrameworkElement)sender).TemplatedParent==null) return;
ContentPresenter presenter = (ContentPresenter)((FrameworkElement) sender).TemplatedParent;
int currentValue = Panel.GetZIndex(presenter);
Console.WriteLine(currentValue);
Panel.SetZIndex(presenter, Zindex++);
}
If your program should be running for a long time you could have some upper limit on Zorder, so if reached you could find the smallest z-index in use and substract this from the z-index on all controls and the Zindex counter.
In my case I set Zindex on selected control and then enumerate all other controls and increment their ZIndex if needed, since at some point you can encounter selected control and all others doen't need to be changed.
Ugly, but so far I have no better solution
The easiest way is to enumerate all items just once and only decrease the z-index if we've passed the one to bring to the front. This ensures the z-index of the bottom-most item always has a z-index of 0 and the last item a z-index of (count - 1), with each differing by an integer of 1.
If you increase (or decrease) each one every time, setting the one to bring to front with highest, the z-index may never start at 0 and the ultimate result might be too counter-intuitive for your liking.
So whenever an item should be brought to the front, you can enumerate the items like this:
//If you don't already have the z-index of the item to bring to front (or target item)...
var i = YourItem.Z;
foreach (var j in YourListOfItems)
{
//Current item occurs after target item in sequence
if (j.Z > i)
{
j.Z--;
}
else if (j.Z < i)
{
//Curent item occurs before target item in sequence; no need to change the z-index
}
else j.Z = YourListOfItems.Count - 1;
}
This assumes you bind a list of your objects to an ItemsControl
and the objects have a Z
property that binds to Panel.ZIndex
property in the ItemContainerStyle
.
I have a similar situation but I need to bring the item to front when the mouse hovers over the control. I have found a much simpler solution to the problem: simply set the Panel.ZIndex in the ItemsControl.ItemContainerStyle via a trigger.
This could also use a DataTrigger that is bound to a flag in the viewModel, for example an IsSelected property that is set when the control is clicked.
<ItemsControl Name="ScanPoints" ItemsSource="{Binding ScanPoints}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas></Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Panel.ZIndex" Value="99999"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Panel.ZIndex" Value="0"/>
</Trigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
精彩评论