2

ObservableCollection で奇妙なことが起こっています。

次のコードがあります。

private readonly ObservableCollection<DisplayVerse> _display;
private readonly ListBox _box;

    private void TransferToDisplay()
    {
        double elementsHeight = 0;

        _display.Clear();

        for (int i = 0; i < _source.Count; i++) {
            DisplayVerse verse = _source[i];
            _display.Add(verse);
            elementsHeight += CalculateItemsHeight(i);
            if (elementsHeight + Offset > _box.ActualHeight) {
                _display.RemoveAt(_display.Count - 1);
                break;
            }
        }
        MessageBox.Show(elementsHeight.ToString());
    }

    private double CalculateItemsHeight(int index)
    {
        ListBoxItem lbi = _box.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
        return lbi != null ? lbi.ActualHeight : 0;
    }

ここでやろうとしているのは、ObservableCollection _display に入るアイテムの数を制御することです。この for ループ内で、要素の高さの合計 (+offset) がリストボックス自体よりも大きくなるまで、要素が追加されることがわかります。

さて、これは奇妙です。この for ループの後、elementsHeight は 0 になります。(CalculateItemsHeight は、lbi が null でなくても、for ループの反復ですべて 0 を返します) datatemplate で定義された UI 要素が作成されていないようです...

まだ。

ここで、_display.Add(verse) の後にいくつかの MessageBoxes を配置すると、CalculateItemsHeight が実際にアイテムの高さを返すことがわかります。

for (int i = 0; i < _source.Count; i++) {
    DisplayVerse verse = _source[i];
    _display.Add(verse);
    MessageBox.Show("pause"); // <----- PROBLEM?
    elementsHeight += CalculateItemsHeight(i);
    if (elementsHeight + Offset > _box.ActualHeight) {
        _display.RemoveAt(_display.Count - 1);
        break;
    }
}
MessageBox.Show(elementsHeight.ToString());

示されているように for ループを変更した後、最後の MessageBoxは実際に、処理されたすべての要素の実際の高さを示しています。

私の質問は、UI 要素が実際に作成されるのはいつですか? MessageBoxの表示中にどこかで行われたようです。この動作は私にとってはかなり奇妙です。スレッドと関係があるのか​​もしれませんが、よくわかりません。

_display ObservableCollection に追加すると、明らかにすぐにアイテムが作成されますが、その視覚要素は作成されません (ただし、後で追加されますが、正確な時期はわかりません)。メッセージ ボックスをポップアップ表示せずに、これと同じ動作を行うにはどうすればよいですか?

4

3 に答える 3

0

wpf レイアウト エンジンはレイアウト パスとアレンジ パスを通過していないため、リストボックス項目にはまだサイズが指定されていません。メッセージ ボックスに固執すると、この実行を行うバックグラウンド スレッドが許可されます。サイズを確認する前に、アイテムに対して Measure() の呼び出しを強制してみてください。

于 2009-05-08T15:10:48.670 に答える
0

解決した

これにより、ほんの一瞬(アイテムを1つずつロードするかのように)ちらつき効果が発生しますが、実際には私のニーズに合っています。

ポイントは、アイテムの高さを取得する前に、アイテムの UI を更新することです。

拡張メソッドを作成しました:

    public static void RefreshUI(this DependencyObject obj)
    {
        obj.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Loaded, (Action)delegate { });
    }

高さを取得する前に、UI を更新します。

private double CalculateItemsHeight(int index)
    {
        ListBoxItem lbi = _box.ItemContainerGenerator.ContainerFromIndex(index) as ListBoxItem;
        if (lbi != null) {
            lbi.RefreshUI();
            return lbi.ActualHeight;
        }
        return 0;
    }
于 2009-05-08T20:22:10.513 に答える