2

典型的なマスター/ディテール シナリオを作成する予定です。つまり、ListViewDataBinding を介してに表示される項目のコレクションとICollectionView、選択した項目に関する詳細を別のコントロール グループ (TextBoxes、NumUpDowns...) に作成する予定です。

これまでのところ問題はありません。実際、以前のプロジェクトでかなり似たシナリオを既に実装しています。ただし、ListView で複数の項目を選択して、詳細ビューに表示される適切な共有値を取得できる必要があります。これは、選択したすべてのアイテムのプロパティに同じ値がある場合、この値を詳細ビューに表示する必要があることを意味します。それらが同じ値を共有していない場合、対応するコントロールは、これを示す視覚的な手がかりをユーザーに提供する必要があり、値は表示されません (または、CheckBoxたとえば「未定義」状態)。ここで、ユーザーが値を編集すると、この変更は選択したすべてのアイテムに適用されます。

その他の要件は次のとおりです。

  • MVVM の互換性 (コード ビハインドが多すぎない)
  • 拡張性 (新しいプロパティ/タイプを後で追加できます)

誰もそのようなシナリオの経験がありますか? 実際、これは非常に一般的なシナリオであるべきだと思います。しかし、そのトピックに関する詳細はどこにも見つかりませんでした。

ありがとう!
ゲーホ。

PS:上記の古いプロジェクトでは、複数選択の特殊なケースを処理する ViewModel のサブクラスを使用したソリューションがありました。選択したすべてのアイテムが等しいかどうかをチェックし、適切な値を返しました。ただし、このアプローチにはいくつかの欠点があり、ハックのように見えました。これは、(他の臭いに加えて) とListView詳細ビューの間の同期を解除して手動で処理する必要があったためです。

4

3 に答える 3

1

ViewModel で、ListView の SelectedItems にバインドするプロパティを作成します。

選択したアイテムの詳細オブジェクトを表す別のプロパティを作成します。

詳細セクション (XAML 内) は、この詳細プロパティ (ViewModel 内) にバインドされています。

選択した項目のコレクションが変更されるたびに (セッター/CollectionChanged イベント)、詳細オブジェクトも更新する必要があります (関連するプロパティを反復処理し、それらが同じ値かどうかを判断します)。

詳細オブジェクトのプロパティが変更されたら、選択した項目のコレクションを繰り返し処理し、関連する変更を加える必要があります。

それでおしまい。

それが役に立てば幸い

于 2010-08-02T09:30:46.593 に答える
1

私はこれとまったく同じ問題に直面しています。

IsSynchronizedWithCurrentItem = "True"CurrentItem のみを同期します (ctrl または shift を押さずに選択した最後のアイテム)。

おそらくあなたが持っているように、私は答えを求めてインターネットを広範囲に検索しましたが、1つも思いつきませんでした. 私のシナリオには、3 層のマスター>詳細>詳細バインディングがあり、各層は独自の ListBox にバインドされています。

とりあえず使えるものを揃えました。

Master>Detail>Detail 層では、層ごとに個別の CollectionViewSource を作成し、その CollectionViewSource.Source を適切なエンティティ オブジェクトに設定しました。MasterView バインドされた ListBox の SelectionChanged イベントで、MasterView.View でフィルタを実行して、Master Primary Key = Detail Foreign Key であるオブジェクトを取得しました。

ずさんですが、これを行うためのより良い方法を見つけた場合は、それを聞いてみたいです.

于 2010-08-02T02:14:02.240 に答える
0

キャプテンが提案したものと同様のアプローチを使用しました。複数のアイテム (つまり、選択されたすべてのアイテム) を表す ViewModel に 1 つのプロパティを作成しました。プロパティの get- および set-accessors では、次のメソッドを使用して、すべてのアイテムの共有値を決定/設定しました。このアプローチはリフレクションを使用しませんが、ラムダ式の形式でデリゲートを使用するため、かなり高速になります。

概要として、これは私の基本的な設計です。

public class MyMultiSelectionViewModel
{
    private List<MyItemType> m_selectedItems = new List<MyItemType>();

    public void UpdateSelectedItems(IList<MyItemType> selectedItems)
    {
        m_selectedItems = selectedItems;

        this.OnPropertyChanged(() => this.MyProperty1);
        this.OnPropertyChanged(() => this.MyProperty2);
        // and so on for all relevant properties
    }

    // properties using SharedValueHelper (see example below)

}

プロパティは次のようになります。

public string Name
{
    get
    {
        return SharedValueHelper.GetSharedValue<MyItemType, string>(m_selectedItems, (item) => item.Name, String.Empty);
    }
    set
    {
        SharedValueHelper.SetSharedValue<MyItemType, string>(m_selectedItems, (item, newValue) => item.Name = newValue, value);
        this.OnPropertyChanged(() => this.Name);
    }
}

クラスのコードはSharedValueHelper次のようになります。

/// <summary>
/// This static class provides some methods which can be used to
/// retrieve a <i>shared value</i> for a list of items. Shared value
/// means a value which represents a common property value for all
/// items. If all items have the same property value, this value is
/// the shared value. If they do not, a specified <i>non-shared value</i>
/// is used.
/// </summary>
public static class SharedValueHelper
{

    #region Methods

    #region GetSharedValue<TItem, TProperty>(IList<TItem> items, Func<TItem, TProperty> getPropertyDelegate, TProperty nonSharedValue)

    /// <summary>
    /// Gets a value for a certain property which represents a
    /// <i>shared</i> value for all <typeparamref name="TItem"/>
    /// instances in <paramref name="items"/>.<br/>
    /// This means, if all wrapped <typeparamref name="TItem"/> instances
    /// have the same value for the specific property, this value will
    /// be returned. If the values differ, <paramref name="nonSharedValue"/>
    /// will be returned.
    /// </summary>
    /// <typeparam name="TItem">The type of the items for which a shared
    /// property value is requested.</typeparam>
    /// <typeparam name="TProperty">The type of the property for which
    /// a shared value is requested.</typeparam>
    /// <param name="items">The collection of <typeparamref name="TItem"/>
    /// instances for which a shared value is requested.</param>
    /// <param name="getPropertyDelegate">A delegate which returns the
    /// property value for the requested property. This is used, so that
    /// reflection can be avoided for performance reasons. The easiest way
    /// is to provide a lambda expression like this:<br/>
    /// <code>(item) => item.MyProperty</code><br/>
    /// This expression will simply return the value of the
    /// <c>MyProperty</c> property of the passed item.</param>
    /// <param name="nonSharedValue">The value which should be returned if
    /// the values are not equal for all items.</param>
    /// <returns>If all <typeparamref name="TItem"/> instances have
    /// the same value for the specific property, this value will
    /// be returned. If the values differ, <paramref name="nonSharedValue"/>
    /// will be returned.</returns>
    public static TProperty GetSharedValue<TItem, TProperty>(IList<TItem> items, Func<TItem, TProperty> getPropertyDelegate, TProperty nonSharedValue)
    {
        if (items == null || items.Count == 0)
            return nonSharedValue;

        TProperty sharedValue = getPropertyDelegate(items[0]);
        for (int i = 1; i < items.Count; i++)
        {
            TItem currentItem = items[i];
            TProperty currentValue = getPropertyDelegate(currentItem);
            if (!sharedValue.Equals(currentValue))
                return nonSharedValue;
        }

        return sharedValue;
    }

    #endregion

    #region SetSharedValue<TItem, TProperty>(IList<TItem> a_items, Action<TItem, TProperty> a_setPropertyDelegate, TProperty a_newValue)

    /// <summary>
    /// Sets the same value for all <typeparamref name="TItem"/>
    /// instances in <paramref name="a_items"/>.
    /// </summary>
    /// <typeparam name="TItem">The type of the items for which a shared
    /// property value is requested.</typeparam>
    /// <typeparam name="TProperty">The type of the property for which
    /// a shared value is requested.</typeparam>
    /// <param name="items">The collection of <typeparamref name="TItem"/>
    /// instances for which a shared value should be set.</param>
    /// <param name="setPropertyDelegate">A delegate which sets the
    /// property value for the requested property. This is used, so that
    /// reflection can be avoided for performance reasons. The easiest way
    /// is to provide a lambda expression like this:<br/>
    /// <code>(item, newValue) => item.MyProperty = newValue</code><br/>
    /// This expression will simply set the value of the
    /// <c>MyProperty</c> property of the passed item to <c>newValue</c>.</param>
    /// <param name="newValue">The new value for the property.</param>
    public static void SetSharedValue<TItem, TProperty>(IList<TItem> items, Action<TItem, TProperty> setPropertyDelegate, TProperty newValue)
    {
        if (items == null || items.Count == 0)
            return;

        foreach (TItem item in items)
        {
            try
            {
                setPropertyDelegate(item, newValue);
            }
            catch (Exception ex)
            {
                // log/error message here
            }
        }
    }

    #endregion

    #endregion

}
于 2010-08-02T14:23:04.900 に答える