Copy/Paste

Hi braintrust :slight_smile: ,

Any thoughts on this?

I am trying to create copy/cut/paste in a list box. I can get individual cell editing going and that works.
I have been able to get copy/cut/paste working on text boxes with the code I’ll show here in the TextBox keydown event.

I thought, all I would have to do is copy that into the CellKeyDown event on the listbox but it doesn’t work.

The code is:

if Keyboard.CommandKey Then
select case key
case chr(67), chr(99)
'C,c
var c As New Clipboard
c.Text = me.CellTextAt(row, column)
c.Close

case chr(86), chr(118)
'V, v
var c As New Clipboard
me.CellTextAt(row, column) = c.Text
c.Close

case chr(88), chr(120)
'X, x
var c As New Clipboard
c.Text = me.CellTextAt(row, column)
me.CellTextAt(row, column) = “”
c.Close
end select

end if

Return False

Note that in the text box I use me.Text instead of me.CellTextAt(row, column).

I placed a break point at the top of this and followed it through and it is working perfectly EXCEPT it doesn’t copy or remove the text in the cell. I can see the current text in the cell I’m working with. I can also see the contents of the clipboard and that all works as expected. The only thing it doesn’t do is to remove/replace text respectively.

Any ideas?

Subclass your listbox and add the copy/cut/paste code there…
Then set the working listbox to that custom subclass super…

i made the cells/columns as textbox in Window Opening

Sub Opening() Handles Opening
  ListBox1.ColumnTypeAt(0)=DesktopListBox.CellTypes.TextField
  ListBox1.ColumnTypeAt(1)=DesktopListBox.CellTypes.TextField
  ListBox1.ColumnTypeAt(2)=DesktopListBox.CellTypes.TextField
  
End Sub
Function CellKeyDown(row as Integer, column as Integer, key as String) Handles CellKeyDown as Boolean
  Return CutCopyPasteListBox(Me, row, column, key)
  
End Function

Public Function CutCopyPasteListBox(lb As DesktopListBox, row As Integer, column As Integer, key As String) As Boolean
  Var handled As Boolean = False
  
  If Keyboard.CommandKey Then
    Var c As New Clipboard
    Select Case key
    Case "C","c" 'Copy
      c.Text = lb.CellTextAt(row, column)
      handled = True
    Case "V","v" 'Paste
      lb.CellTextAt(row, column) = c.Text
      handled = True
    Case "X","x" 'Cut
      c.Text = lb.CellTextAt(row, column)
      lb.CellTextAt(row, column) = ""
      handled = True
    End Select
    c.Close
  End If
  
  Return handled
End Function
1 Like

Yeah, the subtle difference is that @MarkusR is returning True in the cases where the keyboard event is being handled. If you don’t do that then the listbox’s clipboard handler fires and you get nothing.

1 Like

Emile,
Thanks for the reply. I should have started, as I often do, by stating that I am a hobbiest programmer and, whilst I’m “reasonably” proficient, when it comes to some of the techniques you have indicated, you might as well be talking Chinese to me :slight_smile:
“Subclassing” my listbox and making alterations is something I have no idea how to do. I understand the “idea” behind what you are suggesting and I’d like to know about such things, but I can’t find any tutes etc on these sorts of techniques.

I guess, what I’m asking here this time really, is WHY does my code not work. Things like this just completely stump me! I have read and changed the contents of cells many times in this and other apps accessing the CellTextAt(row, column) but for some reason it just won’t do it here. Is this some sort of bug or have I done something wrong. Am I being unrealistic to think things will always work as they should?

Thanks tho :slight_smile:

Hi Greg,
Thanks for the reply. There was actually code following what I posted which I left out for brevity as I didn’t think it was relevant. The full routine is:

If Keyboard.CommandKey Then
  select case key
  case chr(67), chr(99)
    var c As New Clipboard
    c.Text = me.CellTextAt(row, column)
    c.Close
  case chr(86), chr(118)
    'V, v
    var c As New Clipboard
    me.CellTextAt(row, column) = c.Text
    c.Close
  case chr(88), chr(120)
    'X, x
    var c As New Clipboard
    c.Text = me.CellTextAt(row, column)
    me.CellTextAt(row, column) = ""
    c.Close
  end select

end if

if key = chr(9) Or key = chr(29) then
  var i As Integer = column + 1
  select case I
  case 0, 1, 2, 15
    i = 3
  case 4
    i = 5
  case 12, 13
    i = 14 
  end select
  me.EditCellAt(row,i)
ElseIf key = chr(28) Then
  var i As Integer = column - 1
  select case I
  case 0, 1, 2
    i = 14
  case 4
    i = 3  
  case 12, 13
    i = 11
  end select
  me.EditCellAt(row,i)
End

me.SelectedRowIndex = DesktopListBox.NoSelection
Return False

This routine was originally written to handle tabbing forwards and backwards and handling pressing the escape and enter keys when editing the cells. It allows me to skip certain cells that ar not for editing. The all works perfectly so, as th new code works as expected on text boxes I (wrongly) assumed I could just paste it at the start of the routine.

So, I tried changing the Return False to Return True and it no longer allows me to type anything into the cells. I even tried inserting
Return True
Exit
just before the end of the “If Keyboard.CommandKey Then” loop and whilst the editing returned, it still won’t allow copy/cut/paste.

Thanks Marcus,
I’ll have a look at that, but I still don’t get why we have to convert it to a text box, when I have read and written directly to cells in the past.

Barry

try this, it should copy/paste by mouse over the list.
alternative use this two context menu events.


Sub KeyUp(key As String) Handles KeyUp
  System.DebugLog CurrentMethodName
  
  Var xValue As Integer
  xValue = System.MouseX - Me.Left - Self.Left ' Calculate current mouse position relative to top left of ListBox
  
  Var yValue As Integer
  yValue = System.MouseY - Me.Top - Self.Top ' Calculate current mouse position relative to top of ListBox.
  
  Var row As Integer = Me.RowFromXY(xValue, yValue)
  Var column As Integer = Me.ColumnFromXY(xValue, yValue)
  
  System.DebugLog "Row " + row.ToString + " Column " + column.ToString
  
  If row >= 0 And column >= 0 Then
    Call CutCopyPasteListBox(Me, row, column, key)
  End If
  
End Sub

Public Function CutCopyPasteListBox(lb As DesktopListBox, row As Integer, column As Integer, key As String) As Boolean
  System.DebugLog CurrentMethodName
  
  Var handled As Boolean = False
  
  If Keyboard.ControlKey Or Keyboard.CommandKey Then
    System.DebugLog "ControlKey or CommandKey"
    Var c As New Clipboard
    Select Case key
    Case "C","c" 'Copy
      System.DebugLog "Copy"
      c.Text = lb.CellTextAt(row, column)
      handled = True
    Case "V","v" 'Paste
      System.DebugLog "Paste"
      lb.CellTextAt(row, column) = c.Text
      handled = True
    Case "X","x" 'Cut
      System.DebugLog "Cut"
      c.Text = lb.CellTextAt(row, column)
      lb.CellTextAt(row, column) = ""
      handled = True
    End Select
    c.Close
  End If
  
  Return handled
End Function

You will find something in the documentation (about canvas) here:
https://documentation.xojo.com/topics/custom_controls/creating_a_color_selector_control.html#topics-custom-controls-creating-a-color-selector-control-create-your-own-canvas-based-subclass

Reading this forum is also a good idea to learn different technics…

Drag a DesktopListbox from the Library (on the right) into the navigation pane (on the left). Now rename it in the Inspector. Well done, you’ve just subclassed DesktopListbox and can now add methods and properties and more to it that it didn’t have before. Your subclass has the name you gave it, and its superclass is DesktopListbox. So it’s a DesktopListbox with any added features that you implement.

Your new subclass should also appear in the Project Items part of the Library, so you can drag that to your layout, for example, or create an instance in code with:

Var  boxInst = new MySubclass   // or whatever you called it.

To all who have offered so far, thankyou.

Emile, totally agree re the forum and I have learnt so much just from posting issues here. There is a wealth of information and I am so grateful you guys take the time to help.

Tim, thanks for those instructions, I will definitely have a look at that.

Now, to sound like a broken record, the ONLY thing I still don’t get, is why do those cells NOT change in the code I have. In other cases I can change the text using CellTextAt(…) but for some reason, nada! Maybe it’ll always remain one of those mysteries of programming :thinking: :stuck_out_tongue_winking_eye:

Thanks all.

Emile,
I had a look at that tute project and followed it through. The thing it doesn’t explain though, and I feel this is possibly the most important part, is why the Event Definition. It has the same name and parameters as the already existing Event Handler which is where all the working code is placed anyway, whilst the new Event Handler is blank.

I’m really sorry if this sounds dumb, but I don’t get what the purpose of the “Handler” is. There must be reason but…? Good little tute, but like many, doesn’t really explain why.

Anyway, I’ll keep battling away and I’ll understand if you guys get tired of my questions :slight_smile:

Are you sure of that? I don’t get the same behaviour.
Here’s my observation:
I created a blank project with a listbox (filled with random rows and me.ColumnTypeAt(0)=DesktopListBox.CellTypes.TextField in the opening event).
Then, the CellKeyDown event like this:

if Keyboard.CommandKey then
  if key="C" then
    me.CellTextAt(row, column)="W"
    Return True
  end if
end if

Put a breakpoint in the first line, run, edit a cell and type command-C. What I see is the Edit menu is highlighted, proving the “Copy” command is executed, but the breakpoint isn’t encountered. In other words, Mac OS takes the shortcut in behalf of Xojo.
That’s how it happens on my system. Can you double-check on yours?

Sorry, I may have been misleading. I didn’t mean the app worked perfectly, I meant that if I place a break point at the line
if Keyboard.CommandKey Then
and then I type CMD-C on the keyboard, the app stops and if I step through I can see that the row and column are correct, the text in the cell is what it should be and, after executing
c.Text = me.CellTextAt(row, column)
then c = the contents of the cell as it should.

If I then continue and type CMD-V the app again stops as it should and stepping through shows that c still contains the copied text and the row and column are correct but after executing the line to copy c into the cell, it just doesn’t! And that’s what I don’t understand.

At the end of the entire routine, I return TRUE. It was suggested that to work this part should return FALSE so I added the lines
Return FALSE
Exit
That still doesn’t work.

Did you think I meant that the app works if I did that? Sorry if you did, I meant the lines worked if stepped through.

And this is where our observations differ. On my side, the app doesn’t step to the debugger and the Edit menu is highlighted instead (like a regular copy).
Are you editing the cell while you press Command-C?

The Exit is superfluous.

BTW, when posting code please do this:

  1. Add it to your post
  2. Select it with the mouse
  3. Click on the </> button.

It makes code a lot easier to read in posts.

When you edit a listbox cell, you actually have 2 values you’re dealing with: the contents of the cell and the contents of a textfield hovering just above the cell. When you exit edit mode, the contents of the textfield get copied back into the cell. If you’ve modified the contents of the cell, those changes will be overwritten.

Hello again,
So, I tried an exercise to simplify all this. I created a small “Test” app. Single window with just a ListBox with columns. At the Opening Event I created 5 rows and filled each cell with basic data.
Then added this to the following Events:

CellPressed

Me.ColumnTypeAt(column) = DesktopListBox.CellTypes.TextField
Me.EditCellAt(row, column)

CellKeyDown

var Handled As Boolean = False
var c As New Clipboard
if Keyboard.CommandKey Then
  
  select case key
  case chr(67), chr(99)
    c.Text = me.CellTextAt(row, column)
    c.Close
    Handled = True
    
  case chr(86), chr(118)
    'V, v
    me.CellTextAt(row, column) = c.Text
    c.Close
    Handled = True
    
  case chr(88), chr(120)
    'X, x
    c.Text = me.CellTextAt(row, column)
    me.CellTextAt(row, column) = ""
    c.Close
    Handled = True
    
  end select
  
Else
  select case key
  case chr(9)
    'tab
    
    if Keyboard.ShiftKey Then
      'shift-tab
      var i As Integer = column - 1
      if i < 0 Then i = 2
      me.EditCellAt(row,i)
      
    Else
      'tab
      var i As Integer = column + 1
      if i > 2 Then i = 0
      me.EditCellAt(row,i)
      
    end if
    
  case chr(28)
    'L arrow
    var i As Integer = column - 1
    if i < 0 Then i = 2
    me.EditCellAt(row,i)
    
  case chr(29)
    'R arrow
    var i As Integer = column + 1
    if i > 2 Then i = 0
    me.EditCellAt(row,i)
    
  end select
  
End

me.SelectedRowIndex = DesktopListBox.NoSelection

Return Handled

Guess what? Works perfectly!!! So, it would appear, it is elsewhere that the code is falling down. For my working app, the CellPressed is much more complicated as not all columns are editable so I have to deal with them individually, so the issue might be here, I’m not sure.
Anyway, it gives me a starting point. Also, I’m relieved that my basic understanding is still sound. This does work, it’s somewhere else that failing.
Thanks everyone for your help so far. Don’t relax too much tho, I’m sure I’ll be back :slight_smile: