#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>

Global $nCurCol = -1
Global $nSortDir = 1
Global $bSet = 0
Global $nCol = -1

Example1()
Example2()

; *******************************************************
; Пример 1 - сортировка 3-х различных колонок
; *******************************************************
Func Example1()
	Local $lv, $msg

	GUICreate("Тест 1", 300, 200)

	$lv = GUICtrlCreateListView("Колонка1|Кол2|Кол3", 10, 10, 280, 180)
	GUICtrlRegisterListViewSort(-1, "LVSort") ; Регистрация функции обратного вызова сортировки "LVSort"

	GUICtrlCreateListViewItem("ABC|666|10.05.2004", $lv)
	GUICtrlSetImage(-1, "shell32.dll", 7)
	GUICtrlCreateListViewItem("DEF|444|11.05.2005", $lv)
	GUICtrlSetImage(-1, "shell32.dll", 12)
	GUICtrlCreateListViewItem("CDE|444|12.05.2004", $lv)
	GUICtrlSetImage(-1, "shell32.dll", 3)

	GUISetState()

	While 1
		$msg = GUIGetMsg()
		Switch $msg
			Case $GUI_EVENT_CLOSE
				ExitLoop

			Case $lv
				$bSet = 0
				$nCurCol = $nCol
				GUICtrlSendMsg($lv, $LVM_SETSELECTEDCOLUMN, GUICtrlGetState($lv), 0)
				DllCall("user32.dll", "int", "InvalidateRect", "hwnd", GUICtrlGetHandle($lv), "int", 0, "int", 1)
		EndSwitch
	WEnd

	GUIDelete()
EndFunc

; Наша функция обратного вызова сортировки
Func LVSort($hWnd, $nItem1, $nItem2, $nColumn)
	Local $val1, $val2, $nResult

	; Переключает направление сортировки (по убыванию или по возрастанию)
	If $nColumn = $nCurCol Then
		If Not $bSet Then
			$nSortDir = $nSortDir * - 1
			$bSet = 1
		EndIf
	Else
		$nSortDir = 1
	EndIf
	$nCol = $nColumn

	$val1 = GetSubItemText($hWnd, $nItem1, $nColumn)
	$val2 = GetSubItemText($hWnd, $nItem2, $nColumn)

	; Если это 3-я колонка (отсчёт колонок от 0), тогда сравнение дат
	If $nColumn = 2 Then
		$val1 = StringRight($val1, 4) & StringMid($val1, 4, 2) & StringLeft($val1, 2)
		$val2 = StringRight($val2, 4) & StringMid($val2, 4, 2) & StringLeft($val2, 2)
	EndIf

	$nResult = 0 ; Не изменять позиции of item1 и item2

	If $val1 < $val2 Then
		$nResult = -1 ; Переместить item2 перед item1
	ElseIf $val1 > $val2 Then
		$nResult = 1 ; Переместить item2 после item1
	EndIf

	$nResult = $nResult * $nSortDir

	Return $nResult
EndFunc

; Возвращает текст пункта ListView указанной колонки
Func GetSubItemText($nCtrlID, $nItemID, $nColumn)
	Local $stLvfi = DllStructCreate("uint;ptr;int;int[2];int")
	Local $nIndex, $stBuffer, $stLvi, $sItemText

	DllStructSetData($stLvfi, 1, $LVFI_PARAM) ; Поиск пункта нашего сохранённого индекса
	DllStructSetData($stLvfi, 3, $nItemID)

	$stBuffer = DllStructCreate("char[260]")

	$nIndex = GUICtrlSendMsg($nCtrlID, $LVM_FINDITEM, -1, DllStructGetPtr($stLvfi));

	$stLvi = DllStructCreate("uint;int;int;uint;uint;ptr;int;int;int;int")

	DllStructSetData($stLvi, 1, $LVIF_TEXT)
	DllStructSetData($stLvi, 2, $nIndex)
	DllStructSetData($stLvi, 3, $nColumn)
	DllStructSetData($stLvi, 6, DllStructGetPtr($stBuffer))
	DllStructSetData($stLvi, 7, 260)

	GUICtrlSendMsg($nCtrlID, $LVM_GETITEMA, 0, DllStructGetPtr($stLvi));

	$sItemText = DllStructGetData($stBuffer, 1)

	$stLvi = 0
	$stLvfi = 0
	$stBuffer = 0

	Return $sItemText
EndFunc

; *******************************************************
; Пример 2 - сортировка с самосозданием пунктов функцией DllCall
; *******************************************************
Func Example2()
	Local $hGUI, $lv, $msg

	$nCurCol = -1
	$nSortDir = 1
	$bSet = 0
	$nCol = -1

	$hGUI = GUICreate("Тест 2", 300, 200)

	$lv = GUICtrlCreateListView("Колонка1|Кол2|Кол3", 10, 10, 280, 180)
	GUICtrlRegisterListViewSort(-1, "LVSort2") ; Регистрация функции обратного вызова сортировки "LVSort2"

	MyGUICtrlCreateListViewItem("ABC|666|10.05.2004", $lv, -1)
	MyGUICtrlCreateListViewItem("DEF|444|11.05.2005", $lv, -1)
	MyGUICtrlCreateListViewItem("CDE|444|12.05.2004", $lv, -1)

	GUISetState()

	While 1
		$msg = GUIGetMsg()
		Switch $msg
			Case $GUI_EVENT_CLOSE
				ExitLoop

			Case $lv
				$bSet = 0
				$nCurCol = $nCol
				GUICtrlSendMsg($lv, $LVM_SETSELECTEDCOLUMN, GUICtrlGetState($lv), 0)
				DllCall("user32.dll", "int", "InvalidateRect", "hwnd", ControlGetHandle($hGUI, "", $lv), "int", 0, "int", 1)
		EndSwitch
	WEnd
EndFunc

; Наша функция обратного вызова сортировки
Func LVSort2($hWnd, $nItem1, $nItem2, $nColumn)
	Local $val1, $val2, $nResult

	; Переключает направление сортировки (по убыванию или по возрастанию)
	If $nColumn = $nCurCol Then
		If Not $bSet Then
			$nSortDir = $nSortDir * - 1
			$bSet = 1
		EndIf
	Else
		$nSortDir = 1
	EndIf
	$nCol = $nColumn

	$val1 = GetSubItemText($hWnd, $nItem1, $nColumn)
	$val2 = GetSubItemText($hWnd, $nItem2, $nColumn)

	; Если это 3-я колонка (отсчёт колонок от 0), тогда сравнение дат
	If $nColumn = 2 Then
		$val1 = StringRight($val1, 4) & StringMid($val1, 4, 2) & StringLeft($val1, 2)
		$val2 = StringRight($val2, 4) & StringMid($val2, 4, 2) & StringLeft($val2, 2)
	EndIf

	$nResult = 0 ; Не изменять позиции of item1 и item2

	If $val1 < $val2 Then
		$nResult = -1 ; Переместить item2 перед item1
	ElseIf $val1 > $val2 Then
		$nResult = 1 ; Переместить item2 после item1
	EndIf

	$nResult = $nResult * $nSortDir

	Return $nResult
EndFunc

; Создаёт и вставляет пункт в ListView
Func MyGUICtrlCreateListViewItem($sText, $nCtrlID, $nIndex)
	Local $stLvItem = DllStructCreate("uint;int;int;uint;uint;ptr;int;int;int;int;")
	Local $stText = DllStructCreate("char[260]")
	Local $arText = StringSplit($sText, "|")

	If $nIndex = -1 Then $nIndex = GUICtrlSendMsg($nCtrlID, $LVM_GETITEMCOUNT, 0, 0)

	DllStructSetData($stText, 1, $arText[1]) ; Сохраняет текст пункта в структуру

	DllStructSetData($stLvItem, 1, BitOR($LVIF_TEXT, $LVIF_PARAM))
	DllStructSetData($stLvItem, 2, $nIndex)
	DllStructSetData($stLvItem, 6, DllStructGetPtr($stText))
	; Устанавливает lParam структуры в индекс строки - уникальный в ListView
	DllStructSetData($stLvItem, 9, $nIndex)

	$nIndex = GUICtrlSendMsg($nCtrlID, $LVM_INSERTITEMA, 0, DllStructGetPtr($stLvItem))

	If $nIndex > -1 Then
		; Вставляет остальную часть текста колонок
		For $i = 2 To $arText[0]
			DllStructSetData($stText, 1, $arText[$i])
			DllStructSetData($stLvItem, 3, $i - 1) ; Устанавливает индекс подпункта

			GUICtrlSendMsg($nCtrlID, $LVM_SETITEMTEXTA, $nIndex, DllStructGetPtr($stLvItem))
		Next
	EndIf

	$stText = 0
	$stLvItem = 0

	; Изменяет ширину колонки в соответствии с текстом пункта
	For $i = 0 To 2
		GUICtrlSendMsg($nCtrlID, $LVM_SETCOLUMNWIDTH, $i, -1)
		GUICtrlSendMsg($nCtrlID, $LVM_SETCOLUMNWIDTH, $i, -2)
	Next
EndFunc