Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations derfloh on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Is there a way to change the order in a grid by dragging?

Status
Not open for further replies.

tkee

Programmer
Feb 6, 2004
55
US
I have a maintenance form consisting mainly of a grid for accounts. The file structure has a code, description, and other information including an indexed numeric field that I use so that the order can be changed to whatever the users want. That order will be used on several other forms. Is there a fairly simple way I could let them change the order of the records on the screen by dragging them? Then I could update the display order field when the form is closed.
 
I assume you mean you want to let the user drag an individual record up or down the grid, and for that change then to affect the order of the records.

It's theoretically possible, but it would be tricky, and very slow. The actual drag and drop within the grid isn't particuarly easy, plus you'd have to physically re-order the records in the underlying cursor every time a row was dropped.

Or do you mean you simply want to let the user sort the grid to code order, or description order, or whatever? That would be much easier. Just write code in the grid's column headers' Click events. The code would select the appropriate index and redisplay the grid. But I assume that's not what you want, since you specifically asked about drag and drop.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro tips, advice, training, consultancy
Custom software for your business
 
I was afraid of that but figured it was worth a shot, especially since there are such awesome people on this forum. Usually the things that look simple aren't. Thanks for your reply!
 
You might try looking for a different way of displaying the data -- perhaps something like the Microsoft Listview control. That supports drag-and-drop natively. It doesn't physically re-order the data source, but it might provide a first step.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro tips, advice, training, consultancy
Custom software for your business
 
As Mike said, you can do this with drag-and-drop code, but I don't think it's as hard as Mike said. As I understand it, you already have a sort field you want to use, so what you need is for the DragDrop method (or OLEDragDrop--on the whole, OLE drag-and-drop is more much powerful and offers more control) to change the values in that field. That's a easier than actually reordering records.

That said, you're still probably better off using a control that has this functionality natively.
 
If the grid does not accept input, this can be done very easily, without drag and drop, by using a listbox with MoveBar set to .T., once moved loop through the list items and renumber...
You could try using the GridHitTest of the grid as well, but involves coding...
 
This is how you do it without Drag and drop in a grid. This is from the top of my head, so it has not been throughly tested. Just thought I would take a crack at it...sounded interesting.

Code:
PUBLIC oform1

oform1=NEWOBJECT("form1")
oform1.Show
RETURN


	**************************************************
*-- Form:         form1 (c:\intelisys\form1.scx)
*-- ParentClass:  form
*-- BaseClass:    form
*-- Time Stamp:   04/16/09 08:00:13 PM
*
DEFINE CLASS form1 AS form


	Top = 15
	Left = 54
	DoCreate = .T.
	Caption = "Form1"
	*** create these properties in your form
	oldrecno = 0
	newrecno = 0
	oldrecvalue = 0
	newrecvalue = 0
	firsthit = 1
	*************************************
	Name = "Form1"


	ADD OBJECT grid1 AS grid WITH ;
		ColumnCount = 2, ;
		DeleteMark = .F., ;
		Height = 138, ;
		Left = 19, ;
		Panel = 1, ;
		ScrollBars = 2, ;
		Top = 40, ;
		Width = 290, ;
		HighlightStyle = 1, ;
		Name = "Grid1", ;
		Column1.Width = 139, ;
		Column1.Name = "Column1", ;
		Column2.Width = 103, ;
		Column2.Name = "Column2"


	ADD OBJECT form1.grid1.column1.header1 AS header WITH ;
		Caption = "Description", ;
		Name = "Header1"


	ADD OBJECT form1.grid1.column1.text1 AS textbox WITH ;
		BorderStyle = 0, ;
		Margin = 0, ;
		ForeColor = RGB(0,0,0), ;
		BackColor = RGB(255,255,255), ;
		Name = "Text1"


	ADD OBJECT form1.grid1.column2.header1 AS header WITH ;
		Caption = "Number", ;
		Name = "Header1"


	ADD OBJECT form1.grid1.column2.text1 AS textbox WITH ;
		BorderStyle = 0, ;
		Margin = 0, ;
		ForeColor = RGB(0,0,0), ;
		BackColor = RGB(255,255,255), ;
		Name = "Text1"


	PROCEDURE moverecs  &&&& create this procedure in the form
		With This.grid1
			If Thisform.firsthit = 1
				Thisform.oldrecno = Recno("gridcursor")
				Thisform.oldrecvalue = gridcursor.nrec
				Thisform.firsthit = 2
			Else
				Thisform.newrecno = Recno("gridcursor")
				Thisform.newrecvalue = gridcursor.nrec
				Select gridcursor
				Goto Thisform.oldrecno
				Replace gridcursor.nrec With Thisform.newrecvalue
				Goto Thisform.newrecno
				Replace gridcursor.nrec With Thisform.newrecvalue + 1
				nRecno = 0
				Scan
					nRecno = nRecno + 1
					If nRecno = Thisform.newrecvalue And Recno() = Thisform.oldrecno
						nRecno = nRecno + 1
						Loop
					Endif
					If nRecno = (Thisform.newrecvalue + 1)  And Recno() = Thisform.newrecno
						nRecno = nRecno + 1
						Loop
					Endif
					Replace gridcursor.nrec With nRecno
				Endscan
				Thisform.grid1.Refresh
				Thisform.firsthit = 1
				Select gridcursor
				Go Top
			Endif
		Endwith
	ENDPROC


	PROCEDURE Load
		If Select("gridcursor") > 0
			Use In gridcursor
		Endif
		Create Cursor gridcursor (Descip C(25),Nrec N(4,0)) 
		Index On Nrec Tag Nrec
		Set Order To Tag Nrec
		For x = 1 To 100
			Insert Into gridcursor ;
				(Descip,Nrec) ;
				VALUES ;
				("test_"+Transform(x),x)
		Endfor
	ENDPROC


	PROCEDURE grid1.Init
		With This
			.RecordSource = "gridcursor"
			.RecordSourceType = 1
			.column1.ControlSource = "gridcursor.descip"
			.column2.ControlSource = "gridcursor.nrec"
			Bindevent(.column1.text1,"click",Thisform,"moverecs")
			Bindevent(.column2.text1,"click",Thisform,"moverecs")
			.Refresh
			Go Top
		Endwith
	ENDPROC


ENDDEFINE
*
*-- EndDefine: form1
**************************************************
The above is to give you an idea on how it can be done, you will have to fine tune to your needs
 
Sorry; should explain on how to use this.

Click the row you want to move First, then Click the row where you want to move the first row to ...

Now that I think about it a Double Click would be better...
 
Imaginecorp,

You've thought this through a lot more than I did. At a quick glance, your code looks good, but I'd still be worried about performance. Every time you move a row, you are scanning the entire cursor. Might be OK, but with a large cursor it could be slow enough to discourage the user from wanting to do it.

You could probably extend your idea to make it a true drag-and-drop operation, which would be more intuitive, though it would need a bit more code.

It's good that you mentioned a listbox. I hadn't thought of that, although you'd still have to write the results back to the cursor at some point.

On balance, I think I'd still look at the Listview control first.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro tips, advice, training, consultancy
Custom software for your business
 
Thank you for all the thoughtful, creative suggestions. I have several avenues to try now and will see what works best in my particular situation.
 
Hello Mike;
As is obvious by the code, Not too much thought went into it...The performance hit is minor about 2 seconds for 91,091 records.
Just could not wrap my mind around drag and drop... The amount of coding would definately not be worth it. How would the grid be scrolled? How would you determine direction with out a Gridhittest, finally how would the record be inserted into the target row and the target row moved down... Too much coding for my liking.
Listview may work but I have a problem with using Activex especially in a distributed application. I also have a major problem with using others (Marcia Atkins etc) classes, code etc. Though its not that I dont think they are good but I would rather write my own.
Just changed the above code, try it for speed:use doubleclick instead.

Code:
PUBLIC oform1

oform1=NEWOBJECT("form1")
oform1.Show
RETURN


	**************************************************
*-- Form:         form1 (c:\intelisys\form1.scx)
*-- ParentClass:  form
*-- BaseClass:    form
*-- Time Stamp:   04/17/09 09:24:07 AM
*
DEFINE CLASS form1 AS form


	Top = 26
	Left = 14
	Height = 236
	Width = 466
	DoCreate = .T.
	Caption = "Form1"
	oldrecno = 0
	newrecno = 0
	oldrecvalue = 0
	newrecvalue = 0
	firsthit = 1
	Name = "form1"


	ADD OBJECT grid1 AS grid WITH ;
		ColumnCount = 3, ;
		DeleteMark = .F., ;
		GridLines = 3, ;
		Height = 200, ;
		Left = 27, ;
		Panel = 1, ;
		ScrollBars = 2, ;
		Top = 16, ;
		Width = 407, ;
		HighlightBackColor = RGB(192,192,192), ;
		HighlightStyle = 2, ;
		AllowAutoColumnFit = 2, ;
		Name = "Grid1", ;
		Column1.Width = 74, ;
		Column1.Name = "Column1", ;
		Column2.Width = 157, ;
		Column2.Name = "Column2", ;
		Column3.Width = 141, ;
		Column3.Name = "Column3"


	ADD OBJECT form1.grid1.column1.header1 AS header WITH ;
		Caption = "ID", ;
		Name = "Header1"


	ADD OBJECT form1.grid1.column1.text1 AS textbox WITH ;
		BorderStyle = 0, ;
		Margin = 0, ;
		ForeColor = RGB(0,0,0), ;
		BackColor = RGB(255,255,255), ;
		Name = "Text1"


	ADD OBJECT form1.grid1.column2.header1 AS header WITH ;
		Caption = "Company", ;
		Name = "Header1"


	ADD OBJECT form1.grid1.column2.text1 AS textbox WITH ;
		BorderStyle = 0, ;
		Margin = 0, ;
		ForeColor = RGB(0,0,0), ;
		BackColor = RGB(255,255,255), ;
		Name = "Text1"


	ADD OBJECT form1.grid1.column3.header1 AS header WITH ;
		Caption = "Contact", ;
		Name = "Header1"


	ADD OBJECT form1.grid1.column3.text1 AS textbox WITH ;
		BorderStyle = 0, ;
		Margin = 0, ;
		ForeColor = RGB(0,0,0), ;
		BackColor = RGB(255,255,255), ;
		Name = "Text1"


	PROCEDURE moverecs
		With This
			If .firsthit = 1
				.oldrecno = Recno("gridcursor")
				.oldrecvalue = gridcursor.nrec
				.firsthit = 2
			Else
				.newrecno = Recno("gridcursor")
				.newrecvalue = gridcursor.nrec
				NextRecord = .newrecvalue + 1
				Select gridcursor
				Goto .oldrecno
				Replace gridcursor.nrec With .newrecvalue
				Goto .newrecno
				Replace gridcursor.nrec With NextRecord
				nRecno = 0
				Scan
					nRecno = nRecno + 1
					If nRecno = .newrecvalue And Recno() = .oldrecno
						nRecno = nRecno + 1
						Loop
					Endif
					If nRecno = NextRecord And Recno() = .newrecno
						nRecno = nRecno + 1
						Loop
					Endif
					Replace gridcursor.nrec With nRecno
				Endscan
				.grid1.Refresh
				.firsthit = 1
				Select gridcursor
				Seek .newrecvalue
			Endif
		Endwith
	ENDPROC


	PROCEDURE QueryUnload
		If Select("customers") > 0
			Use In customers
		Endif
		If Select("gridcursor") > 0
			Use In gridcursor
		Endif
	ENDPROC


	PROCEDURE Load
		Set Default To Home()+"samples\northwind\"
		If Select("customers") > 0
			Use In customers
		Endif
		If Select("gridcursor") > 0
			Use In gridcursor
		Endif
		Select 0
		Use Home()+"samples\northwind\northwind!customers" Alias customers
		Select customers
		Select * ;
			FROM Home()+"samples\northwind\customers" ;
			INTO Cursor gridcursor1 Readwrite
		[blue]For x = 1 To 1000
			Append From customers
		Endfor[/blue]
		Select gridcursor1
		Select gridcursor1.*, Cast(Recno() As N(10,0)) As NRec ;
			FROM gridcursor1 ;
			INTO Cursor gridcursor Readwrite
		Index On NRec Tag NRec

		Set Order To Tag NRec
	ENDPROC


	PROCEDURE grid1.Init
		With This
			.RecordSource = "gridcursor"
			.RecordSourceType = 1
			.column1.ControlSource = "gridcursor.customerid"
			.column2.ControlSource = "gridcursor.companyname"
			.column3.ControlSource = "gridcursor.contactname"
			For Each oColumn In .Columns
				Bindevent(oColumn.text1,"dblclick",Thisform,"moverecs")
			Next
			.Refresh
			Go Top
		Endwith
	ENDPROC


ENDDEFINE
*
*-- EndDefine: form1
**************************************************
 
Hello Auguy & Mike;

Like I said earlier this sounded interesting so I looked at Marcia's Grid Class at Foxite to see how much better it would be using Drag & Drop. I consider Marcia knowledgeable in VFP and I am sure her way is probably how it would work with Drag & Drop.

Tested it with 91,091 records and it was overall, just painful, for lack of a better word… A lot more coding and Thought, much more that what a feature like this is worth, is required to make it work seamlessly.

Like we say in New York “With all due respect,” I prefer the Simple Basic FoxPro Code …
 
ImagineCorp, Just wondering for my own benefit, did you have all of the indexes set up?

Auguy
Northwest Ohio
 
Hello Auguy; Yes I did, it was not the speed or accuracy (indexes) just found flaws in the code and after giving it a lot of thought came to the conclusion it requires much more thought and coding to make the Drag & Drop work seamlessly... Just my 2 cents
 
Imaginecorp,

You've confirmed what I suspeced re performance. It's not really surprising, given the amount of record access and updating that each drop operation has to generate. (This is no reflection on Marcia's code; it's an inevitable consequence).

That said, it might not be a problem with a very small table - one containing a dozen or so records, maybe. In any case, a drag-and-drop interface is going to be pretty unusable on a 91,000-row table, so maybe Tkee has a much smaller data set in mind.

Like we say in New York "With all due respect," ..

Yeah, we say that too. It usually means "without any respect" <g>

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro tips, advice, training, consultancy
Custom software for your business
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top