1

ツールバーのようなコントロールでレイアウトを行っているので、十分なスペースがない場合はボタンのテキストを非表示にする必要があります。私はすでにWindowsフォームでこれを正常に実行しており、このロジックをWPFに移植しました。しかし、ここには大きな問題があります。アルゴリズムが正しく機能するためには、コンテナーコントロールの目的の幅(すべてが表示されている場合に必要なサイズを知るため)とコントロールの実際の幅(方法を知るため)を知る必要があります。幅が広いので、希望の幅に十分なスペースがあるかどうか)。時々少し後ろ向きですが、最初のものが利用可能です。(必要以上に使用可能なスペースがある場合は、DesiredSizeを増やしてすべてを埋めますが、それより少なくても問題ありません。)後者は完全に使用できません。

私はActualWidthで試しましたが、グリッドがウィンドウよりも広い場合、ActualWidthは実際に表示されているよりも大きくなります。ですから、これはすでに間違っているに違いありません。次にRenderSizeを試しましたが、同じです。Measure呼び出しの後に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;
    }
}

その他のコード:ウィンドウ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

WPF では、ボタン要素を含む自動サイズ調整されたすべての列を含むグリッドの予測可能なサイズが得られないことが判明しました。しかし、この 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 イベントは非同期的にトリガーされるように見えるため、次のレイアウト実行が完了するまでロック変数を設定したままにする必要があり、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 に答える
1

私が理解したことから、あなたはGridを使用していますが、列幅をAutoに設定しています.Autoの代わりにGrid.ColumnのWidthに*を使用するのはどうですか. 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 に答える