Newsgroup: it.comp.dotnet
Titolo: WPF e blendability: ma è possibile che si debba necessariamente "sporcare" il codice?

Ciao a tutti,
 
sotto la supervisione dell'amico e collega marcosabba sto muovendo i 
primi passi nel magico mondo di WPF. Sto sbattendo il muso con il 
concetto di "blendability". Sia con MVVM Light [1] che con soluzioni 
alternative [2] (ma che comunque sono una variazione sul tema), la 
musica è sempre la stessa: bisogna "sporcare" il codice. La mia domanda 
è la seguente: è possibile impostare il design data context con una 
specie di IoC a design time?
 
Ciao e grazie,
Giulio
________________________________________
[1] 
http://blog.galasoft.ch/archive/2011/09/25/detecting-design-mode-in-windows-8winrt.aspx
 
[2] 
http://blog.roboblob.com/2010/01/17/wiring-up-view-and-viewmodel-in-mvvm-and-silverlight-4-blendability-included/
 
 

 impostare il design data context con una specie di IoC a design time?
 
io non faccio nulla di tutto ciò, sono il solito bastian contrario e 
proprio perchè sono un po' "talebano" su certe cose, prima di 
"sporcare" il codice cerco di capire se una soluzione più bella (non 
necessariamente più semplice) c'è, negli anni sono giunto a questo:
 
http://milestone.topics.it/blog/post/design-time-data-reloaded
 
 
(ad oggi è un po' diverso ed è disponibile su Radical)
 
Purtroppo funziona solo con wpf perchè ha il supporto per 
ICustomPropertyDescriptor, sarà adattabile sicuramente a Silverlight 5 
perché introduce un supporto simile.
 
il mio "workflow" ad oggi è una cosa del tipo:
 
diciamo che hai un view model del tipo:
 
class PersonViewModel : BaseBlaBlaBla..
{
    String FirstName;
    String LastName;
 
    ObservableCollection<AddressViewModel> Addresses;
}
 
class AddressViewModel : BaseBlaBlaBla..
{
   String Country;
}
 
la prima cosa da notare, a differenza del post di cui sopra, è che sono 
classi concrete e non vi è traccia di "interfaccia", mentre al tempo 
del post ero obbligato a definire anche i contratti dei view model 
perchè il meccanismo funzionasse.
 
Dato quel modello posso definire una cose del tipo:
 
class dt_AddressVM : DesignTimeHost<AddressViewModel>
{
   public dt_AddressVM()
   {
       this.Expose( v => v.Country )
           .WithStaticValue( "Italia" );
   }
}
 
class dt_PersonVM : DesignTimeHost<PersonViewModel>
{
   public dt_PersonVM()
   {
       this.Expose( v => v.FirstName )
           .WithStaticValue( "Mauro" );
 
       this.Expose( v => v.Addresses )
           .WithDynamicValue( new ObservableCollection<dt_AddressVM>()
           {
               new DesignTimeAddressViewModel(),
               new DesignTimeAddressViewModel()
           } );
   }
}
 
a questo punto nello xaml lo puoi usare così, in un ipotetico elemento 
grid:
 
<grid d:DataContext="{markup:DesignTimeDataHost Type=dt_PersonVM}" />
 
dove DesignTimeDataHost è una mia markup extension che altro non fa che 
creare un'istanza del tipo "Type", e "d" fa parte del modello di 
namespace(s) usato per i design time data:
 
xmlns:mc="
http://schemas.openxmlformats.org/markup-compatibility/2006
"
xmlns:d="
http://schemas.microsoft.com/expression/blend/2008
"
mc:Ignorable="d"
 
cosa c'è di interessante da notare?
 
- DesignTimeHost<T> non è che faccia poi cose turche, si limita a 
sfruttare il fatto che il motore di data binding è un credulone e se 
gli dici che una proprietà c'è lui si fida di te :-), quindi implementa 
ICustomPropertyDescriptor e ti espone nel costruttore un sistema per 
creare proprietà, l'uso dei generics ti consente di sfruttare 
l'intellisense, ma si potrebbe anche giocare facendo comparire cose che 
proprio non esistono.
 
- ci sono svariati metodi With****Value per esporre i valori a design 
time, lo static value è type safe quindi se esponi Country che è di 
tipo string il valore deve essere string, mentre avrai notato 
WithDynamicValue che viola palesemente la type-safaty perchè 
ObservableCollection<dt_AddressVM> non è 
ObservableCollection<AddressViewModel>...eppure funziona...e luce sia 
:-)
 
il tutto, proprio perchè usa ICustomPropertyDescriptor funziona sia con 
il fx 3.5 che con il 4.x, sto pensando di farne una versione ad hoc per 
il 4.x che sfrutti ExpandoObject (e quindi la possibilità di fare 
binding con "dynamic") perchè semplificherebbe le cose internamente e 
aprirebbe interessanti scenari :-)
 
Io lo uso da molto tempo, se lo volete testare:
http://nuget.org/List/Packages/Radical.Design
 
 
Mi piacerebbe avere feedback, okkio che è una v0.9.0 quindi l'API 
pubblica non è stabile, ci sono alunce cose che non mi piacciono molto 
ma si potrebbe fare qualche test insieme per giungere rapidamente ad 
una v1.0
 
Suggerimenti sono sempre ben accetti :-)
 
Domani in giro per Lucca vedrò di identificarti :-P
 
.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
 
 

 Domani in giro per Lucca vedrò di identificarti :-P
 
Non ci siamo beccati. :-(
Va be', vediamo di organizzarci per il prossimo anno.
Per me era la prima volta a Lucca Comics: è una cosa incredibile!!! Io 
sono andato principalmente per l'aspetto musicale (ossìa per il concerto 
di Vince Tempera sulla Goldrake Generation) ma anche tutto il resto è 
davvero spettacolare!!!
 
Ciao,
Giulio
 

 metodo .Expose() usato in DesignTimeHost non lo trovo da nessuna parte
 
rettifico: era proprio lì!
(Mia nonna direbbe: "ah, Santa Lucia...")
 
Ciao,
Giulio
 
-- 
 
 
 

 Suggerimenti sono sempre ben accetti :-)
 
Sto elaborando qualche suggerimento.
Posto qui o mando in PVT?
 
Ciao,
Giulio
 
-- 
 
 
 

 ObservableCollection<AddressViewModel>...eppure funziona...e luce sia :-)
 
OK, credo di esserci arrivato: quando la collection viene bindata, il 
motore di binding ripete la trafila, pertanto trovando dei 
DesignTimeHost<AddressViewModel> succede quello che è successo con 
l'oggetto "padre". Giusto?
 
Ciao,
Giulio
 
-- 
 

 "padre". Giusto?
 
yes :-)


 Giulio


 spettacolare!!!
 
:-)


 come preferisci, o qui oppure mauro [ at ] topics [ dot ] it
 
Preferisco qui.
Sarà OT?
 
Ciao,
Giulio
 
-- 
 
 
 
 

 Sarà OT?
 
proseguiemo sul thread, la blendability è argomento in topic, magari 
l'implementazione specifica no, ma può far comodo a tutti. C'è sempre 
l'ignore-thread :-)


 ObservableCollection<AddressViewModel>...eppure funziona...e luce sia :-)
 
Come ti dicevo in precedenza, sto leggendo molto il codice (lo trovo un 
esercizio molto utile per migliorare lo stile, e inoltre mi viene più 
facile che leggere la doc delle API... :-)) per darti qualche feedback.
 
Intanto comincio con questo: ho qualche problema a capire come funziona 
e a cosa serva il metodo ITropertyBuilder<T>.WithLiveValue(...), e 
adesso cerco anche di spiegare perché.
 
1. (osservazione dovuta solo alla lettura del codice) se al momento 
dell'invocazione del metodo in questione il membro this.property è NULL, 
questo viene passato al .ctor() di LiveDesignTimeProperty, che lo passa 
a quello di DesignTimeProperty<T, TValue> che lo passa a quello di 
DesignTimeTimeProperty che passa sourceProperty.Name... ma se la 
property iniziale è NULL, questo solleva una NullRefException, giusto? 
E' voluto?
 
2. (sempre leggendo il codice) nel costruttore di una 
LiveDesignTimeProperty viene creato un PropetyChangedMonitor<T> (dove T 
nel nostro caso è DesignTimeHost<TViewModel>), in cui si imposta di 
osservare la property espressa nell'argomento "liveValueProperty" e si 
imposta la callback con una  lambda (su facebook avrei messo un <3 :-)) 
che altro non fa che sollevare il metodo RaisePropertyChanged(...) di 
DesignTimeHost<T>. Il "cosa" è abbastanza chiaro, ma mi restano dei 
punti oscuri sul "come" e sul "perché". :-)
 
2.1. perché imporre come signature alla callback Action<T, String> e non 
semplicemente Action() visto che nel costruttore di 
LiveDesignTimeProperty realizzi già una closure sugli oggetti che ti 
servono e i parametri attuali della callback non vengono usati?
 
2.2 non mi è chiaro come si sposano il discorso del monitor e quello del 
property descriptor. In poche parole, nel .ctor() di 
LiveDesignTimePropetry() succede questo:
- creo un observer per l'host
- impongo di osservare una property
- nella callback dico di richiamare il metodo RaisePropertyChanged() di 
host (che a sua volta richiamerà quello di Entity, ecc ecc...)
Quando viene scatenato tutto l'ambaradam? In che punto del codice? In 
che scenario? E' possibile avere una cosa del genere a design-time o è 
qualcosa che serve solo se si usa DesignTimeHost<T> come datacontext di 
runtime (cosa che magari può essere assai utile per fare le prove...)?
 
Grazie,
Giulio
 
p.s. ...e che non si dica che non ho studiato! :-P
--
 

 p.s. ...e che non si dica che non ho studiato! :-P
 
:-)


 Comunque ci guardo.
 
ho dato una rapida occhiata, non può essere null, mai, riesci a farmi 
un esempio in cui secondo te può essere null?
 
Tieni inoltre presente che l'obiettivo di DesignTimeHost non è quello 
di proteggere lo sviluppatore, quindi pochi fronzoli e pochi controlli, 
è qualcosa che non finirà mai in produzione deve solo semplificare la 
vita del designer.


 compile time.
 
Questa non l'ho capita.
:-)
 
Grazie e buona giornata/settimana,
Giulio
 
-- 
 

 Giulio
