Newsgroup: it.comp.dotnet
Titolo: drag & drop  tra WinForm e controllo WPF

Ciao a tutti,
ho provato a postare questa domanda anche su un altro ng, quindi mi scuso 
con chi dovesse leggere due volte la stessa domanda.
Tempo fa per risolvere un mio problemino con un controllo, mi e' stato
suggerito di hostare un controllo WPF in una form WinForm.
Dopo un po' di tempo che "cincischiavo", mi sono deciso ed ho iniziato a
fare quanto suggerito, con qualche leggera modifica; non sto utilizzando una
ListView, ma una ListBox con associato un template, e fin qui i primissimi
test si stanno rivelando positivi.
La domanda che vorrei fare, prima di cominciare a diventar matto e' se e'
possibile fare un Drag & Drop tra un controllo WinForm (ad esempio una
ListView) ed il mio controllo personalizzato WPF che contiene la ListBox.
Grazie e ciao.. Maurizio 

Ciao vbMizio,

hai provato uno spike?
A naso non ci dovrebbero essere problemi.
.m
-- 
Mauro Servienti
{C67C0157-5D98-4733-A75E-93CAEE4BADC8}
Microsoft MVP - Visual C# / MCTS
http://mvp.support.microsoft.com/profile/mauro
blog @ 
http://milestone.topics.it
whynot [ at ] topics [ dot ] it

hai provato uno spike?
...
E' passato un po' di tempo dall'ultima volta che ho usato uno spike, visto 
che e' un bel po' che non vado piu' a pesca.
Non vedo pero' cosa c'entri un piombo ancorato per contrastare il mare 
grosso, con un controllo Wpf.
O forse intendevi un altro tipo di spike? :)
Boh, adesso cerco qualche cosa e vedo di capire cosa intendi.
P.s. Ogni indizio e' ben venuto :)
Ciao e grazie... Maurizio

 P.s. Ogni indizio e' ben venuto :)
significa semplicemente se hai provato ad isolare il problema, quindi 
una micro soluzione con un controllo hostato provando a vedere se D&D 
funziona, tutto li niente pesci :-)


 tutto li niente pesci :-)
...
Peccato, forse con le canne *da pesca* sarei andato meglio :-)

A naso non ci dovrebbero essere problemi.
...
In realta' qualche problema lo sto avendo nel momento in cui cerco di 
estrarre l'oggetto che ho voluto "dragare".
Mi spiego meglio...
In un controllo WinForm, all'evento MouseDown, inizio alla solita maniera il 
DragDrop, cioe':
MioControllo.DoDragDrop (MioOggetto, DragDropEffect.Copy)
MioOggetto e' una istanza di una classe contenuta in una libreria a cui 
faccio riferimento sia nel progetto WinForm, sia nel progetto WpfControl.
Nella ListBox Wpf, contenuta nel progetto WpfControl, nel gestore 
dell'evento ListBox1.DragEnter vado a verificare, tramite il metodo 
GetDataPresent della proprieta' Data dell'argomento e (di tipo 
System.Windows.DragEventArgs) se l'oggetto presente e' del tipo 
MiaLibreria.MiaClasse; nel caso di verifica positiva, imposto la proprieta' 
Effects di e = Copy.
Quando rilascio il tasto del mouse, viene scatenato l'evento Drop sulla 
ListBox1, in questo caso tento l'estrazione dell'oggetto trasportato, in 
questo modo:
If e.Data.GetDataPresent(GetType(MiaLibreria.MiaClasse)) Then
           Dim obj As Object
           Try
               obj = e.Data.GetData("MiaLibreria.MiaClasse") ' oppure 
e.Data.GetData(GetType(MiaLibreria.MiaClasse))
           Catch ex As Exception
           End Try
    [.....]
End if
a questo punto, quando tento di assegnare alla variabile obj, di tipo 
object, il risultato di e.Data.GetData("MiaLibreria.MiaClasse") , viene 
sollevata un'eccezione:
System.Runtime.InteropServices.COMException (0x80004005): Errore HRESULT 
E_FAIL restituito da una chiamata a un componente COM.
Capisco che il problema e' l'estrazione del dato... ma perche'?

In realta' qualche problema lo sto avendo nel momento in cui cerco di 
estrarre l'oggetto che ho voluto "dragare".
...
La classe che definisce l'oggetto  deve essere marcata come serializzabile.
Sembra funzionare

Sembra funzionare
...
Boh, qui succedono cose strane!
Questo D&D tra controlli WinForm e Wpf deve funzionare con tre diversi 
controlli sorgente WinForm, e quindi tre diversi tipi di oggetto.
Questi tre controlli hanno al proprio interno una listview che visualizza 
una serie di oggetti, e ogni ListViewItem ha impostato, come proprieta' Tag, 
il riferimento all'oggetto che sta visualizzando.
Per recuperare l'oggetto, quindi, faccio un cast tra la proprieta' Tag della 
ListViewItem e il tipo dell'oggetto stesso.
Quando inizio il D&D, prendo questo oggetto e lo passo come argomento al 
metodo DoDragDrop dell'oggetto Listview, quindi,
Me.ListView1.DoDragDrop(obj, DragDropEffects.Copy)
In due dei tre casi possibili, questa cosa funziona benissimo, chiaramente 
dopo aver impostata come serializzabile la classe che definisce l'oggetto 
stesso.
Nel terzo caso, invece, quando tento di recuperare il riferimento 
all'oggetto, mi viene sollevata un'eccezione in quanto l'oggetto sovrastante 
non e' marcato come serializzabile. Capisco di non essere stato molto 
chiaro, quindi provo a fare un piccolo esempio.
Diciamo che ho un primo oggetto di tipo Camion, che contiene una serie di 
oggetti Scatola ( List (of Scatola) ), e ognuna di queste scatole contiene 
una serie di oggetti di tipo Penna (List (of Penna) ).
Il mio controllo, che contiene la ListView, e' progettato in modo di 
ricevere un oggetto di tipo Scatola, quindi mostrare, tramite la ListView, 
tutte le penne contenute nella scatola.
Quando inizio il D&D, l'oggetto Penna viene recuperato dalla ListView, e 
viene passato come argomento all'evento DoDragDrop.
Nel controllo Wpf, all'evento Drop, tento di recuperare l'oggetto di 
partenza
obj = e.Data.GetData(GetType(....Penna))
Mi viene sollevata l'eccezione di cui sopra, mi viene detto che l'oggetto 
Scatola non e' marcato come serializzabile.
Che cosa interessa al codice che la scatola non e' serializzabile? Io ho 
chiesto l'oggetto Penna.
Vabbe', facciamo una prova, marchiamo come serializzabile anche la scatola.
Mi viene alzata la stessa eccezione, pero' questa volta a non essere 
serializzabile e' addirittura l'oggetto MioControllo, quello cioe' che 
contiene la Listview.
Boh!!
Nei due casi che stanno funzionando correttamente, la situazione e' la 
stessa, cioe' un controllo che contiene una ListView, la quale visualizza 
una serie di oggetti di tipo Matita, serializzabile.
Qualche idea?
Grazie e ciao... Maurizio

 chiesto l'oggetto Penna.
È fondamentale che sia serializzabile visto che il Clipboard è valido 
per tutto il desktop (*) corrente e quindi trasportabile da un processo 
all'altro mentre il tuo codice gira dentro un appdomain che ha dei 
confini persino più stretti rispetto ad un processo.
Occhio che questi sono concetti di base in Windows e vanno ben compresi 
per non perdere settimane di tempo.
(*) il desktop è un oggetto kernel di windows che, in modo improprio, 
associamo alla schermata di Windows quando le applicazioni sono tutte 
iconizzate.
-- 
Raffaele Rialdi  
http://www.iamraf.net
Weblog: 
http://blogs.ugidotnet.org/raffaele
Microsoft MVP profile 
https://mvp.support.microsoft.com/profile/raffaele
UGIdotNET - 
http://www.ugidotnet.org/

 Qualche idea?
è giusto così, non è questione di D&D ma di serializzazione, quando un 
serializer serializza un oggetto necessariamente serializzerà tutto il 
grafo delle reference che quell'oggetto ha, quindi se quell'oggetto ha 
un riferimento ad un altro oggetto che non è serializzabile il processo 
scoppia.
Quello che dovresti fare è piazzare nel "DataObject" del D&D un 
surrogato del tuo oggetto originale, un DTO insomma, che trasporti le 
informazioni minime necessarie a ricostruire la situazione dall'altra 
parte.


 È fondamentale che sia serializzabile visto che il Clipboard è valido
per tutto il desktop (*) corrente e quindi trasportabile da un processo
all'altro mentre il tuo codice gira dentro un appdomain che ha dei
confini persino più stretti rispetto ad un processo.
...
C'e' qualche cosa che non capisco....
Riprendendo l'esempio di prima, camion, scatole e penne, capirei se dovessi 
serializzare l'oggetto Scatola che ha una lista di Penne, allora 
naturalmente dovrei poter serializzare Scatola ma anche Penna.
Nel processo inverso, Penna e' un oggetto a se stante, scollegato dalla 
Scatola sovrastante... voglio dire.... Per recuperare una Penna, non devo 
sapere come e' fatta una Scatola.
Cmq, per prova ho aggiunto l'attributo <Serializable> anche alla classe 
Scatola, a questo punto mi viene alzata l'eccezione che il mio controllo 
MioControllo (quello che contiene la ListView) non e' seriaizzabile. 
Proviamo quindi ad aggiungere l'attributo <Serializable> anche al mio 
controllo (???), viene alzata l'eccezione che la classe che definisce le 
Form non e' contrassegnata come serializzabile (!!!)
Ora, da quanto ne so io, per essere serializzabile una classe, se eredita da 
una classe base, anche la classe base deve essere serializzabile; se 
contiene riferimenti ad altri oggetti, le rispettive classi devono essere 
serializzabili. Non mi sembra pero' che la cosa si propaghi anche ai livelli 
superiori.. in effetti, a naso, non avrebbe senso.
Sbaglio molto?

 Sbaglio molto?
Sei sempre tu a decidere cosa serializzare e cosa no grazie a 
NoSerialize e altri strumenti quali:
- gli eventi di Serialize e Deserialize che ti permettono di 
"attaccare" e "staccare"  i reference in modo da evitare che quella 
parte del grafo sia serializzata
- implementare ISerializable al posto del meccanismo di default per 
controllare in modo capillare (via codice) cosa serializzare e cosa no
Il serializzatore non fa differenza tra una tua classe e una del 
framework da serializzare, ed è giusto che sia così.


Hai dei riferimenti attivi dalla scatola al controllo? Questo puzza di
errore di progettazione.
...
Si, e' una cosa che in realta' faccio spesso.
Se devo mostrare un qualunque oggetto, spesso lo passo al controllo 
visualizzatore, che sara' in grado quindi di visualizzare l'oggetto stesso.
Non mi sembra una cosa cosi' brutta da fare.. o no?!?!
Grazie ancora... Maurizio

La cosa strana pero' e' che anche nel caso dell'altro controllo (quello che
non mi sta dando problemi), ho una serie di oggetti che al loro interno
hanno una List (of ) ...
...
controllato meglio il codice. In effetti in questo caso non vado a prendere 
degli oggetti contenuti nella List (of Chiodo), ma faccio delle richieste 
diverse e quindi i miei chiodi sono oggetti a se stanti, non vincolati a 
nessuno.
Dovrei quindi riuscire a sganciare il mio oggetto Penna rispetto all'oggetto 
Scatola.
Faccio un po' di prove!

 Grazie ancora... Maurizio
Prego


Ciao,
comincio a non capirci piu' niente (non che prima avessi le idee troppo 
chiare) :-)
Il mio controllo ha una proprieta', objScatola as Scatola
lo scrivo in VB 2008
Private m_objScatola as Scatola
Public WriteOnly Property objScatola as Scatola
   Set (byval value as Scatola)
      m_objScatola = value
   End Set
End Property
In base a quello che ci siamo detti, il grafo *deve* tenere conto anche del 
mio controllo, dato che e' vivo un riferimento... o sbaglio?
Questa tecnica che ho appena mostrato, mi serve per un paio di motivi 
importanti...
Come prima cosa posso intercettare gli eventi dell'oggetto Scatola 
(naturalmente aggiungendo un Handler), e posso andare ad agire sull'oggetto 
Scatola direttamente dal mio controllo UI.
E' cosi' brutto questo approccio?

Ciao vbMizio,

no, non è brutto, ma appena agganci un hadler ad un evento di scatola 
ecco che hai un riferimento che il serilizer cerca di navigare e 
serializzare :-)
ecco perchè dicevo nel mio post di non passare in giro direttamente il 
tuo grafo ma degli oggetti più semplici fatti ad hoc per il drag & 
drop.


ecco perchè dicevo nel mio post di non passare in giro direttamente il
tuo grafo ma degli oggetti più semplici fatti ad hoc per il drag &
drop.
...
In realta' sto provando a passare un clone dell'oggetto Penna, che 
chiaramente non ha riferimenti da nessuna parte, e cosi' funziona.
Devo pero' rivedere tutta la logica che c'e' dietro alle operazioni da fare 
quando ho recuperato l'oggetto clone.
Scrivero'!