1

我正在对类似工具栏的控件进行一些布局,并且在空间不足时需要隐藏按钮的文本。我已经在 Windows 窗体中成功完成了这项工作,现在我已将此逻辑移植到 WPF。但是这里有一个很大的问题:为了让我的算法正常工作,我需要知道容器控件的所需宽度(知道如果所有内容都可见则需要什么大小)和控件的实际宽度(知道如何确实很宽,以及是否有足够的空间容纳所需的宽度)。第一个是可用的,尽管有时有点倒退。(如果可用空间多于所需空间,则 DesiredSize 会增加以将其全部填满,尽管更少也可以。)后一个完全不可用!

我已经尝试过 ActualWidth,但如果 Grid 比窗口宽,则 ActualWidth 比实际可见的要多。所以这一定是错的。然后我尝试了 RenderSize,但它是一样的。在我的 Measure 调用之后使用 Arrange 会导致更多的怪异。

我需要知道控件的实际宽度,而不是它认为自己的宽度。我怎样才能确定那个大小?

更新:好的,这里有一些代码。这个问题已经很长了,仍然不完整。这是来自 Window 的代码隐藏。

private void ToolGrid_LayoutUpdated(object sender, EventArgs e)
{
    AutoCollapseItems();
}

private void AutoCollapseItems()
{
    if (collapsingItems) return;
    if (ToolGrid.ActualWidth < 10) return;   // Something is wrong
    try
    {
        collapsingItems = true;

        // Collapse toolbar items in their specified priority to save space until all items
        // fit in the toolbar. When collapsing, the item's display style is reduced from
        // image and text to image-only. This is only applied to items with a specified
        // collapse priority.

        Dictionary<ICollapsableToolbarItem, int> collapsePriorities = new Dictionary<ICollapsableToolbarItem, int>();

        // Restore the display style of all items that have a collpase priority.
        var items = new List<ICollapsableToolbarItem>();
        EnumCollapsableItems(ToolGrid, items);
        foreach (var item in items)
        {
            if (item.CollapsePriority > 0)
            {
                item.ContentVisibility = Visibility.Visible;
                collapsePriorities[item] = item.CollapsePriority;
            }
        }

        // Group all items by their descending collapse priority and set their display style
        // to image-only as long as all items don't fit in the toolbar.
        var itemGroups = from kvp in collapsePriorities
                         where kvp.Value > 0
                         group kvp by kvp.Value into g
                         orderby g.Key descending
                         select g;
        foreach (var grp in itemGroups)
        {
            //ToolGrid.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
            //ToolGrid.Arrange(new Rect(ToolGrid.DesiredSize));
            //ToolGrid.UpdateLayout();
            System.Diagnostics.Debug.WriteLine("Desired=" + ToolGrid.DesiredSize.Width + ", Actual=" + ToolGrid.ActualWidth);
            if (ToolGrid.DesiredSize.Width <= ToolGrid.ActualWidth) break;
            foreach (var kvp in grp)
            {
                kvp.Key.ContentVisibility = Visibility.Collapsed;
            }
        }
        //ToolGrid.UpdateLayout();
    }
    finally
    {
        collapsingItems = false;
    }
}

更多代码:这是 Window XAML 的一部分:

<Window>
    <DockPanel>
        <Grid Name="ToolGrid" DockPanel.Dock="Top" LayoutUpdated="ToolGrid_LayoutUpdated">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
                ...
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
        </Grid>
4

2 回答 2

1

据我了解,您使用的是 Grid,但您将列宽度设置为 Auto,您如何使用 * 作为 Grid.Column 的宽度而不是 Auto。如果 Auto 然后 Grid 拉伸其宽度和高度以适应其内容,因此您的 Grid.Width 大于窗口宽度。当您使用 * 时,该列不会关心内容,但它始终位于窗口边界内。

现在在实现 * 之后,您可以使用位于窗口边界内的 column.width/height 作为最终宽度/高度,并且在 Grid 内,您可以测量嵌套内部控件的缩小尺寸。这就是你如何获得最终尺寸和缩小尺寸的控件。

显示更多代码/xaml,我们将能够为您提供更多帮助。

编辑:

<Window>
<DockPanel x:Name="dockyPanel>
    <Grid Name="ToolGrid" DockPanel.Dock="Top" LayoutUpdated="ToolGrid_LayoutUpdated">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            ...
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
    </Grid>


var itemGroups = from kvp in collapsePriorities
                     where kvp.Value > 0
                     group kvp by kvp.Value into g
                     orderby g.Key descending
                     select g;
    double x = 0.0;
    foreach (var grp in itemGroups)
    {
        // x will be increased by the sum of all widths of items
        x += grp.SumOfAllWidthOfGroup;

        // if x greater than available space then this group needs to collaps its items
        if(x > this.dockyPanel.ActualWidth)
        {
          foreach (var kvp in grp)
          {
            kvp.Key.ContentVisibility = Visibility.Collapsed;
          }
        }
    }

这个怎么样?我的伪代码会进一步帮助你吗?

于 2013-02-25T19:59:55.170 回答
1

事实证明,WPF 不会为我提供可预测的 Grid 大小,该 Grid 包含所有带有按钮元素的自动调整大小的列。但是这个 Grid 的父元素,无论它是什么以及它是如何布局的,都提供了可用的信息。所以我的解决方案基本上是在已经存在的内容和我的实际工具栏布局网格之间插入另一个级别的 Grid 容器,并比较两者的不同大小。确定网格是否适合给定空间的中心测试现在是:

foreach (var grp in itemGroups)
{
    InnerToolGrid.UpdateLayout();
    if (InnerToolGrid.RenderSize.Width - extentWidth <= ToolGrid.ActualWidth) break;
    foreach (var kvp in grp)
    {
        kvp.Key.ContentVisibility = Visibility.Collapsed;
    }
}

它遍历所有可能被折叠在一起的元素的优先级(无论它们位于网格上的哪个位置),并折叠一个类(组)中的所有元素。然后更新内部网格的布局以反映按钮的变化,并将内部网格的 RenderSize 与外部网格的 ActualWidth 进行比较。如果它更小,则它适合并且不需要折叠更多项目。

所有这些都是从内部网格的 LayoutUpdated 事件中调用的,仍然防止通过锁定变量进行递归。这使我可以对工具栏上任何按钮的大小变化做出反应,例如当文本更新时。

由于 LayoutCompleted 事件似乎是异步触发的,因此 lock 变量必须保持设置直到下一次布局运行完成,并且不能在 LayoutUpdated 处理程序结束时再次重置:

private bool collapsingItems;
private void InnerToolGrid_LayoutUpdated(object sender, EventArgs e)
{
    if (collapsingItems) return;
    // Prevent further calls before the layouting is completely finished
    collapsingItems = true;
    Dispatcher.BeginInvoke(
        (Action) (() => { collapsingItems = false; }),
        System.Windows.Threading.DispatcherPriority.Loaded);
    // ...
}
于 2013-02-26T19:00:06.860 回答