Pergunta

Eu pretendo criar um cenário típico de detalhes mestre, ou seja, uma coleção de itens exibidos em um ListView via banco de dados para um ICollectionView, e detalhes sobre o item selecionado em um grupo separado de controles (caixas de texto, numupdowns ...).

Até agora, não há problema, na verdade eu já implementei um cenário bastante semelhante em um projeto mais antigo. No entanto, deve ser possível Selecione vários itens no ListView e obtenha o apropriado valores compartilhados exibidos na visualização detalhada. Isso significa que, se todos os itens selecionados tiverem o mesmo valor para uma propriedade, esse valor deverá ser exibido na visualização detalhada. Se eles não compartilharem o mesmo valor, o controle correspondente deve fornecer alguma pista visual para o usuário indicando isso, e nenhum valor deve ser exibido (ou um estado "indefinido" em um CheckBox por exemplo). Agora, se o usuário editar o valor, essa alteração deve ser aplicada a tudo itens selecionados.

Outros requisitos são:

  • Compatibilidade MVVM (ou seja, não muito código-behind)
  • Extendibilidade (novas propriedades/tipos podem ser adicionados posteriormente)

Alguém tem experiência com esse cenário? Na verdade, acho que esse deve ser um cenário muito comum. No entanto, não consegui encontrar detalhes sobre esse tópico em nenhum lugar.

Obrigado!
Gehho.

PS: No projeto mais antigo mencionado acima, eu tinha uma solução usando uma subclasse do ViewModel que lida com o caso especial de várias seleções. Ele verificou todos os itens selecionados quanto à igualdade e retornou os valores apropriados. No entanto, essa abordagem teve algumas desvantagens e, de alguma forma, parecia um hack porque (além de outras coisas fedorentas) era necessário quebrar a sincronização entre o ListView e a visualização detalhada e manuseie -a manualmente.

Foi útil?

Solução

No seu ViewModel, crie uma propriedade que se vincule aos SelectedItems do seu ListView.

Crie outra propriedade que represente o objeto Detalhes dos itens selecionados.

A seção Detalhes (no XAML) é vinculativa a esta propriedade Detalhes (no ViewModel).

Toda vez que a coleção de itens selecionados é modificada (evento Setter/CollectionChanged), você também precisa atualizar seu objeto de detalhes (que iteram através das propriedades relevantes e decidam se são do mesmo valor ou não).

Depois que uma propriedade no objeto Detalhes é modificada, você precisa de volta à coleção de itens selecionados e fazer as alterações relevantes nelas.

É isso.

Espero que ajude

Outras dicas

Estou encontrando esse mesmo problema exato.

IsSynchronizedWithCurrentItem = "True" Mantém apenas o CurrentItem em sincronização (o último item que você selecionou sem manter Ctrl ou Shift).

Assim como você provavelmente tem, eu procurei na Internet por toda parte por uma resposta e nunca criei uma. Meu cenário tem um mestre de 3 níveis> Detalhe> encadernação detalhada, onde cada camada está vinculada à sua própria caixa de listagem.

Eu montei algo que funciona por enquanto.

Para meu mestre> Detalhe> Níveis de detalhe, criei uma coleção individual para cada camada e defino esse coleçãoViewSource.Source para o objeto de entidade apropriado. No evento de seleção do MasterView BoundBox, executei um filtro no MasterView.View para recuperar objetos onde a chave primária mestre = detalhe a chave estrangeira.

É desleixado, mas se você encontrou uma maneira melhor de fazer isso, eu adoraria ouvi -lo.

Eu usei uma abordagem semelhante a o sugerido pelo capitão. Eu criei uma propriedade no meu ViewModel que representa vários itens (ou seja, todos os itens selecionados). Na propriedade Get e Accessores de Conjunto, usei os seguintes métodos para determinar/definir o valores compartilhados de/para todos os itens. Essa abordagem não usa nenhuma reflexão, mas usa delegados na forma de expressões Lambda, o que a torna muito rápida.

Como uma visão geral, este é o meu design básico:

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)

}

As propriedades são assim:

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

E o código para o SharedValueHelper A classe se parece com a seguinte:

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

}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top