找出WPF中控件的实际(!)显示大小

找出WPF中控件的实际(!)显示大小,wpf,layout,Wpf,Layout,我在工具栏上做一些布局,比如控件,当没有足够的空间时需要隐藏按钮的文本。我已经在Windows窗体中成功地完成了这项工作,现在我已经将此逻辑移植到WPF。但这里有一个巨大的问题:为了让我的算法正常工作,我需要知道容器控件的期望宽度(如果所有内容都可见,则需要多大的大小)和控件的实际宽度(知道它的实际宽度以及是否有足够的空间容纳期望的宽度)。第一个是可用的,尽管有时有点落后。(如果可用空间比所需空间多,则会增加所需大小以填满所有空间,但越小越好。)后一个空间完全不可用 我尝试过使用ActualWi

我在工具栏上做一些布局,比如控件,当没有足够的空间时需要隐藏按钮的文本。我已经在Windows窗体中成功地完成了这项工作,现在我已经将此逻辑移植到WPF。但这里有一个巨大的问题:为了让我的算法正常工作,我需要知道容器控件的期望宽度(如果所有内容都可见,则需要多大的大小)和控件的实际宽度(知道它的实际宽度以及是否有足够的空间容纳期望的宽度)。第一个是可用的,尽管有时有点落后。(如果可用空间比所需空间多,则会增加所需大小以填满所有空间,但越小越好。)后一个空间完全不可用

我尝试过使用ActualWidth,但是如果网格比窗口宽,则ActualWidth比实际可见的宽度大。所以这肯定是错的。然后我尝试了渲染,但都是一样的。在我的测量呼叫后使用Arrange会导致更多的奇怪

我需要知道控制范围到底有多广,而不是它认为自己有多广。我如何确定这个尺寸

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

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;
    }
}
private void ToolGrid\u布局已更新(对象发送方,事件参数e)
{
自动缩聚序列();
}
专用void AutoCollapseItems()
{
如果(折叠项)返回;
如果(ToolGrid.ActualWidth<10)返回;//有问题
尝试
{
collasingItems=true;
//按指定优先级折叠工具栏项以节省空间,直到所有项
//放入工具栏。折叠时,项目的显示样式将从
//仅图像和文本到图像。这仅适用于具有指定
//崩溃优先级。
Dictionary collapsePriorities=新建字典();
//恢复具有collpase优先级的所有项目的显示样式。
var items=新列表();
EnumCollapsableItems(工具网格,项);
foreach(项目中的var项目)
{
如果(item.CollapsePriority>0)
{
item.ContentVisibility=可见性.Visible;
collapsePriorities[项目]=项目。CollapsePriority;
}
}
//按项目的降序折叠优先级对所有项目进行分组,并设置其显示样式
//仅当所有项目不适合工具栏时才进行图像处理。
var itemGroups=来自collapsePriorities中的kvp
其中kvp.Value>0
按kvp.Value将kvp分组为g
orderby g.键降序
选择g;
foreach(项目组中的var grp)
{
//ToolGrid.Measure(新大小(double.PositiveInfinity,double.PositiveInfinity));
//排列(新矩形(ToolGrid.DesiredSize));
//ToolGrid.UpdateLayout();
System.Diagnostics.Debug.WriteLine(“Desired=“+ToolGrid.DesiredSize.Width+”,Actual=“+ToolGrid.ActualWidth”);

如果(ToolGrid.DesiredSize.Width据我所知,您使用的是Grid,但您将列宽设置为Auto,如何使用*作为网格的宽度。列istead为Auto。如果为Auto,则网格会拉伸其宽度和高度以适应其内容,这就是为什么您的网格。宽度大于windows宽度。使用*时,列不会关心内容,但它将始终位于windows边界内

现在,在实现*之后,您可以使用位于窗口边界内的column.width/height作为最终宽度/高度,在网格内,您可以测量嵌套Inner控件的非设计大小。这就是获得控件的最终大小和非设计大小的方法

显示更多代码/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;
          }
        }
    }


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

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;
    }
}

这是在排列/测量期间使用的吗?还是通过绑定来执行?它在LayoutUpdated事件上调用(但不是递归调用),在后面的代码中。我还发现所需的宽度总是小于实际宽度。如果没有足够的空间,则所需的是可见大小,实际的是所需大小。如果有足够的空间,所需的是所需大小,实际的是可见大小。因此,这两个属性的含义颠倒过来,我不知道哪个是哪个。无用。一些代码会有帮助,你是将
UserControl
子类化还是将
Control
子类化?有大量的代码,数百行。我不想把它们都扯到这个有限的阶段。只有普通的网格容器,没有什么特殊的项目。我现在正在对窗口代码进行布局。当它更稳定时,我可能会把它放在我的后面n一个单独的控件。网格使用自动宽度列和行,正确。但必须这样,因为工具栏项目之间不能有空间。自动是正确的设置,*会导致错误的布局。确定,因此您有一个根网格和一个用于工具栏项目的网格。然后告诉您的根网格不要关心内容。在问题中添加了更多代码.你说“不关心内容”是什么意思?没有根网格,这是一个DockPanel。我说根网格指向顶部的面板。在你的情况下,这是DockPanel。我会改变我的问题,给我一分钟:)好了,怎么样?
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;
    }
}
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);
    // ...
}