是否可以设置DockPanel的内容样式以填充最后一个* visible *子级? [英] Is it possible to style the contents of a DockPanel to fill the last *visible* child?

查看:92
本文介绍了是否可以设置DockPanel的内容样式以填充最后一个* visible *子级?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑此XAML代码段...

Consider this XAML snippet...

<DockPanel x:Name="TestDockPanel">

    <Button x:Name="RightButton" Content="Right" DockPanel.Dock="Right" />
    <Button x:Name="FillButton" Content="Fill" />

</DockPanel>

按照书面规定,DockPanel将在右侧布置"RightButton",然后像这样用"FillButton"填充其余区域...

As written, the DockPanel will layout 'RightButton' to the right, then fill the rest of the area with 'FillButton' like this...

我们正在尝试寻找一种样式设置方式,以便当"FillButton"的可见性更改为"Collapsed"时,"RightButton"现在应填充该区域,就像这样...

We're trying to find a way to style it so that when 'FillButton' has its visibility changed to 'Collapsed', 'RightButton' should now fill the area, like this...

我们知道如何执行此操作的唯一方法是从'TestDockPanel'的子级中物理删除'FillButton',但这需要隐藏代码,我们试图避免这种情况.

The only way we know how to do this is to physically remove 'FillButton' from the children of 'TestDockPanel' but that requires code-behind, which we're trying to avoid.

在下面的答案中,我提出了一个基于子类的解决方案.但是,由于我希望可以与 any DockPanel(或其他子类)一起使用的东西,最好通过样式或附加行为来应用,所以我将其保留.另外,需要明确的是,该解决方案的要求是它必须基于DockPanel,而不是网格或其他面板.

Below in an answer, I've posed a solution based on a subclass. However, I'm leaving this open since I'd like something that can be used with any DockPanel (or other subclass) and preferrably applied via a style or an attached behavior. Also, to be clear, a requirement of the solution is that it must be based on a DockPanel, not a grid or other panel.

推荐答案

我以DockPanel子类的形式创建了一个解决方案,但是我没有将其标记为可接受的答案,因为我希望仍然可以找到一种方法通过样式或附加行为来做到这一点,以便它可以与 any DockPanel(或其他子类)一起使用,而不仅限于此.

I created a solution in the form of a DockPanel subclass, but I'm not marking this as the accepted answer because I'm hoping to still find a way to do this via styles or attached behaviors so it can be used with any DockPanel (or other subclass), not just this.

不过,对于其他人来说,这可能会有所帮助,因此我将其发布在这里.

Still, for others, this may be helpful so I'm posting it here.

完整类的代码如下.工作的重点是ArrangeOverride方法,该方法基于从Reflector中提取的DockPanel的原始逻辑.

The code for the full class is below. The meat of the work is in the ArrangeOverride method, which was based on the original logic from DockPanel as extracted from Reflector.

如果设置了LastChildFill,则现有逻辑的工作方式位于ArrangeOverride中,它将最后一个子项的索引(即要填充的项目的索引)存储在变量中.如果未设置LastChildFill,则将"count"存储在该变量中.

The way the existing logic worked was inside ArrangeOverride, if LastChildFill was set it stored the index of the last child (i.e. the index of the item to be filled) in a variable. If LastChildFill wasn't set, it instead stored 'count' in that variable.

然后在遍历执行实际排列的子级时,如果被排列的元素的索引小于先前存储的索引,则执行停靠"逻辑,否则执行填充"逻辑.

Then when looping through the children performing the actual arrange, if the element being arranged had an index less than the previously stored index, it performed the 'docking' logic, otherwise it performed 'fill' logic.

这意味着当LastChildFill为false时,每个元素都运行停靠"逻辑,因为它们都具有低于该存储索引的索引(该索引又等于计数",即最高索引+ 1).但是,当LastChildFill为true时,最后一个元素的索引要比存储的索引小(实际上等于它的索引),以便一个元素运行填充"逻辑,而其他所有元素都运行对接"逻辑.

That meant when LastChildFill was false, every element ran the 'docking' logic since they all had an index below that stored index (which again equals 'count', or highest index + 1). However, when LastChildFill was true, the last element didn't have an index less than the stored index (it was actually equal to it), so that one element ran the 'fill' logic while everything else ran the 'docking' logic.

我所做的更改是,如果如上设置了LastChildFill,则存储的索引开始指向最后一个子项,但是我随后检查了该元素的可见性,如果该元素不可见,则将索引降低一个并再次检查,继续进行下去,直到找到一个可见元素,或者我用完所有子元素来检查(即它们是否全部不可见).这也是为什么我将变量命名为"firstFilledIndex"的原因,因为从技术上讲,所有元素随后都使用填充"逻辑,即使它后面的所有元素都是不可见的.

The change I made was if LastChildFill is set, as above, the stored index starts out pointing to the last child, but I then check the visibility of that element and if it's invisible, I lower the index by one and check again, continuing until I either find a visible element, or I run out of children to check (i.e. if they were all invisible.) That's also why I named the variable 'firstFilledIndex' since technically that, and all elements afterwards use the 'Fill' logic, even though all the elements after it are invisible.

我终于添加了一个新的LastVisibleChildFill属性,以启用或禁用我的新行为.作为对消费者的帮助,如果将其设置为true,则也会隐式为您还将LastChildFill设置为true.

I finally added a new LastVisibleChildFill property to enable or disable my new behavior. As a help to consumers, if you set that to true, it implicitly also set LastChildFill to true for you.

这是完整的代码.

public class DockPanelEx : DockPanel
{
    public static readonly DependencyProperty LastVisibleChildFillProperty = DependencyProperty.Register(
        "LastVisibleChildFill",
        typeof(bool),
        typeof(DockPanelEx),
        new UIPropertyMetadata(true, (s,e) => {

            var dockPanelEx = (DockPanelEx)s;
            var newValue = (bool)e.NewValue;

            if(newValue)
                dockPanelEx.LastChildFill = true; // Implicitly enable LastChildFill
            // Note: For completeness, we may consider putting in code to set
            // LastVisibileChildFill to false if LastChildFill is set to false

        }));

    /// <summary>
    /// Indicates that LastChildFill should fill the last visible child
    /// Note: When set to true, automatically also sets LastChildFill to true as well.
    /// </summary>
    public bool LastVisibleChildFill
    {
        get { return (bool)GetValue(LastVisibleChildFillProperty); }
        set { SetValue(LastVisibleChildFillProperty, value); }
    }

    protected override Size ArrangeOverride(Size totalAvailableSize)
    {
        UIElementCollection internalChildren = base.InternalChildren;
        int count = internalChildren.Count;

        int firstFilledIndex = count;

        if(LastChildFill)
        {
            for(firstFilledIndex = count - 1; firstFilledIndex >= 0; firstFilledIndex--)
            {
                if(!LastVisibleChildFill || internalChildren[firstFilledIndex].IsVisible)   
                    break;
            }
        }

        double usedLeftEdge   = 0.0;
        double usedTopEdge    = 0.0;
        double usedRightEdge  = 0.0;
        double usedBottomEdge = 0.0;

        for (int i = 0; i < count; i++)
        {
            UIElement element = internalChildren[i];
            if (element != null)
            {
                Size desiredSize = element.DesiredSize;

                var finalRect = new Rect(
                    usedLeftEdge,
                    usedTopEdge,
                    Math.Max(0.0, (totalAvailableSize.Width  - (usedLeftEdge + usedRightEdge))),
                    Math.Max(0.0, (totalAvailableSize.Height - (usedTopEdge  + usedBottomEdge))));

                if (i < firstFilledIndex)
                {
                    switch (GetDock(element))
                    {
                        case Dock.Left:
                            usedLeftEdge += desiredSize.Width;
                            finalRect.Width = desiredSize.Width;
                            break;

                        case Dock.Top:
                            usedTopEdge += desiredSize.Height;
                            finalRect.Height = desiredSize.Height;
                            break;

                        case Dock.Right:
                            usedRightEdge += desiredSize.Width;
                            finalRect.X = Math.Max((double) 0.0, (double) (totalAvailableSize.Width - usedRightEdge));
                            finalRect.Width = desiredSize.Width;
                            break;

                        case Dock.Bottom:
                            usedBottomEdge += desiredSize.Height;
                            finalRect.Y = Math.Max((double) 0.0, (double) (totalAvailableSize.Height - usedBottomEdge));
                            finalRect.Height = desiredSize.Height;
                            break;
                    }
                }
                element.Arrange(finalRect);
            }
        }
        return totalAvailableSize;
    }
}

这篇关于是否可以设置DockPanel的内容样式以填充最后一个* visible *子级?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆