You should be sorting in descending order as we did with the
CardDeck
. We need to add a
SorterDescending
class:
Public Class CardSorterDescending
Implements IComparer(Of Card)
Public Sub New(Optional sortBy As CardSortType = CardSortType.SuitThenKind)
Me.SortBy = sortBy
End Sub
Property SortBy As CardSortType
Public Function Compare(x As Card, y As Card) As Integer Implements IComparer(Of Card).Compare
Select Case Me.SortBy
Case CardSortType.KindOnly
Return y.Kind.CompareTo(x.Kind)
Case CardSortType.SuitOnly
Return y.Suit.CompareTo(x.Suit)
Case CardSortType.SuitThenKind
If x.Suit <> y.Suit Then
Return y.Suit.CompareTo(x.Suit)
End If
Return y.Kind.CompareTo(x.Kind)
Case CardSortType.KindThenSuit
If y.Kind <> y.Kind Then
Return y.Kind.CompareTo(x.Kind)
End If
Return y.Suit.CompareTo(y.Suit)
Case Else
Throw New NotImplementedException($"CardOrderMethod {SortBy} is not implemented.")
End Select
End Function
End Class
As there are a number of common methods, including the
Cards
collection, we can create a
CardsBase
abstract class (
DRY principle - Don't Repeat Yourself):
Imports System.Collections.ObjectModel
Imports System.Collections.Specialized
Public MustInherit Class CardsBase : Inherits ObservableObject : Implements IDisposable
Public Sub New()
Cards = New ObservableCollection(Of Card)
AddHandler Cards.CollectionChanged, AddressOf CardCollectionChanged
Reset()
End Sub
Public ReadOnly Property Cards As ObservableCollection(Of Card)
Public ReadOnly Property Count As Integer
Get
Return Cards.Count
End Get
End Property
Private Sub CardCollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
OnPropertyChanged(NameOf(Count))
End Sub
Public Overridable Sub Reset()
Cards.Clear()
End Sub
Public Sub Sort()
Dim sorted = New List(Of Card)(Cards.ToList())
sorted.Sort()
ReorderCards(sorted)
End Sub
Public Sub SortDescending()
Dim sorted = New List(Of Card)(Cards.ToList())
sorted.Sort(New CardSorterDescending)
ReorderCards(sorted)
End Sub
Public Sub Sort(comparer As IComparer(Of Card))
Dim sorted = New List(Of Card)(Cards.ToList())
sorted.Sort(comparer)
ReorderCards(sorted)
End Sub
Public Sub SortBy(ByVal Optional sortType As CardSortType? = Nothing)
If sortType Is Nothing Then
Sort()
Else
Sort(New CardSorter With {
.SortBy = CType(sortType, CardSortType)
})
End If
End Sub
Public Sub SortByDescending(ByVal Optional sortType As CardSortType? = Nothing)
If sortType Is Nothing Then
SortDescending()
Else
Sort(New CardSorterDescending With {
.SortBy = CType(sortType, CardSortType)
})
End If
End Sub
Protected Sub ReorderCards(ordered As List(Of Card))
For index As Integer = 0 To ordered.Count - 1
Cards.Move(Cards.IndexOf(ordered(index)), index)
Next
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
RemoveHandler Cards.CollectionChanged, AddressOf CardCollectionChanged
End Sub
End Class
Note: There is now a new
SortDescending
subroutine. This is a default sorter and it uses the new
CardSorterDescending
class.
The updated
CardDeck
class:
Public Class CardDeck : Inherits CardsBase
Public Sub New()
MyBase.New()
Reset()
End Sub
Public Overloads Sub Reset()
MyBase.Reset()
Dim cardCollection = [Enum].GetValues(GetType(Suit)) _
.Cast(Of Suit)() _
.SelectMany(Function(__) [Enum].GetValues(GetType(Kind)) _
.Cast(Of Kind)(),
Function(suit, kind) New With {suit, kind}) _
.Select(Function(card) New Card(card.kind, card.suit))
For Each card As Card In cardCollection
Cards.Add(card)
Next
End Sub
Public Function DrawCardAt(index As Integer) As Card
If index < 0 OrElse index >= Count Then
Throw New ArgumentOutOfRangeException(NameOf(index))
End If
Dim card As Card = Cards(index)
Cards.RemoveAt(index)
Return card
End Function
Public Function DrawTopCard() As Card
Return DrawCardAt(0)
End Function
Public Function DrawBottomCard() As Card
Return DrawCardAt(Count - 1)
End Function
Public Function DrawRandomCard() As Card
Dim random As Random = New Random()
Dim index As Integer = random.[Next](Count)
Return DrawCardAt(index)
End Function
Public Sub AddCardOnTop(card As Card)
If Cards.Contains(card) Then
Throw New InvalidOperationException($"Deck already contains card {card}.")
End If
Cards.Insert(0, card)
End Sub
Public Sub AddCardOnBottom(card As Card)
If Not Cards.Contains(card) Then
Cards.Add(card)
Return
End If
Throw New InvalidOperationException($"Deck already contains card {card}.")
End Sub
Public Sub Shuffle()
ReorderCards(Cards.OrderBy(Function(x) Guid.NewGuid()).ToList())
End Sub
End Class
And the new
Hand
class:
Public Class Hand : Inherits CardsBase
Private nameValue As String
Public Sub New(name As String)
MyBase.New()
Me.Name = name
End Sub
Public Property Name As String
Get
Return nameValue
End Get
Set
SetValue(nameValue, Value)
End Set
End Property
Public Sub AddCard(card As Card)
If Cards.Contains(card) Then
Throw New InvalidOperationException($"Hand already contains card {card}.")
End If
Cards.Add(card)
End Sub
Public Sub RemoveCard(card As Card)
If Not Cards.Contains(card) Then
Throw New InvalidOperationException($"Hand does not contain card {card}.")
End If
Cards.Add(card)
End Sub
End Class
Now if you want to do custom Sorting Ascending/
Descending:
1. The
CardDeck
Deck.SortByDescending(CardSortType.SuitThenKind)
2. The
Hand
Hand.SortByDescending(CardSortType.SuitThenKind)
But to answer your original question, you would use
OrderByDescending
and
ThenByDescending
:
hand.Cards = hand.Cards.OrderByDescending(Function(n) n.Suit).ThenByDescending(Function(n) n.Kind)
If the collection is an
ObservableCollection
,
this won't work as you will break the data binding. You need to use a temporary collection, sort that, clear the
ObserableCollection
, then reload it. I do it slightly differently: Temp collection, sort, then I have a
ReorderCards
subroutine that moves the items to the newly sorted order. See above how I do it (If you add a small delay factor, and animation, you could make the cards move to their new positions. That is a more advanced topic outside the scope of Q&A).