10

ここで、WPF DataGrid (.NET 4.0 の System.Windows.Controls.DataGrid) の並べ替えに関して奇妙な問題が発生しました。

その ItemsSource は、datacontext オブジェクトのプロパティにバインドされています。

<DataGrid HeadersVisibility="Column" SelectedIndex="0" MinHeight="30" ItemsSource="{Binding FahrtenView}" AutoGenerateColumns="False" x:Name="fahrtenDG">

FahrtenView は次のようになります。

    public ICollectionView FahrtenView
    {
        get
        {
            var view = CollectionViewSource.GetDefaultView(_fahrten);
            view.SortDescriptions.Add(new SortDescription("Index", ListSortDirection.Ascending));
            return view;
        }
    }

DataGrid がソートされます。ただし、DataContext が初めて割り当てられたときにのみソートされます。その後、(データ階層で別の「親」オブジェクトを選択することによって) DataContext を変更すると、プロパティ FahrtenView が評価されます (BP を入れることができ、デバッガーはそこで停止します) が、追加された sortdescription は完全に無視されるため、ソートは行いますもう機能しません。

すべての DataContextChange で fahrtenDG.Items.Refresh() を呼び出しても役に立ちません。

WPF DataGrid の並べ替えに関しては、これが正しい方法だと確信していますね。では、最初に呼び出されたときにその仕事を完璧にこなしたのに、なぜ頑固に仕事を拒否するのでしょうか?

何か案が?とても感謝しています。

乾杯、ヘンドリック

4

7 に答える 7

8

私は DataGrid から継承して、その根性を簡単に垣間見ました。私が見つけたのは、いくつかの不思議な理由で、OnItemsSourceChangedが初めて呼び出されたときはすべて問題ないように見えますが、その後の OnItemsSourceChanged のすべての呼び出しで ItemsSource コレクション ビューの SortDescription リストが空です。

そのため、OnItemsSourceChanged の最後に呼び出されるカスタムSetupSortDescriptionイベントを追加しました。今、私はイベント ハンドラー関数に並べ替えの説明を追加しています。これは魅力的に機能しています。

これは WPF ツールキット DataGrid のバグだと思います。

これが私のオーバーライドされた OnItemsSourceChanged です

    protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
    {
        if (SetupSortDescriptions != null && (newValue != null)) 
            SetupSortDescriptions(this, new ValueEventArgs<CollectionView>((CollectionView)newValue)); 

        base.OnItemsSourceChanged(oldValue, newValue);
    }
于 2012-08-06T14:01:49.363 に答える
4

kat のインターライトされた DataGrid を使用して、WPF DataGrid の動作を作成しました。

この動作は、初期の SortDescriptions を保存し、 のすべての変更に適用しますItemsSourceIEnumerable<SortDescription>変更のたびにリゾートを発生させる を指定することもできます。

行動

public class DataGridSortBehavior : Behavior<DataGrid>
{
    public static readonly DependencyProperty SortDescriptionsProperty = DependencyProperty.Register(
        "SortDescriptions",
        typeof (IEnumerable<SortDescription>),
        typeof (DataGridSortBehavior),
        new FrameworkPropertyMetadata(null, SortDescriptionsPropertyChanged));

    /// <summary>
    ///     Storage for initial SortDescriptions
    /// </summary>
    private IEnumerable<SortDescription> _internalSortDescriptions;

    /// <summary>
    ///     Property for providing a Binding to Custom SortDescriptions
    /// </summary>
    public IEnumerable<SortDescription> SortDescriptions
    {
        get { return (IEnumerable<SortDescription>) GetValue(SortDescriptionsProperty); }
        set { SetValue(SortDescriptionsProperty, value); }
    }


    protected override void OnAttached()
    {
        var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof (DataGrid));
        if (dpd != null)
        {
            dpd.AddValueChanged(AssociatedObject, OnItemsSourceChanged);
        }
    }

    protected override void OnDetaching()
    {
        var dpd = DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof (DataGrid));
        if (dpd != null)
        {
            dpd.RemoveValueChanged(AssociatedObject, OnItemsSourceChanged);
        }
    }

    private static void SortDescriptionsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is DataGridSortBehavior)
        {
            ((DataGridSortBehavior) d).OnItemsSourceChanged(d, EventArgs.Empty);                
        }
    }

    public void OnItemsSourceChanged(object sender, EventArgs eventArgs)
    {
        // save description only on first call, SortDescriptions are always empty after ItemsSourceChanged
        if (_internalSortDescriptions == null)
        {
            // save initial sort descriptions
            var cv = (AssociatedObject.ItemsSource as ICollectionView);
            if (cv != null)
            {
                _internalSortDescriptions = cv.SortDescriptions.ToList();
            }
        }
        else
        {
            // do not resort first time - DataGrid works as expected this time
            var sort = SortDescriptions ?? _internalSortDescriptions;

            if (sort != null)
            {
                sort = sort.ToList();
                var collectionView = AssociatedObject.ItemsSource as ICollectionView;
                if (collectionView != null)
                {
                    using (collectionView.DeferRefresh())
                    {
                        collectionView.SortDescriptions.Clear();
                        foreach (var sorter in sort)
                        {
                            collectionView.SortDescriptions.Add(sorter);
                        }
                    }
                }
            }
        }
    }
}

オプションの SortDescriptions パラメーターを使用した XAML

<DataGrid  ItemsSource="{Binding View}" >
    <i:Interaction.Behaviors>
        <commons:DataGridSortBehavior SortDescriptions="{Binding SortDescriptions}"/>
    </i:Interaction.Behaviors>
</DataGrid>

ViewModel ICollectionView のセットアップ

View = CollectionViewSource.GetDefaultView(_collection);
View.SortDescriptions.Add(new SortDescription("Sequence", ListSortDirection.Ascending));

オプション: 変更可能な SortDescriptions を提供するための ViewModel プロパティ

public IEnumerable<SortDescription> SortDescriptions
{
    get
    {
        return new List<SortDescription> {new SortDescription("Sequence", ListSortDirection.Ascending)};
    }
}
于 2015-07-16T22:14:20.073 に答える
1

同じコレクションで CollectionViewSource.GetDefaultView(..) を呼び出すと、同じ collectionview オブジェクトが返されます。これは、同一の sortdescription 構造体を追加しても変更がトリガーされない理由を説明できます。

バインドされたコレクションを更新していないため、 fahrtenDG.Items.Refresh() は機能しません。

CollectionViewSource.GetDefaultView(_fahrten).Refresh() は機能するはずです-私はそれへの参照を保持します。

あなたの説明から、データコンテキストの変更がよくわかりません-新しいオブジェクトに変更していますか? その場合、すべてのバインディングを再評価する必要があります。それは常に同じコレクションであり、listelements の Index プロパティが変更されるため、変更が予想されます。その場合、リスト要素に INotifyPropertyChanged の実装が必要になる可能性があります。コレクションが変更されない場合は、変更する必要がないからです。リゾート。

あなたの OnItemsSourceChanged(..) 実装はハックのようです:)

于 2012-08-03T11:07:47.233 に答える
0

私は、付属の動作を使用するユルゲンのアプローチを支持します。ただし、ビュー モデル クラスで CollectionViewSource オブジェクトを宣言したときにこの問題が発生したため、SortDescriptions_CollectionChanged次のコードに示すようにイベント ハンドラーを追加することで、問題をより直接的に解決できることがわかりました。このコードは完全にビュー モデル クラス内にあります。

public CollectionViewSource FilteredOptionsView
{
    get
    {
        if (_filteredOptionsView == null)
        {
            _filteredOptionsView = new CollectionViewSource
            {
                Source = Options,
                IsLiveSortingRequested = true
            };
            SetOptionsViewSorting(_filteredOptionsView);
            _filteredOptionsView.View.Filter = o => ((ConstantOption)o).Value != null;
        }
        return _filteredOptionsView;
    }
}
private CollectionViewSource _filteredOptionsView;

protected void SetOptionsViewSorting(CollectionViewSource viewSource)
{
    // define the sorting
    viewSource.SortDescriptions.Add(_optionsViewSortDescription);
    // subscribe to an event in order to handle a bug caused by the DataGrid that may be
    // bound to the CollectionViewSource
    ((INotifyCollectionChanged)viewSource.View.SortDescriptions).CollectionChanged
                                    += SortDescriptions_CollectionChanged;
}

protected static SortDescription _optionsViewSortDescription
                    = new SortDescription("SortIndex", ListSortDirection.Ascending);

void SortDescriptions_CollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
{
    var collection = sender as SortDescriptionCollection;
    if (collection == null) return;
    // The SortDescriptions collection should always contain exactly one SortDescription.
    // However, when DataTemplate containing the DataGrid bound to the ICollectionView
    // is unloaded, the DataGrid erroneously clears the collection.
    if (collection.None())
        collection.Add(_optionsViewSortDescription);
}
于 2016-11-24T00:47:59.190 に答える