Eu tenho uma aula a seguir:
[DataContract]
public class Pair<TKey, TValue> : INotifyPropertyChanged, IDisposable
{
public Pair(TKey key, TValue value)
{
Key = key;
Value = value;
}
#region Properties
[DataMember]
public TKey Key
{
get
{ return m_key; }
set
{
m_key = value;
OnPropertyChanged("Key");
}
}
[DataMember]
public TValue Value
{
get { return m_value; }
set
{
m_value = value;
OnPropertyChanged("Value");
}
}
#endregion
#region Fields
private TKey m_key;
private TValue m_value;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
#region IDisposable Members
public void Dispose()
{ }
#endregion
}
Que coloquei em um ObservableCollection:
ObservableCollection<Pair<ushort, string>> my_collection =
new ObservableCollection<Pair<ushort, string>>();
my_collection.Add(new Pair(7, "aaa"));
my_collection.Add(new Pair(3, "xey"));
my_collection.Add(new Pair(6, "fty"));
P: Como faço para classificar por chave?
Respostas:
Classificar um observável e retornar o mesmo objeto classificado pode ser feito usando um método de extensão. Para coleções maiores, esteja atento ao número de notificações alteradas na coleção.
Eu atualizei meu código para melhorar o desempenho e lidar com duplicatas (obrigado a nawfal por destacar o desempenho ruim do original, embora tenha funcionado bem no exemplo de dados original). O observável é particionado em uma metade classificada à esquerda e uma metade não classificada à direita, onde cada vez que o item mínimo (conforme encontrado na lista classificada) é deslocado para o final da partição classificada a partir do não classificado. Pior caso O (n). Essencialmente, uma classificação de seleção (veja a saída abaixo).
uso: amostra com um observador (usei uma classe Person para mantê-lo simples)
Detalhes do andamento da classificação, mostrando como a coleção é dinamizada:
A classe Person implementa IComparable e IEquatable; o último é usado para minimizar as mudanças na coleção de modo a reduzir o número de notificações de mudança levantadas
Para retornar um ObservableCollection, chame .ToObservableCollection em * SortOC * usando, por exemplo, [esta implementação] [1].
**** resposta original - isso cria uma nova coleção **** Você pode usar linq como o método doSort abaixo ilustra. Um rápido snippet de código: produz
3: xey 6: fty 7: aaa
Alternativamente, você pode usar um método de extensão na própria coleção
fonte
ObservableCollection
, mas cria uma nova coleção.BinarySearch
vez deIndexOf
.Esta extensão simples funcionou perfeitamente para mim. Eu só tinha que ter certeza de que
MyObject
eraIComparable
. Quando o método de classificação é chamado na coleção observável deMyObjects
, oCompareTo
método emMyObject
é chamado, o que chama meu método de classificação lógica. Embora não tenha todos os sinos e assobios do resto das respostas postadas aqui, é exatamente o que eu precisava.fonte
return Utils.LogicalStringCompare(a.Title, b.Title);
vez dereturn string.Compare(a.Title, b.Title);
? @NeilWEncontrei uma entrada de blog relevante que fornece uma resposta melhor do que as aqui:
http://kiwigis.blogspot.com/2010/03/how-to-sort-obversablecollection.html
ATUALIZAR
O ObservableSortedList que @romkyns aponta nos comentários mantém a ordem de classificação automaticamente.
No entanto, observe também a observação
fonte
Você pode usar este método simples:
Você pode classificar assim:
Mais detalhes: http://jaider.net/2011-05-04/sort-a-observablecollection/
fonte
ObservableCollection
vincula a ItemSource dos menus suspensos e não vê a coleção de forma alguma. Além disso, esta operação de limpeza e enchimento é ultra rápida ... a "lenta" pode ser do tipo que já está otimizada. finalmente, você pode modificar este código para implementar seu método de movimento, tendo osortedlist
esource
o resto é fácil.Move
eventos, isso também apenas para os realmente movidos.O WPF fornece classificação ao vivo pronta para uso usando a
ListCollectionView
classe ...Depois que a inicialização for concluída, não há mais nada a fazer. A vantagem sobre a classificação passiva é que ListCollectionView faz todo o trabalho pesado de uma forma transparente para o desenvolvedor. Novos itens são colocados automaticamente em sua ordem de classificação correta. Qualquer classe que derive
IComparer
de T é adequada para a propriedade de classificação personalizada.Consulte ListCollectionView para obter a documentação e outros recursos.
fonte
Eu gostei da abordagem do método de extensão de classificação por bolha no blog de "Richie" acima, mas não quero necessariamente apenas classificar comparando o objeto inteiro. Com mais frequência, desejo classificar por uma propriedade específica do objeto. Então, eu o modifiquei para aceitar um seletor de chave da maneira que OrderBy faz, para que você possa escolher em qual propriedade classificar:
Que você chamaria da mesma maneira que chamaria OrderBy, exceto que classificará a instância existente de sua ObservableCollection em vez de retornar uma nova coleção:
fonte
OrderBy
e, em seguida, fazer uma comparação para descobrir a mudança real.A resposta de @NielW é o caminho a percorrer, para uma classificação real no local. Eu queria adicionar uma solução ligeiramente alterada que permite que você ignore a necessidade de usar
IComparable
:agora você pode chamá-lo como qualquer método LINQ:
fonte
if(!Ascending) sorted.Reverse();
pouco antes defor
: D (e não há necessidade de se preocupar mais com a memória, esse método Reverse não cria nenhum objeto novo, é o reverso no local)Eu gostaria de acrescentar à resposta de NeilW . Para incorporar um método que se assemelha ao encarregado do serviço. Adicione este método como uma extensão:
E use como:
fonte
Uma variação é quando você classifica a coleção no local usando um algoritmo de classificação de seleção . Os elementos são movidos para o lugar usando o
Move
método. Cada movimento irá disparar oCollectionChanged
evento comNotifyCollectionChangedAction.Move
(e tambémPropertyChanged
com o nome da propriedadeItem[]
).Este algoritmo tem algumas propriedades interessantes:
CollectionChanged
eventos disparados) é quase sempre menor do que outros algoritmos semelhantes, como classificação por inserção e classificação por bolha.O algoritmo é bastante simples. A coleção é iterada para encontrar o menor elemento que é movido para o início da coleção. O processo é repetido começando no segundo elemento e assim por diante até que todos os elementos tenham sido movidos para o lugar. O algoritmo não é terrivelmente eficiente, mas para qualquer coisa que você vá exibir em uma interface de usuário, isso não importa. No entanto, em termos de número de operações de movimentação, é bastante eficiente.
Aqui está um método de extensão que, para simplificar, requer que os elementos sejam implementados
IComparable<T>
. Outras opções estão usando umIComparer<T>
ou umFunc<T, T, Int32>
.Classificar uma coleção é simplesmente uma questão de invocar o método de extensão:
fonte
T
ser capaz de classificar os elementos na coleção. A classificação envolve o conceito de maior e menor e só você pode definir comoProfileObject
é ordenada. Para usar o método de extensão é necessário implementarIComparable<ProfileObject>
noProfileObject
. Outras alternativas são as indicadas, especificando umIComparer<ProfileObject>
ou aFunc<ProfileObject, ProfileObject, int>
e alterando o código de classificação de acordo.Para melhorar um pouco o método de extensão na resposta xr280xr, adicionei um parâmetro bool opcional para determinar se a classificação é decrescente ou não. Também incluí a sugestão do Carlos P no comentário a essa resposta. Por favor veja abaixo.
fonte
Você precisa manter sua coleção organizada o tempo todo? Ao recuperar os pares, você precisa que eles estejam sempre classificados ou é apenas por algumas vezes (talvez apenas para apresentação)? Quão grande você espera que sua coleção seja? Existem muitos fatores que podem ajudá-lo a decidir o método de bruxa a ser usado.
Se você precisa que a coleção seja classificada o tempo todo, mesmo quando você insere ou deleta elementos e a velocidade de inserção não é um problema, talvez você deva implementar algum tipo de
SortedObservableCollection
como @Gerrie Schenck mencionou ou verificar esta implementação .Se você precisa que sua coleção seja classificada apenas algumas vezes, use:
Isso levará algum tempo para classificar a coleção, mas mesmo assim, pode ser a melhor solução dependendo do que você está fazendo com ela.
fonte
Minha resposta atual já tem mais votos, mas encontrei uma maneira melhor e mais moderna de fazer isso.
fonte
Faça uma nova classe
SortedObservableCollection
, derive-aObservableCollection
e implemente-aIComparable<Pair<ushort, string>>
.fonte
Uma maneira seria convertê-lo em uma lista e, em seguida, chamar Sort (), fornecendo um delegado de comparação. Algo como:-
(não testado)
fonte
Eu acho que esta é a solução mais elegante:
http://www.xamlplayground.org/post/2009/07/18/Use-CollectionViewSource-effectively-in-MVVM-applications.aspx
fonte
Que diabos, vou dar uma resposta rapidamente remendada também ... parece um pouco com algumas outras implementações aqui, mas vou adicioná-la a qualquer um:
(mal testei, espero não estar me envergonhando)
Vamos estabelecer alguns objetivos primeiro (minhas suposições):
1) Deve classificar
ObservableCollection<T>
no local, para manter notificações, etc.2) Não deve ser terrivelmente ineficiente (ou seja, algo próximo da eficiência de classificação "boa" padrão)
fonte
Nenhuma dessas respostas funcionou no meu caso. Ou porque atrapalha a ligação ou requer tantos códigos adicionais que é uma espécie de pesadelo, ou a resposta é simplesmente quebrada. Então, aqui está outra resposta mais simples que pensei. É muito menos código e permanece a mesma coleção observável com um tipo de método this.sort adicional. Deixe-me saber se há algum motivo pelo qual eu não deveria estar fazendo isso (eficiência, etc.).
... Onde ScoutItem é minha classe pública. Parecia muito mais simples. Benefício adicionado: ele realmente funciona e não mexe com ligações ou retorna uma nova coleção, etc.
fonte
Certo, como estava tendo problemas para fazer ObservableSortedList funcionar com o XAML, fui em frente e criei SortingObservableCollection . Ele herda de ObservableCollection, por isso funciona com XAML e testei a unidade com 98% de cobertura de código. Eu usei em meus próprios aplicativos, mas não prometo que é livre de bugs. Sinta-se à vontade para contribuir. Aqui está um exemplo de uso de código:
É um PCL, portanto, deve funcionar com Windows Store, Windows Phone e .NET 4.5.1.
fonte
new
em todos esses métodos, se alguém tiver uma instância de tipo mais genérico, esses métodos não serão chamados. Emoverride
vez disso, cada método substituível e altere-os conforme necessário ou faça fallbackbase.Method(...)
. Você, por exemplo, nem precisa se preocupar.Add
porque isso usa internamente.InsertItem
, então se.InsertItem
for sobrescrito e ajustado,.Add
não vai atrapalhar o pedido.Isso é o que eu faço com as extensões OC:
fonte
Isso funcionou para mim, encontrei há muito tempo em algum lugar.
Uso:
fonte
Eu precisava ser capaz de classificar por várias coisas, não apenas uma. Essa resposta é baseada em algumas das outras respostas, mas permite uma classificação mais complexa.
Ao usá-lo, faça uma série de chamadas OrderBy / ThenBy. Como isso:
fonte
Aprendi muito com as outras soluções, mas encontrei alguns problemas. Primeiro, alguns dependem do IndexOf, que tende a ser muito lento para listas grandes. Em segundo lugar, minha ObservableCollection tinha entidades EF e o uso de Remove parecia corromper algumas das propriedades de chave estrangeira. Talvez eu esteja fazendo algo errado.
Independentemente disso, A Move pode ser usado em vez de Remover / Inserir, mas isso causa alguns problemas com a correção de desempenho.
Para corrigir o problema de desempenho, crio um dicionário com os valores classificados IndexOf. Para manter o dicionário atualizado e preservar as propriedades da entidade, use uma troca implementada com dois movimentos em vez de um conforme implementado em outras soluções.
Um único movimento desloca os índices dos elementos entre os locais, o que invalidaria o dicionário IndexOf. Adicionar um segundo movimento para implementar uma troca restaura os locais.
fonte
fonte