문제

나는 일반적인 마스터----토대 시나리오를 만들 계획이다. 즉, ListView Databinding을 통해 ICollectionView, 및 별도의 컨트롤 그룹 (TextBoxes, NumupDowns ...)에서 선택한 항목에 대한 세부 사항.

지금까지 문제는 없습니다. 실제로 이전 프로젝트에서 이미 비슷한 시나리오를 구현했습니다. 그러나 가능해야합니다 여러 항목을 선택하십시오 ListView에서 적절한 것을 얻습니다 공유 값이 표시됩니다 세부 사항에서. 즉, 선택한 모든 항목이 속성에 대해 동일한 값을 갖는 경우이 값은 세부 뷰에 표시되어야합니다. 동일한 값을 공유하지 않으면 해당 컨트롤은이를 나타내는 사용자에게 시각적 단서를 제공해야하며 값을 표시해서는 안됩니다 (또는 "정의되지 않은"상태가 CheckBox 예를 들어). 이제 사용자가 값을 편집하면이 변경 사항을 적용해야합니다. 모두 선택된 항목.

추가 요구 사항은 다음과 같습니다.

  • MVVM 호환성 (즉, 코드-비만이 많지 않음)
  • 확장 성 (새로운 특성/유형은 나중에 추가 할 수 있습니다)

누구든지 그러한 시나리오에 대한 경험이 있습니까? 사실, 나는 이것이 매우 일반적인 시나리오라고 생각합니다. 그러나 나는 그 주제에 대한 세부 정보를 어디서나 찾을 수 없었습니다.

감사!
Gehho.

추신: 위에서 언급 한 오래된 프로젝트에는 뷰 모델의 서브 클래스를 사용하여 다중 선택의 특수 사례를 처리하는 솔루션이있었습니다. 선택한 모든 항목이 평등을 확인하고 적절한 값을 반환했습니다. 그러나이 접근법은 약간의 단점이 있었고 어떻게 든 해킹처럼 보였습니다. (다른 냄새 나는 것 외에) ListView 세부 뷰와 수동으로 처리합니다.

도움이 되었습니까?

해결책

In your ViewModel, create a property that will bind to your ListView's SelectedItems.

Create another property that will represent the details object of your selected items.

The details section (in the XAML) is binding to this details property (in the ViewModel).

Every time selected items collection is modified (setter/CollectionChanged event), you need to update your details object as well (that will iterate through the relevant properties and decide if they're of the same value or not).

Once a property in the details object is modified, you need to iterate back on your selected items collection and make the relevant changes on them.

That's it.

Hope it helps

다른 팁

I am running into this same exact issue.

IsSynchronizedWithCurrentItem = "True" only keeps the CurrentItem in sync (the last item you selected without holding ctrl or shift).

Just as you probably have, I have searched the internet far and wide for an answer and never came up with one. My scenario has a 3 tier Master>Detail>Detail binding, where each tier is bound to it's own ListBox.

I've rigged up something that works for the time being.

For my Master>Detail>Detail tiers I created an individual CollectionViewSource for each tier and set that CollectionViewSource.Source to the appropriate entity object. On the MasterView bound ListBox's SelectionChanged Event I performed a filter on the MasterView.View to retrieve objects where the Master Primary Key = Detail Foreign Key.

It's sloppy, but if you have found a better way to get this done, I'd love to hear it.

I have used an approach similar to the one suggested by Captain. I have created one property in my ViewModel which represents multiple items (i.e. all selected items). In the property get- and set-accessors I have used the following methods to determine/set the shared values of/for all items. This approach does not use any reflection, but uses delegates in the form of lambda expressions which makes it pretty fast.

As an overview, this is my basic design:

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)

}

The properties look like this:

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

And the code for the SharedValueHelper class looks like this:

/// <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

}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top