AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Can you move a listview column programmatically?

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Ask for Help
View previous topic :: View next topic  
Author Message
wtg
Guest





PostPosted: Tue Oct 03, 2006 9:41 pm    Post subject: Can you move a listview column programmatically? Reply with quote

I have a need for a checkbox on a listview column other than the first. I've found I can create a listview with the checkbox and then move the column to a different position via my mouse, dragging Col 1 to the 4th column position, for instance. However, I can't find a way to accomplish the same thing programmatically.

Is the only way to accomplish this going to be feeding the GUI an emulated mouse click and drag, or am I missing something obvious?

Sorry if this is covered somewhere. I've read the help file extensively and searched the forums with no luck.
Back to top
PhiLho



Joined: 27 Dec 2005
Posts: 6721
Location: France (near Paris)

PostPosted: Wed Oct 04, 2006 9:44 am    Post subject: Reply with quote

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/listview/messages/lvm_setcolumnorderarray.asp
_________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")
Back to top
View user's profile Send private message Visit poster's website
wtg



Joined: 04 Oct 2006
Posts: 63
Location: Louisville, KY

PostPosted: Wed Oct 04, 2006 6:47 pm    Post subject: LVM_SETCOLUMNORDERARRAY Reply with quote

Thanks for pointing that out to me PhiLho. It's a bit more than I was ready for, but I think I'm getting close.

Can you see what's wrong with my test program below? I've never used SendMessage before (and have only used AHK for about a week) and so I've had to scrounge the help file and the forums to figure out what to do. I'm sure I'm probably making a newbie mistake, but I can't see why this won't reorder column 1 and 2.

Code:


Gui, Add, ListView, grid Checked vTestList, Column 1|Column 2|Column 3
Gui, Add, Button, gSwap, Swap
Gui, Add, Statusbar

Gui, Show, Autosize Center +Resize,Swap Test

lv_Add("", "r1c1", "r1c2", "r1c3")
lv_Add("", "r2c1", "r2c2", "r2c3")
lv_Add("", "r3c1", "r3c2", "r3c3")
Return

   
Swap:   
   cap = 3*4
   VarSetCapacity(ColOrder, cap, 0)
   InsertInteger(1, ColOrder, 0*4)
   InsertInteger(0, ColOrder, 1*4)
   InsertInteger(2, ColOrder, 2*4)
   
   SendMessage, 0x103A, cap, &ColOrder, TestList, ahk_class AutoHotkeyGUI
   MsgBox Errorlevel = %ErrorLevel%
return

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity.  To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
   Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
      DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}

GuiClose:
   ExitApp
Back to top
View user's profile Send private message
PhiLho



Joined: 27 Dec 2005
Posts: 6721
Location: France (near Paris)

PostPosted: Thu Oct 05, 2006 11:04 am    Post subject: Reply with quote

Some errors: wParam is the number of elements in the array, not the size of the buffer in bytes; cap = 3*4 is a classical error, it should be cap := 3*4; AFAIK, SendMessage doesn't take a GUI control variable name (it is not a GUI command), you have to put the ClassNN.

I have made a function out of your code:
Code:
Gui Add, ListView, grid Checked vTestList, C1|C2|C3|C4|C5
Gui Add, Button, gSwap, Swap
Gui Add, Statusbar

Gui Show, Autosize Center +Resize, Swap Test

LV_Add("", "r1c1", "r1c2", "r1c3", "r1c4", "r1c5")
LV_Add("", "r2c1", "r2c2", "r2c3", "r2c4", "r2c5")
LV_Add("", "r3c1", "r3c2", "r3c3", "r3c4", "r3c5")
LV_ModifyCol()
Return


Swap:
   colNb := LV_GetCount("Column")
   Random col1, 1, colNb
   Loop
   {
      Random col2, 1, colNb
      If (col2 != col1)
         Break
   }
   Swap(col1, col2, colNb, 1)
   LV_ModifyCol()
return

; Swap two columns of the list view # _lvID, given by their index, starting at 1
Swap(_col1, _col2, _colNb, _lvID)
{
   local colOrder, pos

   VarSetCapacity(colOrder, _colNb * 4, 0)
   Loop %_colNb%
   {
      pos := A_Index - 1
      If (A_Index = _col1)
         InsertInteger(_col2 - 1, colOrder, pos * 4)
      Else If (A_Index = _col2)
         InsertInteger(_col1 - 1, colOrder, pos * 4)
      Else
         InsertInteger(pos, colOrder, pos * 4)
   }
   SendMessage 0x1000 + 58   ; LVM_SETCOLUMNORDERARRAY
         , _colNb, &colOrder, SysListView32%_lvId%, A
   SendMessage 0x1000 + 21   ; LVM_REDRAWITEMS
         , 0, _colNb - 1, SysListView32%_lvId%, A
}
[EDIT: The ListView needs a redraw message to correctly update. Also added more columns and random swapping for test...]
_________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")
Back to top
View user's profile Send private message Visit poster's website
wtg



Joined: 04 Oct 2006
Posts: 63
Location: Louisville, KY

PostPosted: Thu Oct 05, 2006 1:08 pm    Post subject: Reply with quote

PhiLho,

Thank you so much... you really helped clarify things.

I didn't understand how to provide the handle to the list control. If I understand correctly, it's SysListView321 because this was the first list added to the screen, and the second added would be SysListView322 and so on. Is there a function of any kind, or can one be written, that would return SysListView32n using just the list variable name? I mean, AHK commands that manipulate the list using it's variable name are essentially converting those references to the appropriate SysListView32n reference behind the scenes, right?

The cap assignment problem was something I introduced after the fact cleaning up code to post, and using it as the # of elements in the SendMessage command is a bug I introduced while trying to figure out why my call wasn't working. It's one of those changes that could have taken me another day to figure out once I got the list controls handle right, or may not have gotten them both right at the same time without your help.

By the way, you used the LVM_REDRAWITEMS command to make the columns redraw. Is there an advantage to doing this over a simple GuiControl, +Redraw, TestList?

Thanks again!
Back to top
View user's profile Send private message
PhiLho



Joined: 27 Dec 2005
Posts: 6721
Location: France (near Paris)

PostPosted: Thu Oct 05, 2006 1:37 pm    Post subject: Reply with quote

wtg wrote:
By the way, you used the LVM_REDRAWITEMS command to make the columns redraw. Is there an advantage to doing this over a simple GuiControl, +Redraw, TestList?
Probably no... I just had the nose in MSDN, so I searched there instead of AHK doc. I wouldn't have thought of it anyway, as the doc. presents it as a pair with -Redraw (block updates, then re-allow them).

Mapping a GUI control ClassNN to its variable name is a bit tricky.
One way (the only one?) is to use:
GuiControlGet var, FocusV
so you have to do a GuiControl Focus first (can be done once just after the Gui building).
_________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")
Back to top
View user's profile Send private message Visit poster's website
wtg



Joined: 04 Oct 2006
Posts: 63
Location: Louisville, KY

PostPosted: Thu Oct 05, 2006 2:09 pm    Post subject: Reply with quote

PhiLho wrote:
[Mapping a GUI control ClassNN to its variable name is a bit tricky.
One way (the only one?) is to use:
GuiControlGet var, FocusV
so you have to do a GuiControl Focus first (can be done once just after the Gui building).


Doing some more reading after your suggestion I believe you meant GuiControlGet,var,Focus instead of FocusV. That's really helpful. Since the ListView variable isn't actually used to store a value by the control, one could as a matter of practice use:
Code:

 GuiControl,Focus,MyList
 GuiControlGet,MyList,Focus

right after building the screen, storing the ClassNN value in the control's variable. Seems as good a thing as anything to store in the variable.
Back to top
View user's profile Send private message
PhiLho



Joined: 27 Dec 2005
Posts: 6721
Location: France (near Paris)

PostPosted: Thu Oct 05, 2006 4:19 pm    Post subject: Reply with quote

Yes, I got it the way around... (I shown how to get a variable name, not the ClassNN.) Sorry. but at least it pushed you in the right direction. Smile
_________________
vPhiLho := RegExReplace("Philippe Lhoste", "^(\w{3})\w*\s+\b(\w{3})\w*$", "$1$2")
Back to top
View user's profile Send private message Visit poster's website
adamrgolf



Joined: 28 Dec 2006
Posts: 362

PostPosted: Wed Jun 27, 2007 10:15 am    Post subject: Reply with quote

PhiLho wrote:
Some errors: wParam is the number of elements in the array, not the size of the buffer in bytes; cap = 3*4 is a classical error, it should be cap := 3*4; AFAIK, SendMessage doesn't take a GUI control variable name (it is not a GUI command), you have to put the ClassNN.

I have made a function out of your code:
Code:
Gui Add, ListView, grid Checked vTestList, C1|C2|C3|C4|C5
Gui Add, Button, gSwap, Swap
Gui Add, Statusbar

Gui Show, Autosize Center +Resize, Swap Test

LV_Add("", "r1c1", "r1c2", "r1c3", "r1c4", "r1c5")
LV_Add("", "r2c1", "r2c2", "r2c3", "r2c4", "r2c5")
LV_Add("", "r3c1", "r3c2", "r3c3", "r3c4", "r3c5")
LV_ModifyCol()
Return


Swap:
   colNb := LV_GetCount("Column")
   Random col1, 1, colNb
   Loop
   {
      Random col2, 1, colNb
      If (col2 != col1)
         Break
   }
   Swap(col1, col2, colNb, 1)
   LV_ModifyCol()
return

; Swap two columns of the list view # _lvID, given by their index, starting at 1
Swap(_col1, _col2, _colNb, _lvID)
{
   local colOrder, pos

   VarSetCapacity(colOrder, _colNb * 4, 0)
   Loop %_colNb%
   {
      pos := A_Index - 1
      If (A_Index = _col1)
         InsertInteger(_col2 - 1, colOrder, pos * 4)
      Else If (A_Index = _col2)
         InsertInteger(_col1 - 1, colOrder, pos * 4)
      Else
         InsertInteger(pos, colOrder, pos * 4)
   }
   SendMessage 0x1000 + 58   ; LVM_SETCOLUMNORDERARRAY
         , _colNb, &colOrder, SysListView32%_lvId%, A
   SendMessage 0x1000 + 21   ; LVM_REDRAWITEMS
         , 0, _colNb - 1, SysListView32%_lvId%, A
}
[EDIT: The ListView needs a redraw message to correctly update. Also added more columns and random swapping for test...]


I have a need for this as well, however i cannot get PhiLho's code, quoted here, to work so I can see a demonstration.

Am I doing something wrong?
Back to top
View user's profile Send private message
adamrgolf



Joined: 28 Dec 2006
Posts: 362

PostPosted: Wed Jun 27, 2007 10:22 am    Post subject: Reply with quote

My bad -- the code needed to be as follows to test (duh):

Code:
Gui Add, ListView, grid Checked vTestList, C1|C2|C3|C4|C5
Gui Add, Button, gSwap, Swap
Gui Add, Statusbar

Gui Show, Autosize Center +Resize, Swap Test

LV_Add("", "r1c1", "r1c2", "r1c3", "r1c4", "r1c5")
LV_Add("", "r2c1", "r2c2", "r2c3", "r2c4", "r2c5")
LV_Add("", "r3c1", "r3c2", "r3c3", "r3c4", "r3c5")
LV_ModifyCol()
Return


Swap:
   colNb := LV_GetCount("Column")
   Random col1, 1, colNb
   Loop
   {
      Random col2, 1, colNb
      If (col2 != col1)
         Break
   }
   Swap(col1, col2, colNb, 1)
   LV_ModifyCol()
return

; Swap two columns of the list view # _lvID, given by their index, starting at 1
Swap(_col1, _col2, _colNb, _lvID)
{
   local colOrder, pos

   VarSetCapacity(colOrder, _colNb * 4, 0)
   Loop %_colNb%
   {
      pos := A_Index - 1
      If (A_Index = _col1)
         InsertInteger(_col2 - 1, colOrder, pos * 4)
      Else If (A_Index = _col2)
         InsertInteger(_col1 - 1, colOrder, pos * 4)
      Else
         InsertInteger(pos, colOrder, pos * 4)
   }
   SendMessage 0x1000 + 58   ; LVM_SETCOLUMNORDERARRAY
         , _colNb, &colOrder, SysListView32%_lvId%, A
   SendMessage 0x1000 + 21   ; LVM_REDRAWITEMS
         , 0, _colNb - 1, SysListView32%_lvId%, A
}

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity.  To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
   Loop %pSize%  ; Copy each byte in the integer into the structure as raw binary data.
      DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}

GuiClose:
   ExitApp
Back to top
View user's profile Send private message
wtg



Joined: 04 Oct 2006
Posts: 63
Location: Louisville, KY

PostPosted: Wed Jun 27, 2007 1:54 pm    Post subject: Reply with quote

Glad you got it sorted out.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Ask for Help All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group