Oppure

Loading
09/02/16 16:18
robypiro
salve a tutti,
gli utilizzatori di una piccola applicazione che ho svilupato necessitano di dover copiare alcune righe da un datagridview ed incollarle su di una mail (outlook). La copia dei dati avviene correttamente con il solito ctrl+c ma, così facendo, si copiano i dati perdendo la formattazione della griglia. In pratica mi servirebbe fare un copia/incolla in stile access, è possibile?
aaa
10/02/16 8:09
Thejuster
Nella programmazione tutto e possibile.
Dipende pi dal tuo livello di preparazione.

Normalmente nella clipboard il formato data e quello che viene
Assegnato dall'utente.

Un semplice copia-incolla non sempre funziona.

Outlook di norma accetta il data in html.

Dovresti con una funzione generare codice html e con
I relativi dati della gridview esempio.

String b = "<b>" + gridview.Rows[0].Cell[0].value.ToString() + "</b>";
Clipboard.SetTex(b);

Prova in questo modo

mire.forumfree.it/ - Mire Engine
C# UI Designer
10/02/16 14:32
robypiro
Ho provato ad intercettare la pressione di ctrl+c e fare poi il codice html ma, quando vado ad incollare su outlook, mi incolla il testo comprensivo di tutti i tag html. Questo è il codice di esempio che sto provando prima i fare il ciclo per leggere le righe selezionate:

Private Sub DataGridView1_KeyUp(sender As Object, e As KeyEventArgs) Handles DataGridView1.KeyUp
        Dim a As String
        If e.Modifiers = Keys.Control AndAlso e.KeyCode = Keys.C Then
            a = "<HTML>"
            a = a + "<TABLE>"
            a = a + "<TR>"
            a = a + "<TD>prova prova</TD>"
            a = a + "</TR>"
            a = a + "</TABLE></HTML>"
            Clipboard.SetText(a)
        End If
    End Sub


Poi, tanto che ci siamo, questo codice per intercettare il ctrl+c sembra funzionare solo ogni tanto, non sempre
Ultima modifica effettuata da robypiro 10/02/16 14:46
aaa
10/02/16 16:18
GN
Non sono sicuro, ma probabilmente Outlook e altri programmi che usano il rendering engine di IE/Microsoft interpretano il contenuto della clipboard come HTML solo se glielo si "indica"; questa documentazione (non ho letto tutto) msdn.microsoft.com/en-us/library/…(v=vs.85).aspx spiega come mettere HTML nella clipboard. Probabilmente devi quindi aggiungere un header (come specificato in quella pagina) ed utilizzare la versione del metodo SetText che prende due argomenti (msdn.microsoft.com/it-it/library/…(v=vs.110).aspx), passando al secondo parametro il formato esatto, cioè TextDataFormat.Html (per ulteriori informazioni vedi msdn.microsoft.com/it-it/library/…(v=vs.110).aspx).

Inoltre c'è una domanda simile alla tua qui stackoverflow.com/questions/13332377/…
aaa
11/02/16 10:18
robypiro
Grazie delle risposte! Ho provato a leggere i documenti indicati, ma ancora non ci sono. Facendo questo codice, seguendo le indicazioni dei documenti, non mi carica niente nella clipboard

    
   Private Sub DataGridView1_KeyUp(sender As Object, e As KeyEventArgs) Handles DataGridView1.KeyUp
        Dim a As String
        If e.Modifiers = Keys.Control AndAlso e.KeyCode = Keys.C Then
            a = "Version:0.9 StartHTML:000125 EndHTML:000260 StartFragment:000209 EndFragment:000222 <!DOCTYPE>"
            a = a + "<HTML>"
            a = a + "<HEAD></HEAD>"
            a = a + "<BODY>"
            a = a + "<!-StartFragment->"
            a = a + "<TABLE>"
            a = a + "<TR>"
            a = a + "<TD>prova prova</TD>"
            a = a + "</TR>"
            a = a + "</TABLE><!-EndFragment-></BODY></HTML>"
            Dim DataObject = New DataObject()
            DataObject.SetData(DataFormats.Html, a)
            Clipboard.SetDataObject(DataObject)
        End If
    End Sub
aaa
11/02/16 15:24
GN
Dunque, mi sembra ci siano alcune cose che non vanno nel tuo codice (ovviamente potrei sbagliarmi):

-scrivere
a = a + "<HTML>"
a = a + "<HEAD></HEAD>"

e smili, NON inserisce un ritorno a capo tra <html> e <head>! Potrebbe non centrare con il problema, comunque visto che è scritto in quel modo mi sembrava giusto fartelo notare (magari poi lo sapevi già e l'hai scritto così per qualche altro motivo). Per inserire un ritorno a capo in una stringa, usa Environment.NewLine:
a = "riga1" + Environment.NewLine + "riga2"


-probabilmente i ritorni a capo servono invece nell'header, o almeno così sembra dalla documentazione; penso dovresti quindi separare i vari campi con ritorni a capo.

-ti consiglio di assegnare sempre un tipo alle variabili, e di non usare nomi di variabile uguali a nomi di classi/tipi, fra l'altro mi sembra strano che il compilatore ti permetta di farlo, ma con
Dim DataObject = New DataObject()

stai creando una brutta ambiguità: adesso cos'è DataObject, una variabile o una tipo? Io scriverei piuttosto qualcosa come
Dim htmlDO As DataObject = New DataObject()


-nella risposta precedente dicevo
utilizzare la versione del metodo SetText che prende due argomenti

che secondo la risposta su Stackoverflow funziona, ed è molto più semplice. Poi potrei sbagliarmi io, ma quello che hai scritto alle righe 14, 15 e 16 mi sembra un'inutile complicazione. Io intendevo
Clipboard.SetText(a, TextDataFormat.Html)


-rimane poi da risolvere il problema degli indici: come indicato nella documentazione, devi indicare il numero di byte dall'inizio della clipboard (penso quindi tenendo conto anche dell'header) all'inizio e alla fine dell'html. Se ho tempo più tradi provo a farlo io e se riesco ti do ulteriori informazioni.
Ultima modifica effettuata da GN 11/02/16 15:27
aaa
11/02/16 15:47
robypiro
Ha fatto le modifiche che mi ha indicato, però non saprei come calcolare il numero dei byte anche perchè non so a priopri quante righe l'utente andrà a copiare. Adesso il codice è così e, ahi me, ancora non funziona (dopo aver fatto crtl+c, su outlook il crtl+v non da niente)

    Private Sub DataGridView1_KeyUp(sender As Object, e As KeyEventArgs) Handles DataGridView1.KeyUp
        Dim a As String
        If e.Modifiers = Keys.Control AndAlso e.KeyCode = Keys.C Then
            a = "Version:0.9"
            a = a + Environment.NewLine + "StartHTML:000125"
            a = a + Environment.NewLine + "EndHTML:000260"
            a = a + Environment.NewLine + "StartFragment:000209"
            a = a + Environment.NewLine + "EndFragment:000222"
            a = a + Environment.NewLine + "<!DOCTYPE>"
            a = a + Environment.NewLine + "<HTML>"
            a = a + Environment.NewLine + "<HEAD></HEAD>"
            a = a + Environment.NewLine + "<BODY>"
            a = a + Environment.NewLine + "<!-StartFragment->"
            a = a + Environment.NewLine + "<TABLE>"
            a = a + Environment.NewLine + "<TR>"
            a = a + Environment.NewLine + "<TD>prova prova</TD>"
            a = a + Environment.NewLine + "</TR>"
            a = a + Environment.NewLine + "</TABLE>"
            a = a + Environment.NewLine + "<!-EndFragment->"
            a = a + Environment.NewLine + "</BODY>"
            a = a + Environment.NewLine + "</HTML>"
            Clipboard.SetText(a, TextDataFormat.Html)
        End If
    End Sub
aaa
11/02/16 20:43
GN
Ok, ho risolto.
Premetto che non so se sia il modo migliore di farlo, e che non era mio intento fornire il codice "pronto" ma spiegare come farlo, però ho dovuto fare un po' di prove e sono arrivato a questo frammento di codice funzionante. Ho cercato di commentare molto in modo da spiegare come funziona.

Prima di tutto serve una funzione che sostituisca solo la prima occorrenza in una stringa. Eccola (mettila nel tuo sorgente da qualche parte dove non crea problemi, magari nello stesso file fuori dal Sub che gestisce l'evento):
Public Function replaceFirst(text As String, search As String, replace As String) As String
		Dim i As Integer = text.IndexOf(search) 'posizione del primo carattere della stringa da sostituire
		If i < 0 Then Return text 'se non viene trovata l'occorrenza, restituisce la stringa invariata
		Return text.Substring(0, i) + replace + text.Substring(i + search.Length) 'componiamo la stringa risultante concatenando la parte della stringa originale prima del frammento da sostituire, la stringa da sostituire, e la parte dopo
End Function

Credit: mi sono leggermente ispirato (ok, non poi così leggermente :rotfl:, l'ho più che altro "tradotta";) a questa stackoverflow.com/a/…

Poi, ecco qua la "soluzione" che ho implementato:
'Il contenuto formattato in HTML che vogliamo copiare nella clipboard:
Dim tabella As String = "<table border='1'><tr><td><b>riga1</b></td></tr><tr><i>riga2</i></tr></title>"
'inizializzo la stringa da mettere nella clipboard con l'header, per ora senza settare gli indici. Al loro posto metto degli zeri, in modo da fissare una lunghezza per la stringa
Dim a As String = "Version:0.9" + vbCrLf +"StartHTML:000000" + vbCrLf +"EndHTML:000000" + vbCrLf +"StartFragment:000000" + vbCrLf +"EndFragment:000000" + vbCrLf
'ora aggiungiamo alla stringa da copiare l'html necessario.
'ottengo la dimensione della stringa convertendo la stringa in un array di byte con la codifica UTF8 e leggendone la dimensione
Dim headerSize As Integer = Text.Encoding.UTF8.GetBytes(a).Length
a += tabella
'ottengo la dimensione della stringa convertendo la stringa in un array di byte con la codifica UTF8 e leggendone la dimensione
Dim totalSize As Integer = Text.Encoding.UTF8.GetBytes(a).Length
'l'HTML inizia appena dopo l'header e finisce alla fine della clipboard,
'quindi abbiamo l'indice di inizio in headerSize e quello di fine in totalSize.
'A questo punto posso sostituire gli zeri con i valori trovati, facendo attenzione che questi siano di esattamente 6 cifre come i 6 zeri
'(utilizzando padLeft sulla stringa la porto ad dimensione di 6 caratteri aggiungendo zeri a sinistra)
Dim inizio As String = headerSize.ToString().padLeft(6, "0")
Dim fine As String = totalSize.ToString().padLeft(6, "0")
'la prima occorrenza degli zeri (per StartHTML) la sostituisco con l'indice di inizio
a = replaceFirst(a, "000000", inizio)
'la seconda (EndHTML) con l'indice di fine
a = replaceFirst(a, "000000", fine)
'stessa cosa per StartFragment e EndFragment
a = replaceFirst(a, "000000", inizio)
a = replaceFirst(a, "000000", fine)
Clipboard.SetText(a, TextDataFormat.Html)

La principale modifica che dovresti fare, ovviamente, è mettere al posto della stringa tabella i tuoi dati (come vedi nell'esempio ne ho messi alcuni a caso, giusto per provare la formattazione).
Come spiegato nei commenti il trucco sta nel mettere inizialmente degli zeri nell'header (ma in realtà porebbe essere qualsiasi altro carattere che in UTF8 ha una codifica in cui 1 char = 1 byte, come un'altra cifra o una lettera) per misurarne correttamente la lunghezza, e poi quando si conoscono i dati sostituirli al posto giusto.

Ho provato a farlo girare in una semplice applicazione console, poi ho incollato in Word e come previsto ho ottenuto la tabellina con la prima riga in grassetto e la seconda in corsivo. Immagino che su Outlook funzioni ugualmente (del resto a quanto ho capito questo formato per la clipboard è uno standard su Windows, inoltre immagino che i programmi Microsoft come quelli della suite Office gestiscano tutti il paste in HTML).

Una soluzione più elaborata e "seria", però in C# (da cui ho preso spunto), è spiegata qui theartofdev.com/2014/06/12/setting-htmltext-to-clipboard-revisited/.
Ultima modifica effettuata da GN 11/02/16 20:47
aaa