C# FrameworkElement.BringToFront()和SendToBack()实现

C# FrameworkElement.BringToFront()和SendToBack()实现,c#,wpf,C#,Wpf,我一直在寻找我希望的微不足道的东西,但显然不是,因为我可以在网上找到任何令人满意的东西:一个FrameworkElement.BringToFront()和FrameworkElement.SendToBack()的实现 我的场景是,我有几个自定义控件,它们由ContentPresenter在Canvas中呈现。我可以调试两个控件的可视树,如下所示: Element1: CustomControl {HashCode

我一直在寻找我希望的微不足道的东西,但显然不是,因为我可以在网上找到任何令人满意的东西:一个
FrameworkElement.BringToFront()
FrameworkElement.SendToBack()
的实现

我的场景是,我有几个自定义控件,它们由
ContentPresenter
Canvas
中呈现。我可以调试两个控件的可视树,如下所示:

Element1:                                    
    CustomControl {HashCode: 35948007}      
    ContentPresenter {HashCode: 52397778}      
    Canvas {HashCode: 47345023}                
    ItemsPresenter {HashCode: 23452031}        
    Border {HashCode:  5260558}                
    ItemsControl {HashCode: 34643909}          
    ...

Element2:
    CustomControl {HashCode:  9741694}
    ContentPresenter {HashCode:  3532547}
    Canvas {HashCode: 47345023}          
    ItemsPresenter {HashCode: 23452031}  
    Border {HashCode:  5260558}          
    ItemsControl {HashCode: 34643909}    
    ...
    
如您所见,它们的共同祖先是“Canvas{HashCode:47345023}”。设置自定义控件的Z索引也没有帮助,因为Z索引总是相对于它们没有的同级。我以下面的内容结束,它沿着可视化树查找第一个
面板
,以及作为该面板子控件的自定义控件的祖先,以便设置该祖先的Z索引

public static void BringToFront(this FrameworkElement element)
{
    setZIndex(element, indices => indices.Max() + 1);
}

public static void SendToBack(this FrameworkElement element)
{
    setZIndex(element, indices => indices.Min() - 1);
}

private static void setZIndex(this FrameworkElement element, Func<IEnumerable<int>, int> indexCallback)
{
    DependencyObject ancestor = element;
    DependencyObject parent = VisualTreeHelper.GetParent(element);

    while (parent != null && !(parent is Panel))
    {
        ancestor = parent;
        parent = VisualTreeHelper.GetParent(parent);
    }

    if (parent is Panel panel && ancestor is UIElement ancestorElement)
    {
        var index = indexCallback(panel.ChildrenOfType<UIElement>().Select(e => Panel.GetZIndex(e)));
        Panel.SetZIndex(ancestorElement, index);
    }
}
publicstaticvoidbringtofront(此FrameworkElement)
{
setZIndex(元素,索引=>index.Max()+1);
}
公共静态void SendToBack(此FrameworkElement)
{
setZIndex(元素,索引=>index.Min()-1);
}
私有静态void setZIndex(此FrameworkElement元素,Func indexCallback)
{
DependencyObject祖先=元素;
DependencyObject parent=VisualTreeHelper.GetParent(元素);
while(parent!=null&&!(parent是面板))
{
祖先=父母;
父级=VisualTreeHelper.GetParent(父级);
}
if(父级为面板面板&&父级为UIElement AnteStorElement)
{
var index=indexCallback(panel.ChildrenOfType().Select(e=>panel.GetZIndex(e));
面板设置索引(ANCESTORELENT,索引);
}
}

问:这是一个好方法吗?它看起来很复杂…

这不应该是“简单”的静态方法。两者都依赖于
画布的一个实例。扩展
Canvas
或创建一个以
Panel
为目标的附加行为,或者修改您的方法,将宿主
Panel
作为参数,或者将这些方法转换为
Panel
的扩展方法,这将更加优雅。通过这种方式,您始终可以直接访问宿主
面板
,从而访问目标元素的所有同级。您只需设置
Panel.ZIndex
,而无需在可视化树中搜索父
Panel
。这将减少代码行数。@BionicCode但这正是我问题的一部分:控件是作为
数据模板的一部分呈现的子控件-它们不知道(也不应该明确地知道)它们最终呈现在哪个面板中。我还认为这些项不必知道它们的
面板,我没有想到这一点。我假设有一个对象知道哪个子对象属于哪个
面板
。负责更改这些子项的z索引的同一对象。如果您无法访问主机
面板
,则搜索它是唯一的解决方案。你没有提供任何关于你如何使用你的方法的信息。我没有足够的信息来真正帮助或改进。我只是提供了一个不需要可视化树遍历的替代解决方案。