WPF - TabControl 和 ZIndex 中重叠的自定义选项卡
问题
我有一个自定义选项卡控件,使用绑定到 ViewModel 的 Chrome 形状选项卡。由于形状的原因,边缘有点重叠。我有一个在 TabControl_SelectionChanged
上设置 tabItem 的 ZIndex 的函数,它可以很好地选择选项卡和拖/放选项卡,但是当我通过中继命令添加或关闭选项卡时,我得到了不寻常的结果。有人有什么想法吗?
默认视图:
删除标签页:
连续添加 2 个或更多选项卡:
一次添加超过 1 个选项卡不会重置其他最近添加的选项卡的 zindex,因此它们会位于右侧的选项卡,并且关闭选项卡不会正确呈现替换它的 SelectedTab 的 ZIndex,并且它显示在右侧选项卡的后面。
设置 ZIndex 的代码
private void PrimaryTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.Source is TabControl)
{
TabControl tabControl = sender as TabControl;
ItemContainerGenerator icg = tabControl.ItemContainerGenerator;
if (icg.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
foreach (object o in tabControl.Items)
{
UIElement tabItem = icg.ContainerFromItem(o) as UIElement;
Panel.SetZIndex(tabItem, (o == tabControl.SelectedItem ? 100 :
90 - tabControl.Items.IndexOf(o)));
}
}
}
}
通过使用断点,我可以看到它正确地将 ZIndex 设置为我想要的值,但是布局没有显示更改。我知道一些更改正在生效,因为如果它们都不起作用,那么选项卡边缘将被反转(右侧选项卡将绘制在左侧选项卡的顶部)。单击选项卡将正确设置所有选项卡(包括应在顶部绘制的选项卡)的 zindex,并且拖放它们以重新排列它们也会正确呈现(这会删除并重新插入选项卡项目)。我能想到的唯一区别是我使用 MVVM 设计模式,并且添加/关闭选项卡的按钮是中继命令。
有谁知道为什么会发生这种情况以及我该如何解决它?
ps 我确实尝试在 ViewModel 中设置 ZIndex 并绑定到它,但是通过中继命令添加/删除选项卡时会发生同样的事情。
Problem
I have a custom tab control using Chrome-shaped tabs that binds to a ViewModel. Because of the shape, the edges overlap a bit. I have a function that sets the tabItem's ZIndex on TabControl_SelectionChanged
which works fine for selecting tabs, and dragging/dropping tabs, however when I Add or Close a tab via a Relay Command I am getting unusual results. Does anyone have any ideas?
Default view:
Removing Tabs:
Adding 2 or more Tabs in a row:
Adding more then 1 tab at a time will not reset the zindex of other recently-added tabs so they go behind the tab on the Right, and closing tabs does not correctly render the ZIndex of the SelectedTab that replaces it and it shows up behind the tab on its right.
Code to set ZIndex
private void PrimaryTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.Source is TabControl)
{
TabControl tabControl = sender as TabControl;
ItemContainerGenerator icg = tabControl.ItemContainerGenerator;
if (icg.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
foreach (object o in tabControl.Items)
{
UIElement tabItem = icg.ContainerFromItem(o) as UIElement;
Panel.SetZIndex(tabItem, (o == tabControl.SelectedItem ? 100 :
90 - tabControl.Items.IndexOf(o)));
}
}
}
}
By using breakpoints I can see that it is correctly setting the ZIndex to what I want it to, however the layout is not displaying the changes. I know some of the changes are in effect because if none of them were working then the tab edges would be reversed (the right tabs would be drawn on top of the left ones). Clicking a tab will correctly set the zindex of all tabs (including the one that should be drawn on top) and dragging/dropping them to rearrange them also renders correctly (which removes and reinserts the tab item). The only difference I can think of is I am using the MVVM design pattern and the buttons that Add/Close tabs are relay commands.
Does anyone have any idea why this is happening and how I can fix it??
p.s. I did try setting a ZIndex in my ViewModel and binding to it, however the same thing happens when adding/removing tabs via the relay command.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
谢谢阿部,你的第二条评论引导我找到了解决方案!
我将 tabItem.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate); 添加到循环的每次迭代中。
我仍然有兴趣了解是否有其他人找到解决此问题的方法,而无需在每次更改时刷新每个 tabItem。我尝试在循环结束时刷新整个选项卡控件,但这仅适用于关闭选项卡,而不能添加它们。我知道 Panel.ZIndex 设置正确,只是在渲染时不尊重该属性。
编辑:上面的代码行在拖/放选项卡时会导致异常闪烁,该选项卡会短暂显示被拖动选项卡后面的选项卡。我将代码移动到一个单独的函数中,并以较低的调度程序优先级调用它,这解决了问题。最终代码如下:
Thank you Abe, your second comment lead me to my solution!
I added
tabItem.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
to each iteration of the loop.I would still be interested in learning if anyone else finds a way around this without refreshing each tabItem on every change. I tried refreshing the entire tab control at the end of the loop but that only worked for closing tabs, not adding them. I know that Panel.ZIndex is getting set correctly, it's just not honoring that property when rendering.
EDIT: The above line of code was causing an unusual flicker when Dragging/Dropping the tabs that would briefly show the tab behind the one being dragged. I moved the code to a separate function and called it at a lower dispatcher priority and this fixed the problem. Final code is below:
听起来您只需要在集合发生变化时再次运行算法即可。由于您正在测试
ItemContainerGenerator.Status
属性,因此该算法可能无法运行。您可能需要考虑监听StatusChanged
事件,并在其更改为ContainersGenerate
时再次运行算法。It sounds like you just need to run your algorithm again when the collection changes. Since you are testing the
ItemContainerGenerator.Status
property, the algorithm may not run. You may want to consider listening to theStatusChanged
event, and when it changes toContainersGenerated
run the algorithm again.