Что нового

Элементы GUI Выравнивание содержимого и заголовков (текста шапки) GUIListView по разным краям

fewayax

Новичок
Сообщения
3
Репутация
0
При создании GUIListView сделать стандартными средствами так, чтобы выравнивание заголовка отличалось от выравнивания содержимого невозможно.
(Сразу хочу предупредить - далее в тексте не рассматривается выравнивание заголовка по центру. То есть для выравнивания по центру дальнейшие высказывания где-то рабочие, где-то не рабочие, в где-то необходима доработка).

А зачастую необходимо в разных колонках сделать выравнивание к разным краям (текстовую информацию к левому краю, числовую - к правому).
Можно сделать выравнивание одновременно и заголовка и содержимого. Но лично мне не нравится полученный таким образом вид таблицы (хочется как проводнике Windows - все заголовки прижаты к левому краю)

Снимок1.PNG


Выравнивание можно выполнить тремя функциями.

Код:
;вариант 1
_GUICtrlListView_JustifyColumn($ListView1, 1, 1)
;вариант 2
_GUICtrlHeader_SetItemFormat(_GUICtrlListView_GetHeader($ListView1), 1, BitOR($HDF_RIGHT, $HDF_STRING)) ;
;вариант 3 (примечание - по факту функция _GUICtrlHeader_SetItemAlign вызывает _GUICtrlHeader_SetItemFormat)
_GUICtrlHeader_SetItemAlign(_GUICtrlListView_GetHeader($ListView1), 1, 1)


В варианте 1 параметром является идентификатор элемента GUIListView (($ListView1), в двух других параметром является дескриптор GUIHeader (_GUICtrlListView_GetHeader($ListView1)). То есть теоретически в первом варианте устанавливаем параметры таблицы, а в оставшихся изменяем параметры заголовка таблицы. Но по факту все варианты изменяют одновременно и выравнивание заголовка и выравнивание записей в таблице.

Если колонка не будет изменять свою ширину, то есть простейший способ сделать выравнивание заголовка - просто добавить необходимое количество пробелов.

Код:
;в колонке Размер сделаем выравнивание по правому краю, а заголовок, при подборе нужного количества пробелов, будет прижат к левому краю
;работает если ширина колонки Размер не будет изменяться
$ListView1 = GUICtrlCreateListView("Файл|Размер|Дата", 0, 20, 600, 200) ; строка для наглядности
$ListView1 = GUICtrlCreateListView("Файл|Размер                            |Дата", 0, 20, 600, 200)


Также есть простой, но не очень элегантный способ выравнивания заголовка влево даже при изменяющемся размере ширины колонки. Просто добавляем большое количество пробелов справа от текста заголовка.
Код:
$ListView1 = GUICtrlCreateListView("Файл|Размер" & StringFormat("%200s", " ") & "|Дата", 0, 20, 600, 200) ;после слова Размер добавлено 200 пробелов

В результате получаем в изображении текста заголовка три точечки (которые указывают что при текущей ширине колонки показан не весь текст заголовка)
Снимок2.PNG


И, наконец, самый сложный вариант. Динамически, в зависимости от размера ширины колонки, формируем текст заголовка добавляя пробелы.
В общем виде

Код:
;$hHead - дескриптор заголовка в котором изменяется текст заголовка
;$iColumnIndex - индекс колонки в которой изменяется текст заголовка
;$iItemWidth - текущая ширина колонки
Func GetHeaderText($hHead, $iColumnIndex, $iItemWidth=0)
    If Not $iItemWidth Then
        $iItemWidth = _GUICtrlHeader_GetItemWidth($hHead, $iColumnIndex)
    EndIf
        
    $iZazor = 12 ;такой должна быть разница между шириной заголовка и шириной столбца (постоянная величина или зависит от конкретного компьютера - не проверял)
    $sHeaderText = StringStripWS(_GUICtrlHeader_GetItemText($hHead, $iColumnIndex), 1 + 2) ;минус пробелы в начале и конце
    $iHeaderTextWidth =  GetStringDimensions($hHead, $sHeaderText)[0]
    If $iHeaderTextWidth < ($iItemWidth - $iZazor) Then 
        $nWidth1space = GetStringDimensions($hHead,"          ")[0] / 10 ;может пробел ВСЕГДА помещается в целое число пикселей - не проверял
        $iKolSpace = Floor(($iItemWidth - $iZazor - $iHeaderTextWidth) / $nWidth1space)
        $sHeaderTextNew = $sHeaderText & StringFormat("%" & $iKolSpace & "s", "") 
    Else 
        $sHeaderTextNew = $sHeaderText
    EndIf
    Return $sHeaderTextNew
EndFunc

;из описания _WinAPI_GetTextExtentPoint32
Func GetStringDimensions($hWnd, $sText)
    Local $hDC = _WinAPI_GetDC($hWnd) ; Возвращает дескриптор контекста устройства указанного окна.
    Local $hFont = _SendMessage($hWnd, $WM_GETFONT) ; Возвращает дескриптор шрифта, используемого для создания текста.
    Local $hSelectObject = _WinAPI_SelectObject($hDC, $hFont) ; Выбирает объект в указанном контекст устройстве.
    Local $tSIZE = _WinAPI_GetTextExtentPoint32($hDC, $sText) ; Вычисляет ширину и высоту указанной строки.

    _WinAPI_SelectObject($hDC, $hSelectObject)
    _WinAPI_ReleaseDC($hWnd, $hDC) ; Освобождает контекст устройства
    Local $aReturn[2] = [DllStructGetData($tSIZE, 1), DllStructGetData($tSIZE, 2)] ; Объявляет массив с шириной и высотой строки.
    Return $aReturn
EndFunc   ;==>GetStringDimensions




Пример

Код:
#include <GUIConstantsEx.au3>
#include <ListViewConstants.au3>
#include <WindowsConstants.au3>

#include <File.au3>
#include <GuiListView.au3>

;#include <GuiHeader.au3>
;#include <Array.au3>
#include <WinAPIConv.au3>
#include <WinAPI.au3>


$Form1 = GUICreate("Form1", 550, 250, 500,-1, $WS_THICKFRAME)
$ListView1 = GUICtrlCreateListView("Файл|Размер|Дата", 0, 20, 600, 200, -1, $LVS_EX_GRIDLINES + $LVS_EX_HEADERDRAGDROP + $LVS_EX_FULLROWSELECT) ; 

GUICtrlSendMsg(-1, $LVM_SETCOLUMNWIDTH, 0, 300)
GUICtrlSendMsg(-1, $LVM_SETCOLUMNWIDTH, 1, 100)
GUICtrlSendMsg(-1, $LVM_SETCOLUMNWIDTH, 2, 120)
_GUICtrlListView_JustifyColumn($ListView1, 1, 1) ;в колонке с индексом 1 выравнивание по правому краю

Global $hHeader = _GUICtrlListView_GetHeader($ListView1)
Global $aColumnContr[2] = [1, 0]    ;индекс колонки заголовок которой перемещаем и ширина колонки до изменения

_GUICtrlHeader_SetItemText($hHeader, $aColumnContr[0], GetHeaderText($hHeader, $aColumnContr[0]))

;заполняем таблицу (достаточно небольшого количества элементов)
$aFiles = _FileListToArray(@WindowsDir & "\System32", '*', 1, 1)
#include <Math.au3>
$iMax = _Min ( 7, $aFiles[0] )
For $i = 1 To $iMax
    FileToGUIListView($aFiles[$i], $ListView1)
Next


GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
GUISetState(@SW_SHOW)

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch
WEnd


;На основании хелпера по _GUICtrlHeader_Create (в т.ч. русскоязычного)
;Отслеживание сообщений что "что атрибуты пункта Header изменились" и на основании этого добавляем
;необходимое количество пробелов в текст заголовка
Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam
    
    Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    Local $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    Local $iCode = DllStructGetData($tNMHDR, "Code")
    Switch $hWndFrom
        Case $hHeader    ;уведомление о событиях связанных с заголовками $ListView1
            Switch $iCode

#cs
                ;Уведомление высылается элементом Header родительскому окну о том, что атрибуты пункта Header собираются измениться
                Case $HDN_ITEMCHANGING, $HDN_ITEMCHANGINGW ; Notifies a header control's parent window that the attributes of a header item are about to change
                        _WM_NOTIFY_DebugEvent("$HDN_ITEMCHANGING", $tagNMHEADER, $lParam, "IDFrom,,Item,Button") ;#include <Extras\WM_NOTIFY.au3>
                        Return False ; To allow the changes
                        ; Return True  ; To prevent them

#ce
                ; Уведомление высылается элементом Header родительскому окну о том, что атрибуты пункта Header изменились
                Case $HDN_ITEMCHANGED, $HDN_ITEMCHANGEDW ; Notifies a header control's parent window that the attributes of a header item have changed
                        ;_WM_NOTIFY_DebugEvent("$HDN_ITEMCHANGED", $tagNMHEADER, $lParam, "IDFrom,,Item,Button");#include <Extras\WM_NOTIFY.au3>
                        ; no return value
                            
                        $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam)
                        If DllStructGetData($tNMHEADER, "Item") =  $aColumnContr[0] Then ;изменяется контролируемая нами колонка
                            $iHeaderItemWidth = _GUICtrlHeader_GetItemWidth($hHeader, $aColumnContr[0])
                            If $iHeaderItemWidth <> $aColumnContr[1] Then ;меняем только если ширина колонки изменилась
                                $aColumnContr[1] = $iHeaderItemWidth 
                                
                                _GUICtrlHeader_SetItemText($hHeader, $aColumnContr[0], GetHeaderText($hHeader, $aColumnContr[0], $iHeaderItemWidth))
                            EndIf
                        EndIf
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY


Func GetHeaderText($hHead, $iColumnIndex, $iItemWidth=0)
    If Not $iItemWidth Then
        $iItemWidth = _GUICtrlHeader_GetItemWidth($hHead, $iColumnIndex)
    EndIf
        
    $iZazor = 12 ;такой должна быть разница между шириной заголовка и шириной столбца (постоянная величина или зависит от конкретного компьютера - не проверял)
    $sHeaderText = StringStripWS(_GUICtrlHeader_GetItemText($hHead, $iColumnIndex), 1 + 2) ;минус пробелы в начале и конце
    $iHeaderTextWidth =  GetStringDimensions($hHead, $sHeaderText)[0]
    If $iHeaderTextWidth < ($iItemWidth - $iZazor) Then 
        $nWidth1space = GetStringDimensions($hHead,"          ")[0] / 10 ;может пробел ВСЕГДА помещается в целое число пикселей - не проверял
        $iKolSpace = Floor(($iItemWidth - $iZazor - $iHeaderTextWidth) / $nWidth1space)
        $sHeaderTextNew = $sHeaderText & StringFormat("%" & $iKolSpace & "s", "") 
    Else 
        $sHeaderTextNew = $sHeaderText
    EndIf
    Return $sHeaderTextNew
EndFunc

;из описания _WinAPI_GetTextExtentPoint32
Func GetStringDimensions($hWnd, $sText)
    Local $hDC = _WinAPI_GetDC($hWnd) ; Возвращает дескриптор контекста устройства указанного окна.
    Local $hFont = _SendMessage($hWnd, $WM_GETFONT) ; Возвращает дескриптор шрифта, используемого для создания текста.
    Local $hSelectObject = _WinAPI_SelectObject($hDC, $hFont) ; Выбирает объект в указанном контекст устройстве.
    Local $tSIZE = _WinAPI_GetTextExtentPoint32($hDC, $sText) ; Вычисляет ширину и высоту указанной строки.

    _WinAPI_SelectObject($hDC, $hSelectObject)
    _WinAPI_ReleaseDC($hWnd, $hDC) ; Освобождает контекст устройства
    Local $aReturn[2] = [DllStructGetData($tSIZE, 1), DllStructGetData($tSIZE, 2)] ; Объявляет массив с шириной и высотой строки.
    Return $aReturn
EndFunc   ;==>GetStringDimensions

Func FileToGUIListView($FullFileName, $pListView)
    $t = FileGetTime($FullFileName)
    $FileDateTime = $t[2] & '.' & $t[1] & '.' & $t[0] & " " & $t[3] & ':' & $t[4] & ':' & $t[5]
    $sSize = _WinAPI_StrFormatKBSize(FileGetSize($FullFileName))
    GUICtrlCreateListViewItem($FullFileName & "|" & $sSize & "|" & $FileDateTime, $pListView)
EndFunc
 

Norm

Продвинутый
Сообщения
279
Репутация
74
Aльтернативный костыль.

Код:
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiListView.au3>
#include <GuiScrollBars.au3>
#include <WinAPISysWin.au3>

Global $aHdrText[3] = ["Col-0", "Col-1", "Col-2"]
Global $vSortSense_Full = True, $vSortSense_Item = True
Global Const $HDN_ITEMCHANGINGA = $HDN_FIRST - 0, $iDLLUser32 = DllOpen("user32.dll")
Global $wProcNewLV, $wProcOldLV, $wProcNewLB, $wProcOldLB

$hGUI = GUICreate("ListView", 500, 225)
$cLv_Header = GUICtrlCreateListView("", 10, 10, 480, 25)
$hLV_Header = GUICtrlGetHandle($cLv_Header)
$cLV_Items = GUICtrlCreateListView("", 10, 34, 480, 175, $LVS_NOCOLUMNHEADER)
$hLV_Items = GUICtrlGetHandle($cLV_Items)
_GUICtrlListView_SetExtendedListViewStyle($hLV_Items, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_GRIDLINES, $LVS_EX_DOUBLEBUFFER))

For $i = 0 To 2
    _GUICtrlListView_AddColumn($cLv_Header, $aHdrText[$i], 150, $i ? (($i = 1) ? 2 : 0) : 1)
    _GUICtrlListView_AddColumn($cLV_Items, $aHdrText[$i], 150, $i ? (($i = 1) ? 1 : 2) : 0)
Next
For $i = 0 To 4
    $sData = ""
    For $j = 0 To 2
        $sData &= "Item " & $i & "-" & Random(0, 100, 1) & "|"
    Next
    GUICtrlCreateListViewItem(StringTrimRight($sData, 1), $cLV_Items)
Next

GUIRegisterMsg($WM_NOTIFY, "_WM_NOTIFY")
GUISetState()
$wProcNewLV = DllCallbackRegister("_LVWndProc", "ptr", "hwnd;uint;wparam;lparam")
$wProcOldLV = _WinAPI_SetWindowLong($hLV_Header, $GWL_WNDPROC, DllCallbackGetPtr($wProcNewLV))

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
    EndSwitch
WEnd

Func _WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam
    Local $tNMHEADER = DllStructCreate($tagNMHEADER, $lParam)
    Local $tInfo, $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    Local $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    Switch DllStructGetData(DllStructCreate("int;int;int", $lParam), 3)
        Case $HDN_ITEMCHANGINGA, $HDN_ITEMCHANGINGW
            Local $iCol = DllStructGetData($tNMHEADER, "Item")
            _GUICtrlListView_SetColumnWidth($hLV_Items,$iCol,_GUICtrlListView_GetColumnWidth($hLV_Header,$iCol))
        Case Else
            Switch DllStructGetData($tNMHDR, "Code")
                Case $LVN_COLUMNCLICK
                    $tInfo = DllStructCreate($tagNMLISTVIEW, $lParam)
                    Switch $hWndFrom
                        Case $hLV_Header
                            _GUICtrlListView_SimpleSort($hLV_Items, $vSortSense_Item, DllStructGetData($tInfo, "SubItem"))
                    EndSwitch
                Case $LVN_ENDSCROLL
                    $tInfo = DllStructCreate($tagNMLVSCROLL, $lParam)
                    _GUICtrlListView_Scroll($hLV_Header, DllStructGetData($tInfo, "DX"), DllStructGetData($tInfo, "DY"))
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_NOTIFY

Func _LVWndProc($hWnd, $Msg, $wParam, $lParam)
    #forceref $hWnd, $Msg, $wParam, $lParam
    Switch $Msg
        Case $WM_NCCALCSIZE
            _RemoveVHScroll($hWnd)
        Case $WM_WINDOWPOSCHANGING
            Local $tWINDOWPOS = DllStructCreate($tagWINDOWPOS, $lParam)
            ;uncomment for mouse scrolling - no mouse scrolling without, but border is not erased (vertical key scrolling flickers visibly))
            DllStructSetData($tWINDOWPOS, "Flags", _
            BitAND(DllStructGetData($tWINDOWPOS, "Flags"), BitNOT($SWP_FRAMECHANGED)))
    EndSwitch
    ;pass the unhandled messages to default WindowProc
    Local $aResult = DllCall($iDLLUser32, "lresult", "CallWindowProcW", "ptr", _
    $wProcOldLV, "hwnd", $hWnd, "uint", $Msg, "wparam", $wParam, "lparam", $lParam)
    If @error Then Return -1
    Return $aResult[0]
EndFunc   ;==>_LVWndProc

Func _LBWndProc($hWnd, $Msg, $wParam, $lParam)
    #forceref $hWnd, $Msg, $wParam, $lParam
    Switch $Msg
        Case $WM_NCCALCSIZE
            _RemoveVHScroll($hWnd) ;listview will add these styles again when restored from minimize
        Case $WM_WINDOWPOSCHANGING
            Local $tWINDOWPOS = DllStructCreate($tagWINDOWPOS, $lParam)
            DllStructSetData($tWINDOWPOS, "Flags", _
            BitAND(DllStructGetData($tWINDOWPOS, "Flags"), BitNOT($SWP_FRAMECHANGED)))
    EndSwitch
    ;pass the unhandled messages to default WindowProc
    Local $aResult = DllCall($iDLLUser32, "lresult", "CallWindowProcW", "ptr", _
    $wProcOldLB, "hwnd", $hWnd, "uint", $Msg, "wparam", $wParam, "lparam", $lParam)
    If @error Then Return -1
    Return $aResult[0]
EndFunc   ;==>_LBWndProc

Func _RemoveVHScroll($hWnd)
    Local $iLVStyle = _WinAPI_GetWindowLong($hWnd, $GWL_STYLE)
    $iLVStyle = BitAND($iLVStyle, BitNOT($WS_VSCROLL))
    $iLVStyle = BitAND($iLVStyle, BitNOT($WS_HSCROLL))
    _WinAPI_SetWindowLong($hWnd, $GWL_STYLE, $iLVStyle)
EndFunc

Вот тоже вариант в виде библиотеки

iwuKiJd.png
 
Последнее редактирование:
Автор
F

fewayax

Новичок
Сообщения
3
Репутация
0

Я в Вашем коде сделал форму с изменяющимися размерами
Код:
$hGUI = GUICreate("ListView", 500, 225)
GUISetStyle($WS_OVERLAPPEDWINDOW)


При увеличении высоты формы заголовок "отстыковывается" от таблицы, а при уменьшении заголовок уменьшает высоту.
Насколько я понял, AutoIt стандартными функциями не поддерживает привязку визуальных элементов на форме относительно друг друга. Поэтому чтобы элементы не плавали необходимо либо делать форму в которой пользователь не может менять размеры либо делать костыль для привязки таблицы к заголовку.
Уточнение для начинающих (если они будут это читать) - возможность изменения ширины столбцов и изменение размеров формы это различающиеся сущности. Поэтому можно сделать форму с неизменяющимися размерами, но в которой у GUIListView можно будет менять ширину столбцов.


Не понял наличие функции Func _LBWndProc. Она случайно оказалась? Если нет, то где происходит ее вызов?

Подскажите для чего в Func _WM_NOTIFY
Код:
Case $LVN_ENDSCROLL
                    $tInfo = DllStructCreate($tagNMLVSCROLL, $lParam)
                    _GUICtrlListView_Scroll($hLV_Header, DllStructGetData($tInfo, "DX"), DllStructGetData($tInfo, "DY"))


И в Func _LVWndProc
Код:
Case $WM_WINDOWPOSCHANGING
            Local $tWINDOWPOS = DllStructCreate($tagWINDOWPOS, $lParam)
            ;uncomment for mouse scrolling - no mouse scrolling without, but border is not erased (vertical key scrolling flickers visibly))
            DllStructSetData($tWINDOWPOS, "Flags", _
            BitAND(DllStructGetData($tWINDOWPOS, "Flags"), BitNOT($SWP_FRAMECHANGED)))


Закомментировал эти части кода - не увидел разницы в поведении.
 

Norm

Продвинутый
Сообщения
279
Репутация
74
При увеличении высоты формы заголовок "отстыковывается" от таблицы, а при уменьшении заголовок уменьшает высоту.
(Сразу хочу предупредить - далее в тексте не рассматривается выравнивание заголовка по центру. То есть для выравнивания по центру дальнейшие высказывания где-то рабочие, где-то не рабочие, в где-то необходима доработка).
Если кому-то именно такой вариант подойдёт, который может выравнивать текст заголовок не только по левому и правому краю, но и по центру, то и изменение размеров сможет тоже отловить и подправить.

Подскажите для чего в Func _WM_NOTIFY
Что бы при скролинге, по горизонтали, заголовок тоже перемещался.

Не понял наличие функции Func _LBWndProc. Она случайно оказалась? Если нет, то где происходит ее вызов?
Код:
$wProcNewLV = DllCallbackRegister("_LVWndProc", "ptr", "hwnd;uint;wparam;lparam")
$wProcOldLV = _WinAPI_SetWindowLong($hLV_Header, $GWL_WNDPROC, DllCallbackGetPtr($wProcNewLV))

Закомментировал эти части кода - не увидел разницы в поведении.
Всё это тоже костыль, для удаления скроллов в заголовке.

P.S.
Спасибо за вопросы, я тоже заметил в вашем коде неиспользуемые функции.
 
Последнее редактирование:
Верх