Сортировка в ListView

Язык Visual Basic на платформе .NET.

Модераторы: Ramzes, Sebas

Oldman
Обычный пользователь
Обычный пользователь
 
Сообщения: 56
Зарегистрирован: 10.11.2003 (Пн) 12:23
Откуда: ТРТУ

Сортировка в ListView

Сообщение Oldman » 15.03.2005 (Вт) 15:11

Как организовать сортировку в ListView, чтобы порядок сортировки был вида:
1,2,3,4,10,20,100,1000 и т.д.
А то стандартыми методами удается отсортировать так:
1,10,100,1000,2,20,3,4.
Ежики не колются, только бухают иногда

hCORe
VB - Экстремал
VB - Экстремал
Аватара пользователя
 
Сообщения: 2332
Зарегистрирован: 22.02.2003 (Сб) 15:21
Откуда: parent directory

Сообщение hCORe » 15.03.2005 (Вт) 15:15

Напрашивается "некрасивое решение" - перед сортировкой добавить ведущие нули. Сделать это, не используя тормозную функцию Format можно вот так (для чисел!):
Код: Выделить всё
Public Function FastFMT(Expression As Double, _
LeadDigits As Byte) As String
On Error Resume Next
    Dim strTmp As String
    Dim lngTmp As Long
   
    If LeadDigits = 1 Then _
    FastFMT = CStr(Expression): Exit Function
    lngTmp = 10 ^ (LeadDigits - 1)
   
    If Expression >= lngTmp Then _
    FastFMT = CStr(Expression): Exit Function
   
    FastFMT = String(LeadDigits - _
    Len(CStr(Expression)), "0") & CStr(Expression)
       
End Function
Моду создают модоки, а распространяют модозвоны.

Oldman
Обычный пользователь
Обычный пользователь
 
Сообщения: 56
Зарегистрирован: 10.11.2003 (Пн) 12:23
Откуда: ТРТУ

Сообщение Oldman » 15.03.2005 (Вт) 15:24

Просто до этого я использовал MSHFlexGrid и там все правильно сортировалось...
Но по некоторым причинам пришлось переползти на ListView и началось...
А переползти пришлось после переустановки Винды, когда был установлен только .Net, а VB6 не поставили. .Net отказался работать с проектом, сказав, что MSHFlexGrid - неизвестный объект... :(
Ежики не колются, только бухают иногда

Andrey Fedorov
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
 
Сообщения: 3287
Зарегистрирован: 21.05.2004 (Пт) 9:28
Откуда: Москва

Сообщение Andrey Fedorov » 15.03.2005 (Вт) 16:19

Сортировку в ListView делать можно как угодно (хоть по нескольким колонкам одновременно) - с помощью API.

Попробуй скачать отсюда:

http://www.devcity.net/page.asp?p=afl

делал давно, но сортировка там должна быть - в проекте примера.

Впрочем, вот прямая ссылка для скачивания:

http://www.vbcity.com/download/AFileLib.zip
Фиг Вам! - Сказал Чебурашка, обгладывая Крокодила Гену...

Oldman
Обычный пользователь
Обычный пользователь
 
Сообщения: 56
Зарегистрирован: 10.11.2003 (Пн) 12:23
Откуда: ТРТУ

Сообщение Oldman » 20.03.2005 (Вс) 23:40

Огромное спасибо за пример... Все более менее понятно, но как быстро данный алгоритм сортировки будет с таблицей примерно в 18-20 тыс. записей?
:oops:
Ежики не колются, только бухают иногда

Andrey Fedorov
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
 
Сообщения: 3287
Зарегистрирован: 21.05.2004 (Пт) 9:28
Откуда: Москва

Сообщение Andrey Fedorov » 21.03.2005 (Пн) 8:12

Oldman писал(а):Огромное спасибо за пример... Все более менее понятно, но как быстро данный алгоритм сортировки будет с таблицей примерно в 18-20 тыс. записей?
:oops:


Скорей всего будет тормозить. Сам попробуй.
Для таких таблиц все-же лучше использовать нормальный Grid и не мучиться - лично я ListView практически не пользую...
Фиг Вам! - Сказал Чебурашка, обгладывая Крокодила Гену...

Sebas
Неуловимый Джо
Неуловимый Джо
Аватара пользователя
 
Сообщения: 3626
Зарегистрирован: 12.02.2002 (Вт) 17:25
Откуда: столько наглости такие вопросы задавать

Сообщение Sebas » 21.03.2005 (Пн) 11:09

В ListVIew нужно задать сортирующий объект наследуемый от IComparer
- Я никогда не понимал, почему они приходят ко мне чтобы умирать?

sebas<-@->mail.ru

Oldman
Обычный пользователь
Обычный пользователь
 
Сообщения: 56
Зарегистрирован: 10.11.2003 (Пн) 12:23
Откуда: ТРТУ

Сообщение Oldman » 21.03.2005 (Пн) 11:35

Да не могу я нормальный грид найти... :(
А с ЛистВью никак не разберусь :(
Чтобы я ему не передавал для сортировки - он через жопу сортирует :evil:
Ежики не колются, только бухают иногда

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 12.03.2006 (Вс) 2:07

Привет всем.
Опытные программисты и так знают, а новичкам (как мне) пригодиться.

При сортировке набора данных сравниваются два значения. Это ключ к пониманию проблемы поднятой Oldmanом.
Sebasупомянул про обьект наследуемый от IComparer.
И он в принципе прав.
Данные могут быть различных типов(int, long, string, datetime).
В любом алгоритме сортировки мы должны сравнивать два значения одного типа, string со string или long c long.
У Oldmanа в листвью все сортировалось как string. Потому и цифры сортировались так 1, 11, 17, 2, 23, 3, 41, а не 1, 2, 3, 11, 17, 23, 41.
Для правильной сортировки процедура сортировки должна знать с каким типом данных она работает.
Если к примеру в листвью в одной колнке даны цыфры-номер клиента, в другой текст - имя клиента, в третьей дата - дата заказа, то в первом случае сравниваются два обьекта приведенные к типу long, во втором к типу string, в третьем к типу datetime. И для этого мы передаем в функцию сортировки данные как обьекты, а там в алгоритме сортировки для сравнения данных используем функцию comparer, которая осуществляет кастинг обьектов перед их сравнением.
Вот пример ( извините что на C#, но смысл понятен)
Код: Выделить всё
using System;
using System.Collections;

namespace DataGegevens
{
   /// <summary>
   /// Summary description for clData.
   /// </summary>
   ///

   public struct gegevens
   {
      public int nr;
      public long volgnr;
      public string naam;
      public string voornaam;
      public DateTime gebdat;
      
   }
   
   public class VglNr: IComparer
   {
      int IComparer.Compare(object var1, object var2)
      {
         if(var2.GetType()== typeof(gegevens))
            return ((gegevens)var1).nr-((gegevens)var2).nr;
         else
            return ((gegevens)var1).nr-(int)var2;
      }
   }

   public class VglNaam: IComparer
   {
      int IComparer.Compare(object var1, object var2)
      {
         if(var2.GetType()== typeof(gegevens))      
            return string.Compare(((gegevens)var1).naam,((gegevens)var2).naam);         
         else      
            return string.Compare(((gegevens)var1).naam,(string)var2);
      }
   }

   public class VglVoorNaam: IComparer
   {
      int IComparer.Compare(object var1, object var2)
      {
         if(var2.GetType()== typeof(gegevens))      
            return string.Compare(((gegevens)var1).voornaam,((gegevens)var2).voornaam);         
         else      
            return string.Compare(((gegevens)var1).voornaam,(string)var2);
      }
   }

   public class VglGebDat: IComparer
   {
      int IComparer.Compare(object var1, object var2)
      {
         if(var2.GetType()== typeof(gegevens))      
            return DateTime.Compare(((gegevens)var1).gebdat,((gegevens)var2).gebdat);         
         else      
            return DateTime.Compare(((gegevens)var1).gebdat,(DateTime)var2);
      }
   }

   public class VglVolgNr: IComparer
   {
      int IComparer.Compare(object var1, object var2)
      {
         if(var2.GetType()== typeof(gegevens))
            return (int)(((gegevens)var1).volgnr-((gegevens)var2).volgnr);
         else
            return (int)(((gegevens)var1).nr-(long)var2);
      }
   }
}

Это код содержащий структуру с номером, именем, фамилией и датой рождения (на форме эти данные отображаются в листвью) и несколько классов с методами comparer каждый для сравнения того или иного типа данных.

На форме мы вызываем сортировку так

Код: Выделить всё
private void bttnSorteer_Click(object sender, System.EventArgs e)
      {
         dataTabel.Sort(myVgl);
         tonen();
      }

где dataTabel это Array или ArrayList в зависимости что задекларировать.
tonen() это вызов процедуры заполнения листвью.
myVlg декларируем как private IComparer myVgl;
и определяем методом
Код: Выделить всё
switch(kol)
         {
            case 0:
               myVgl= new VglNr();
               break;
            case 1:
               myVgl=new VglVolgNr();
               break;
            case 2:
               myVgl=new VglNaam();
               break;
            case 3:
               myVgl=new VglVoorNaam();
               break;
            case 4:
               myVgl=new VglGebDat();
               break;
в зависимости по какой колонке в листвью кликнули.
И тогда цыферки будут сортироваться как цыферки, фамилиии как текст, а даты как даты.

ЗЫ в ближайшее время перепишу этот учебный пример с C# на VB.NET и выложу весь проект.

Эту же прпоблему я увидел в примере с готдотнет, вопрос по которому я задавал здесь http://bbs.vbstreets.ru/viewtopic.php?t=23657

Вот теперь сам понял почему там сортировалось так же как и у Oldmanа. Дату они определяли, а остальное сортировали как string.

VAngel
Обычный пользователь
Обычный пользователь
Аватара пользователя
 
Сообщения: 81
Зарегистрирован: 13.01.2005 (Чт) 0:10
Откуда: 2:5030

Сообщение VAngel » 12.03.2006 (Вс) 9:59

Недавно на форуме поднимался вопрос с книгами, так в одной и из книг как раз расматривался вопрос сортировки, с определением типов данных и их сортировки в listview, незнаю правда насколько но будет быстро работать...

только кодов у меня сейчас нет, они осталиь на работе, если интересно то я скину?

Кстати книга называлась: разработка элементов microsoft .net на visual basic .net

Thomas
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 246
Зарегистрирован: 12.11.2005 (Сб) 0:17
Откуда: "Сказочное королевство"

Сообщение Thomas » 13.03.2006 (Пн) 0:39

Приветствую всех.
Книга которую упомянул VAngel называется
Коннелл Джон "Разработка элементов управления Microsoft.NET на Microsoft Visual Basic.NET"
Ниже код примера предложенного Джоном.
Код формы
Код: Выделить всё
Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer. 
    'Do not modify it using the code editor.
    Friend WithEvents lvListView As System.Windows.Forms.ListView
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.lvListView = New System.Windows.Forms.ListView
        Me.SuspendLayout()
        '
        'lvListView
        '
        Me.lvListView.Location = New System.Drawing.Point(8, 8)
        Me.lvListView.Name = "lvListView"
        Me.lvListView.Size = New System.Drawing.Size(272, 248)
        Me.lvListView.TabIndex = 0
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(292, 273)
        Me.Controls.Add(Me.lvListView)
        Me.Name = "Form1"
        Me.Text = "Sorting a ListView"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Dim sPoe As String = "The thousand injuries of Fortunate " & "I had borne as I best could; but when he ventured " & "upon insult, I vowed revenge"
    'Каждое слово строки помещается в массив.
    Dim aPoe() As String = sPoe.Split(" ")

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Dim iColumn As Integer
        Dim iLength As Integer = aPoe.Length - 1
        Dim item1 As ListViewItem
        Dim dDate As Date = Today
        Dim sDate As String
        buildListView()
        For iColumn = 0 To iLength
            item1 = New ListViewItem(iColumn * 22)
            item1.SubItems.Add(dDate.AddDays(iColumn * 47))
            item1.SubItems.Add(aPoe(iColumn))
            lvListView.Items.Add(item1)
        Next
    End Sub

    Private Sub buildListView()
        With lvListView
            ' Очистка списка.
            .Items.Clear()
            ' Установка вида "таблица".
            .View = View.Details
            ' Это свойство позволяет менять столбцы местами.
            .AllowColumnReorder = True
            ' При выборе элемента вместе с ним будут выделяться и его подэлементы.
            .FullRowSelect = True
            ' Включить отображение сетки,
            .GridLines = True
            ' Сортировать элементы списка по возрастанию.
            .Sorting = SortOrder.Ascending
            .Columns.Add("Numbers", lvListView.Width \ 3, HorizontalAlignment.Left)
            .Columns.Add("Dates", lvListView.Width \ 3, HorizontalAlignment.Left)
            .Columns.Add("Strings", (lvListView.Width \ 3) - 22, HorizontalAlignment.Left)
        End With
    End Sub

    Private Sub lvListView_ColumnClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ColumnClickEventArgs) Handles lvListView.ColumnClick
        Static bDirection As Boolean = False
        lvListView.ListViewItemSorter = New SortRoutines.CompareListViewItems(e.Column, lvListView)
        If bDirection = False Then
            lvListView.Sorting = SortOrder.Descending
        Else
            lvListView.Sorting = SortOrder.Ascending
        End If
        lvListView.Sort()
        bDirection = Not bDirection
    End Sub
End Class


А далее код класса
Код: Выделить всё

Public Class SortRoutines
    Public Class CompareListViewItems
        Implements IComparer
        Shared bSortDirection As Boolean = True 'обьявлена как шаред чтобы сохранить свое значение между вызовами класса
        Dim sRoutineToUse As String = "String" 'определяет тип сортировки - текст, номер, дата.
        Dim LView As ListView 'обрабатываемый элемент управления
        Dim iColumn As Integer 'сортируемый столбец
        Dim lvCollection As ListView.ListViewItemCollection
        Dim bSafeToSort As Boolean = False

        Sub New(ByVal itemlndex As Integer, ByVal lvListView As ListView)
            iColumn = itemlndex
            LView = lvListView
            bSortDirection = Not bSortDirection
            lvCollection = New ListView.ListViewItemCollection(lvListView)
            determineType()
        End Sub
        Private Function determineType() As Boolean
            'Чтобы сортировка имела смысл, должно быть хотя бы 2 элемента.
            If (LView.Items.Count < 2) Then
                bSafeToSort = False
                Return False
            End If
            'Если значение равно Null, подэлемент, наверное, не добавлен.
            If (checkForNull() = True) Then
                bSafeToSort = False
                Return False
            End If
            'Если все элементы имеют значения, надо определить их тип.
            If (isItADate() = True) Then
                sRoutineToUse = "Date"
            ElseIf (isItANumber() = True) Then
                sRoutineToUse = "Number"
            Else
                sRoutineToUse = "String"
            End If
        End Function
        Private Function checkForNull() As Boolean
            Dim lvTest As ListViewItem
            Dim sItemContents As String
            For Each lvTest In lvCollection
                Try
                    If iColumn = 0 Then
                        sItemContents = lvTest.Text
                    Else
                        sItemContents = lvTest.SubItems(iColumn).Text
                    End If
                Catch
                    Return True
                End Try
            Next
            bSafeToSort = True
            Return False 'все в порядке
        End Function
        Private Function isItADate() As Boolean
            Dim lvTest As ListViewItem 'объект для просмотра элементов
            Dim oObjectToTest As Object 'объект для проверки типа
            Dim sItemContents As String 'содержание элемента
            'Просмотр всех элементов.
            For Each lvTest In lvCollection
                If iColumn = 0 Then
                    sItemContents = lvTest.Text
                Else
                    sItemContents = lvTest.SubItems(iColumn).Text
                End If
                Try
                    oObjectToTest = CDate(sItemContents)
                Catch
                    Return False
                End Try
            Next
            Return True
        End Function
        Private Function isItANumber() As Boolean
            Dim lvTest As ListViewItem 'объект для просмотра элементов
            Dim oObjectToTest As Object 'объект для проверки типа
            Dim sItemContents As String 'содержание элемента
            ' Просмотр всех элементов.
            For Each lvTest In lvCollection
                If iColumn = 0 Then
                    sItemContents = lvTest.Text
                Else
                    sItemContents = lvTest.SubItems(iColumn).Text
                End If
                Try
                    oObjectToTest = CDbl(sItemContents)
                Catch
                    Return False
                End Try
            Next
            Return True
        End Function
        Function compare(ByVal oFirst As Object, ByVal oSecond As Object) As Integer Implements System.Collections.IComparer.Compare
            If bSafeToSort = False Then Exit Function
            Dim lvElement1 As ListViewItem = CType(oFirst, ListViewItem)
            Dim lvElement2 As ListViewItem = CType(oSecond, ListViewItem)
            If sRoutineToUse = "String" Then
                If bSortDirection = True Then
                    If iColumn = 0 Then
                        Return String.Compare(lvElement1.Text, lvElement2.Text)
                    Else
                        Return String.Compare(lvElement1.SubItems(iColumn).Text, lvElement2.SubItems(iColumn).Text)
                    End If
                Else
                    If iColumn = 0 Then
                        Return String.Compare(lvElement2.Text, lvElement1.Text)
                    Else
                        Return String.Compare(lvElement2.SubItems(iColumn).Text, lvElement1.SubItems(iColumn).Text)
                    End If
                End If
            ElseIf sRoutineToUse = "Number" Then
                If bSortDirection = True Then
                    If iColumn = 0 Then
                        Return Math.Sign(CLng(lvElement1.Text - lvElement2.Text))
                    Else
                        Return Math.Sign(CLng(lvElement1.SubItems(iColumn).Text - lvElement2.SubItems(iColumn).Text))
                    End If
                Else
                    If iColumn = 0 Then
                        Return Math.Sign(CLng(lvElement2.Text - lvElement1.Text))
                    Else
                        Return Math.Sign(CLng(lvElement2.SubItems(iColumn).Text - lvElement1.SubItems(iColumn).Text))
                    End If
                End If
            Else 'Это дата
                If bSortDirection = True Then
                    If iColumn = 0 Then
                        Return Date.Compare(Date.Parse(lvElement1.Text), Date.Parse(lvElement2.Text))
                    Else
                        Return Date.Compare(Date.Parse(lvElement1.SubItems(iColumn).Text), Date.Parse(lvElement2.SubItems(iColumn).Text))
                    End If
                Else
                    If iColumn = 0 Then
                        Return Date.Compare(Date.Parse(lvElement2.Text), Date.Parse(lvElement1.Text))
                    Else
                        Return Date.Compare(Date.Parse(lvElement2.SubItems(iColumn).Text), Date.Parse(lvElement1.SubItems(iColumn).Text))
                    End If
                End If
            End If
        End Function
    End Class
End Class


На VS 2003 работает.

volbel
Начинающий
Начинающий
 
Сообщения: 1
Зарегистрирован: 08.08.2006 (Вт) 3:45
Откуда: Moscow

Проверял на VS2005, тоже работает. А то всё перерыл, только

Сообщение volbel » 09.08.2006 (Ср) 3:42

Проверял на VS2005, тоже работает. А то всё перерыл, только здесь нашёл. Спасибо!!!

kibernetics
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 945
Зарегистрирован: 03.05.2006 (Ср) 13:31
Откуда: Minsk

Сообщение kibernetics » 18.01.2007 (Чт) 17:13

а можно ли приведённый выше код Thomas'a както реализовать на VB6.0?


Вернуться в Visual Basic .NET

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 37

    TopList