Leaderboard
Popular Content
Showing content with the highest reputation on 12/02/2015 in all areas
-
Multi-line items in custom drawn ListView
pixelsearch reacted to LarsJ for a topic
Items in a standard listview in details/report view (this example deals only with listviews in details/report view) can display a single line of text. There seems not to be any options to change this. There is no word wrap option. If you search the forums, it's possible to find examples of listviews with multiple lines of text in each row. The multi-line items are implemented as owner drawn items through LVS_OWNERDRAWFIXED control style and WM_DRAWITEM messages. A problem with the owner drawn technique is that you are forced to draw everything yourself. Besides item texts and the background behind texts (white for non-selected items, dark blue for selected items with focus, button face for selected items without focus) you also have to draw checkboxes, images, icons and the background behind these elements yourself. Another technique is custom drawn listview items. Custom drawn items are implemented through NM_CUSTOMDRAW notifications included in WM_NOTIFY messages. NM_CUSTOMDRAW notifications are generated automatically by the code in ComCtl32.dll when the listview is updated. Implementing custom drawn items is a matter of responding to these messages or not. The great advantage of custom drawn items is that the drawing process is divided into several stages. For a listview up to six different stages. Some of these stages can be used for default drawing without any additional code at all. Other stages can be used for custom drawing with your own code. Multi-line text items fits perfectly with the custom drawn technique. Item texts and the background is drawn by custom code. Checkboxes, images, icons and the background is drawn by default code. Increase height of listview items The usual way to increase the height of listview items is to respond to WM_MEASUREITEM messages. But this method can only be used for owner drawn listviews. In a custom drawn listview the height can be increased by defining a text font with a suitable height: Func _GUICtrlListView_SetItemHeightByFont( $hListView, $iHeight ) ; Get font of ListView control ; Copied from _GUICtrlGetFont example by KaFu ; See https://www.autoitscript.com/forum/index.php?showtopic=124526 Local $hDC = _WinAPI_GetDC( $hListView ), $hFont = _SendMessage( $hListView, $WM_GETFONT ) Local $hObject = _WinAPI_SelectObject( $hDC, $hFont ), $lvLOGFONT = DllStructCreate( $tagLOGFONT ) _WinAPI_GetObject( $hFont, DllStructGetSize( $lvLOGFONT ), DllStructGetPtr( $lvLOGFONT ) ) Local $hLVfont = _WinAPI_CreateFontIndirect( $lvLOGFONT ) ; Original ListView font _WinAPI_SelectObject( $hDC, $hObject ) _WinAPI_ReleaseDC( $hListView, $hDC ) _WinAPI_DeleteObject( $hFont ) ; Set height of ListView items by applying text font with suitable height $hFont = _WinAPI_CreateFont( $iHeight, 0 ) _WinAPI_SetFont( $hListView, $hFont ) _WinAPI_DeleteObject( $hFont ) ; Restore font of Header control Local $hHeader = _GUICtrlListView_GetHeader( $hListView ) If $hHeader Then _WinAPI_SetFont( $hHeader, $hLVfont ) ; Return original ListView font Return $hLVfont EndFunc Large images will also increase the height of listview items. See example E. Height of listview If the height of the listview does not fit an integer number of rows, you can see empty space below last row in the bottom of the listview. This issue is exacerbated by tall items. The following function is used to calculate the height of the listview to match a given number of rows: Func _GUICtrlListView_GetHeightToFitRows( $hListView, $iRows ) ; Get height of Header control Local $tRect = _WinAPI_GetClientRect( $hListView ) Local $hHeader = _GUICtrlListView_GetHeader( $hListView ) Local $tWindowPos = _GUICtrlHeader_Layout( $hHeader, $tRect ) Local $iHdrHeight = DllStructGetData( $tWindowPos , "CY" ) ; Get height of ListView item 0 (item 0 must exist) Local $aItemRect = _GUICtrlListView_GetItemRect( $hListView, 0, 0 ) ; Return height of ListView to fit $iRows items ; Including Header height and 8 pixels of additional room Return ( $aItemRect[3] - $aItemRect[1] ) * $iRows + $iHdrHeight + 8 EndFunc The calculation includes the height of the header. This means that the function works for a multi-line header with tall items (example A and B). Reference example WM_NOTIFY messages and NM_CUSTOMDRAW notifications are send to the parent of the listview control. The parent is the AutoIt GUI and messages can be handled by a function registered with GUIRegisterMsg. Example 1 is a reference example which shows the different stages of the custom drawing process. None of the stages contains any code except for ConsoleWrite statements. This is code for the reference example: #include <GUIConstants.au3> #include <GuiListView.au3> #include "GuiListViewEx.au3" Opt( "MustDeclareVars", 1 ) Global $hGui, $idListView, $hListView, $fListViewHasFocus = 0, $iItems = 3, $bAutoItMsgLoop = False Example() Func Example() ; Create GUI $hGui = GUICreate( "Custom draw stages", 420, 200 ) ; Create ListView $idListView = GUICtrlCreateListView( "", 10, 10, 400, 180, $GUI_SS_DEFAULT_LISTVIEW-$LVS_SINGLESEL, $WS_EX_CLIENTEDGE+$LVS_EX_FULLROWSELECT+$LVS_EX_GRIDLINES ) $hListView = GUICtrlGetHandle( $idListView ) ; Add columns to ListView _GUICtrlListView_AddColumn( $hListView, "Column 1", 94 ) _GUICtrlListView_AddColumn( $hListView, "Column 2", 94 ) _GUICtrlListView_AddColumn( $hListView, "Column 3", 94 ) _GUICtrlListView_AddColumn( $hListView, "Column 4", 94 ) ; Fill ListView For $i = 0 To $iItems - 1 GUICtrlCreateListViewItem( $i & "/Column 1|" & $i & "/Column 2|" & $i & "/Column 3|" & $i & "/Column 4", $idListView ) Next ; Adjust height of GUI and ListView to fit ten rows Local $iLvHeight = _GUICtrlListView_GetHeightToFitRows( $hListView, 10 ) WinMove( $hGui, "", Default, Default, Default, WinGetPos( $hGui )[3] - WinGetClientSize( $hGui )[1] + $iLvHeight + 20 ) WinMove( $hListView, "", Default, Default, Default, $iLvHeight ) ; Register WM_NOTIFY message handler ; To handle NM_CUSTOMDRAW notifications ; And to check when ListView receives/loses focus GUIRegisterMsg( $WM_NOTIFY, "WM_NOTIFY" ) ; Register WM_ACTIVATE message handler ; If GUI loses focus selected listview items are drawn with a button face background color. ; To check when GUI receives/loses focus ; When GUI receives focus selected items are redrawn with the dark blue background color. GUIRegisterMsg( $WM_ACTIVATE, "WM_ACTIVATE" ) ; Detection of received focus is faster through the WM_ACTIVATE message than directly ; through the listview. This provides a faster and smoother redraw of selected items. ; Show GUI GUISetState( @SW_SHOW ) ; Message loop While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE ExitLoop EndSwitch If Not $bAutoItMsgLoop Then _ ; We want to see only one message at a time $bAutoItMsgLoop = ( ConsoleWrite( "AutoIt message loop <<<<<<<<<<<<<<<<<<<<<" & @CRLF ) > 0 ) WEnd ; Cleanup GUIDelete() EndFunc ; WM_NOTIFY message handler 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 $hListView Switch $iCode Case $NM_CUSTOMDRAW $bAutoItMsgLoop = False Local $tNMLVCustomDraw = DllStructCreate( $tagNMLVCUSTOMDRAW, $lParam ) Local $dwDrawStage = DllStructGetData( $tNMLVCustomDraw, "dwDrawStage" ) Switch $dwDrawStage ; Specifies the drawing stage ; Stage 1 Case $CDDS_PREPAINT ; Before the paint cycle begins ConsoleWrite( "Stage 1: CDDS_PREPAINT" & @CRLF ) Return $CDRF_NOTIFYITEMDRAW + _ ; Stage 2 will be carried out $CDRF_NOTIFYPOSTPAINT ; Stage 6 will be carried out Return $CDRF_NOTIFYITEMDRAW ; Notify the parent window before an item is painted Return $CDRF_NOTIFYPOSTPAINT ; Notify the parent window after the paint cycle is complete ; Stage 2 Case $CDDS_ITEMPREPAINT ; Before an item is painted ConsoleWrite( "Stage 2: CDDS_ITEMPREPAINT" & @CRLF ) Return $CDRF_NOTIFYSUBITEMDRAW + _ ; Stage 3 will be carried out $CDRF_NOTIFYPOSTPAINT ; Stage 5 will be carried out Return $CDRF_NOTIFYSUBITEMDRAW ; Notify the parent window before a subitem is painted Return $CDRF_NOTIFYPOSTPAINT ; Notify the parent window after an item is painted ; Stage 3 Case BitOR( $CDDS_ITEMPREPAINT, _ $CDDS_SUBITEM ) ; Before a subitem is painted ConsoleWrite( "Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM" & @CRLF ) Return $CDRF_NOTIFYPOSTPAINT ; Stage 4 will be carried out Return $CDRF_NOTIFYPOSTPAINT ; Notify the parent window after a subitem is painted ; Stage 4 Case BitOR( $CDDS_ITEMPOSTPAINT, _ $CDDS_SUBITEM ) ; After a subitem has been painted ConsoleWrite( "Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM" & @CRLF ) ; Stage 5 Case $CDDS_ITEMPOSTPAINT ; After an item has been painted ConsoleWrite( "Stage 5: CDDS_ITEMPOSTPAINT" & @CRLF ) ; Stage 6 Case $CDDS_POSTPAINT ; After the paint cycle is complete ConsoleWrite( "Stage 6: CDDS_POSTPAINT" & @CRLF ) EndSwitch Case $NM_KILLFOCUS If $fListViewHasFocus Then GUICtrlSendMsg( $idListView, $LVM_REDRAWITEMS, 0, $iItems - 1 ) $fListViewHasFocus = 0 EndIf Case $NM_SETFOCUS If Not $fListViewHasFocus Then _ GUICtrlSendMsg( $idListView, $LVM_REDRAWITEMS, 0, $iItems - 1 ) $fListViewHasFocus = 2 EndSwitch EndSwitch Return $GUI_RUNDEFMSG EndFunc ; WM_ACTIVATE message handler Func WM_ACTIVATE( $hWnd, $iMsg, $wParam, $lParam ) #forceref $iMsg, $lParam If $hWnd = $hGui Then _ $fListViewHasFocus = BitAND( $wParam, 0xFFFF ) ? 1 : 0 Return $GUI_RUNDEFMSG EndFunc Code is added to check when GUI and listview receives and loses focus. This is important in the other examples. Output in SciTE console immediately after example is opened: Stage 1: CDDS_PREPAINT Stage 2: CDDS_ITEMPREPAINT Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 5: CDDS_ITEMPOSTPAINT Stage 2: CDDS_ITEMPREPAINT Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 5: CDDS_ITEMPOSTPAINT Stage 2: CDDS_ITEMPREPAINT Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 3: CDDS_ITEMPREPAINT, CDDS_SUBITEM Stage 4: CDDS_ITEMPOSTPAINT, CDDS_SUBITEM Stage 5: CDDS_ITEMPOSTPAINT Stage 6: CDDS_POSTPAINT AutoIt message loop <<<<<<<<<<<<<<<<<<<<< Note that the entire custom draw process from stage 1 to 6 is not interrupted by the AutoIt message loop. The other examples are all based on the reference example. Examples This is common to all examples. First line in item texts is stored directly in the listview. Additional lines are stored in a global array named $aLines. Index in the array is item ID as returned by GUICtrlCreateListViewItem and stored in ItemParam internally in listview memory. Item texts and background is drawn with custom code. Other item elements and background is drawn with default code. In all examples LVS_SINGLESEL style is removed to be able to select multiple items. This is a picture of example E: Example 2, 3 and 4 are simple examples. Example 5 and 6 deals with subitem icons and colors. Example 7 about listview notifications shows a way to catch double click and Enter key. A dummy control is used to forward the double click event to AutoIt main message loop to avoid lengthy or blocking code in WM_NOTIFY function. Example 8 and 9 shows how to respond to header notifications and how to rearrange columns by dragging header items with the mouse. In both examples LVS_EX_HEADERDRAGDROP extended style is added to the listview. When columns are rearranged, header item index and listview subitem index is always the same independent of column position, while header item order changes depending on column position. Because the header is a child control of the listview, the listview must be subclassed to catch header notifications. Subclassing is implemented with the four functions SetWindowSubclass, GetWindowSubclass, RemoveWindowSubclass and DefSubclassProc (all implemented in WinAPIShellEx.au3). Since we are subclassing a header control contained in a listview this issue must be taking into account. Note that the subclass callback function is only running while the primary mouse button is pressed on the header. This means no performance impact on the listview eg. when you are dragging the scroll bar. This is important for a custom drawn listview. Quite a lot of extra code is added (most easily seen in example 9) to fix an issue due to column 0 and other columns have different left margins. When first column is moved to another position there is a white gap between columns for selected rows (Windows XP), or the text is painted too close to the left edge of the item (Windows 7). The problem is seen in the picture to the right where the two first columns are swapped: LVS_EX_HEADERDRAGDROP style is only used in example 8 and 9. Usage of a multi-line header is demonstrated in example A and B. See Custom/owner drawn multi-line header in ListView for more information. Example C shows a method to deal with focus issues when more controls (here just a single button) are added to the GUI. When the listview has focus selected items are drawn with the dark blue background color. The problem arises if the listview and GUI loses focus eg. to Calculator. When focus is lost selected items are drawn with the button face background color. If GUI receives focus again by clicking the button (and not the listview), selected items are first very briefly redrawn with the dark blue background color (it seems like a blink) and then with the correct button face background color. To avoid this issue a hidden label control is added to the GUI. Immediately before the GUI loses focus, focus is moved from the listview to the label. In example D items are added with the commands _GUICtrlListView_AddItem and _GUICtrlListView_AddSubItem. In all examples an array is used to store the multi-line item texts. The array contains all lines except the first line which is stored directly in the listview. This example shows how to manually store array row index in ItemParam when items are added with _GUICtrlListView_AddItem and _GUICtrlListView_AddSubItem. Inspiration for example E about large images in first column (see picture above) comes from this thread. In example E the images are used to increase the height of the listview items instead of a text font. Because it's large 128x128 pixel images there is plenty of room in subitems in second and third column. In all examples 15 lines of code is used to repaint the first line item text. The text that was painted by default code in middle of the item is first deleted by filling the item with the background color. Then the text is extracted from the listview and repainted in top of item. This code can be avoided by storing all text lines in the array. This is demonstrated in example F. This makes the custom draw code faster. Performance considerations In a custom drawn (or owner drawn or virtual) listview performance considerations are important because the custom drawing (or owner drawing or data display) is performed by AutoIt code. In a normal listview drawing and data display is performed by compiled C++ code in ComCtl32.dll. Lengthy and slow code in NM_CUSTOMDRAW Case statements (or WM_DRAWITEM functions or LVN_GETDISPINFO Case statements) should be avoided. Perform as many calculations as possible before the repetitive and fast executions of these code blocks. Use static variables to avoid repeating the same calculation again and again. Executing a function directly with DllCall or GUICtrlSendMsg is faster than executing the same function through an implementation in an UDF. Simple GDI functions are faster than more advanced GDI+ functions. Use different drawing stages to optimize custom drawing. The CDDS_PREPAINT stage is only performed once for the entire drawing process. The CDDS_ITEMPREPAINT stage is performed once per item. The stage given by BitOR( CDDS_ITEMPREPAINT, CDDS_SUBITEM ) is performed once per subitem including subitem 0. Default drawing should be used as much as possible, because the code is running in ComCtl32.dll. In a listview the time it takes to update all visible rows is proportional to the number of visible rows. Reducing the height of the list view and thus the number of visible rows improves performance. Especially in a custom drawn (or owner drawn or virtual) listview. ListviewMultilineItems.7z 1) Custom draw stages.au3 2) Two-line listview items.au3 3) Three-line listview items.au3 4) First column checkbox and icon.au3 5) Check boxes and icons.au3 6) Background and text colors.au3 7) ListView notifications.au3 8) Header notifications.au3 9) Rearrange columns.au3 A) Multi-line header 1.au3 A) Multi-line header 2.au3 C) Button control.au3 D) _GUICtrlListView_AddItem.au3 E) Large images in first column.au3 F) Storing all lines in array.au3 GuiHeaderEx.au3 GuiListViewEx.au3 ListViewCustomDraw.au3 Images\ 8 images for example E You need AutoIt 3.3.10 or later. Tested on Windows 7 32/64 bit and Windows XP 32 bit. Comments are welcome. Let me know if there are any issues. (Set tab width = 2 in SciTE to line up comments by column.) ListviewMultilineItems.7z1 point -
1 point
-
I think I will provide a solution to get the filtered rows in a single array and add that to the Excel wiki.1 point
-
Send("{CTRLDOWN}") MouseClick("right") Send("{CTRLUP}") A Little google goes a long way1 point
-
@DiveHigh, Welcome to the AutoIt forum. Unfortunately you appear to have missed the Forum rules on your way in. (there is also a link link in my signature) Please read them now particularly the bit about not discussing game automation - and then you will understand why you will get no help and this thread will now be locked. See you soon with a legitimate question I hope. Jos1 point
-
The problem is that after using the SpecialCells method, you are returned a Range of non-continuous areas, which you can't use the Rows property on. $oRange = $oWorkbook.ActiveSheet.Usedrange.SpecialCells($xlCellTypeVisible) Local $iRowCount = 0 For $oArea In $oRange.Areas $iRowCount = $iRowCount + $oArea.Rows.Count Next ConsoleWrite($iRowCount & @CRLF)1 point
-
Function _AD_GetPasswordInfo will return the password info. Function _AD_GetObjectProperties(@username, "accountExpires") will return the user account information.1 point
-
Here is efect universal solution for all edit controls in GUI #include <GUIConstantsEx.au3> #include <GUIEdit.au3> #include <WinAPI.au3> $hGUI = GUICreate("Test", 500, 500) $hInput1 = GUICtrlCreateEdit("here is some text 1", 10, 10, 480, 240) $hInput2 = GUICtrlCreateEdit("here is some text 2", 10, 250, 480, 240) ; Create dummy for accelerator key to activate $hSelAll = GUICtrlCreateDummy() GUISetState() ; Set accelerators for Ctrl+a Dim $AccelKeys[1][2]=[["^a", $hSelAll]] GUISetAccelerators($AccelKeys) While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE Exit Case $hSelAll _SelAll() EndSwitch WEnd Func _SelAll() $hWnd = _WinAPI_GetFocus() $class = _WinAPI_GetClassName($hWnd) If $class = 'Edit' Then _GUICtrlEdit_SetSel($hWnd, 0, -1) EndFunc ;==>_SelAll1 point