Posts
42
Comments
64
Trackbacks
12
venerdì 18 aprile 2008
[WPF] starting problems

Per chi ha da sempre programmato utilizzando WindowsForm le novità introdotte con l'avvento di WPF sono veramente tante.

Mi sono accorto però, supportando alcuni team nell'approccio a WPF, che il primo vero scoglio/novità che si incontra "migrando" verso questa nuova tecnologia è la filosofia su cui si basa il sitema di layout.
L'utilizzo (corretto) di WPF "spinge" verso la definizione di un layout fluido, combinando i vari container a disposizione, e non più posizionale. Viene enfatizzato il concetto di creazione di un layout più flessibile che possa adattarsi correttamente al ridimensionamento delle window e al cambiamento dei dati contenuti/presentati.

Non che con WinForm questo non fosse possibile (TableLoyoutPanel e FlowLayoutPanel docet), ma con WPF questo modo di "fare layout" è lo standard de-facto (a meno di particolari esigenze, come per esempio la fase di "drawing").
Non credo di esagerare dicendo che WPF sovverte completamente il modello di layouting del .Net fx 2.0, rendendo il "flow-based" layout lo standard e fornendo un supporto "semplice" per il layout posizionale.

Una volta superato questo primo scoglio...bhe ci sono le DependencyProperty, i RoutedEvent, il DataBinding, gli Stili e chi più ne ha più ne metta ;-)

Che ne pensate?

posted @ venerdì 18 aprile 2008 1.42 | Feedback (4)
mercoledì 27 febbraio 2008
[NH] Delete...something

Con NH è "molto semplice" gestire il life-cycle di un oggetto, ma a volte nasce l'esigenza di poter effettuare operazioni che esulano dalle semplici CRUD sulla singola entità.

Un esempio semplice è quello che mi è capitato oggi: cancellare, a fronte di una certa operazione dell'utente, tutti i record di una tabella che avevano particolari requisiti.
Che fare? Caricare tutte le entità in questione e poi cancellarle una ad una? naaaaaaaaaaaaaaaaahhhhhhhhhhhhhh!!!

La session di NH mette a disposizione il metodo Delete con quattro overload. Il primo, quello di cui sopra, per l'eliminazione del singolo oggetto. Gli altri tre permettono di specificare una query SQL o HQL per definire quali sono gli elementi che devono essere eliminati.

Per fare un semplice esempio, svuotare la tabella degli utenti:

session.Delete("from User");

Facile no?

posted @ mercoledì 27 febbraio 2008 10.47 | Feedback (0)
mercoledì 20 febbraio 2008
[WPF] WPF.Start()

Come per qualunque altra applicazione, anche le applicazioni WPF hanno bisogno di un entry point, ovvero il famoso metodo "Main" decorato con l'attributo [STAThread].

Con WPF abbiamo (almeno) tre modi per poter definire quale deve essere il "punto di ingresso" della nostra applicazione. Tralasciando il tradizionale metodo "lato C#" analiziamo gli altri due modi (molto simili) che XAML ci mette a disposizione:

1. Creo un file EntryPoint.xaml e il relativo EntryPoint.cs e li "lego" tra di loro:

2. Creo un unico file EntryPoint.xaml senza dover necessariamente definire il relativo file EntryPoint.cs

 

NOTA: In entrambi i casi la build action del file xaml deve essere impostata su ApplicationDefinition.

Dando un occhio alla cartella obj del nostro progetto, si nota subito che, in entrambe le soluzioni il compilatore genera un file EntryPoint.g.cs praticamente identico, se non per il fatto che, nel secondo caso, non avendo un corrispondente file C# con un namespace correttamente definito, il compilatore assegna al file creato il namespace XamlGeneratedNamespace.
E' in questo file che viene generato, oltre al metodo InitializeComponent(), il metodo che fungerà da entry point per la nostra applicazione:

Sarà che sono prolisso, sia quando parlo, sia quando scrivo, ma l'idea che il codice deve essere autoesplicativo ha sempre caratterizzato il mio modo di vedere...insomma se devo scegliere, preferisco di gran lunga la prima e più "completa" soluzione.

posted @ mercoledì 20 febbraio 2008 23.06 | Feedback (1)
lunedì 28 gennaio 2008
[LINQ2SQL] Modalità di caricamento

Nell'ottica di sfruttare al meglio le potenzialità di un ORM in genere, e di LINQ2SQL in particolare (anche se a mio modesto parere è un "ibrido") bisogna, come da molte parti è già stato suggerito, "ricordarsi di non dimenticare" che sempre con un database relazionale abbiamo a che fare.
Questo preambolo per dire che il miglior suggerimento che si può dare a chi inizia ad utilizzare Linq2Sql è quello di non dimenticarsi MAI di abbinare l'utilizzo di un profiler per vedere cosa stiamo cercando di far fare al nostro db...

In un semplice esempio (il database di riferimento è il "famoso" AdventureWorks) possiamo notare come l'utilizzo fuori controllo di designer e strumenti RAD possono "deviare" dal corretto/miglior utilizzo delle tecnologie che abbiamo a disposizione:

 

Di default il caricamento definito nel DataContext è un "caricamento tardivo", quindi nell'esempio di codice seguente il caricamento degli indirizzi che soddisfano il criterio impostato viene scatenato nel momento in cui inizia l'iterazione sul risultato, all'interno del ciclo foreach.
La selezione del nome dello stato (o della provincia) associata all'indirizzo corrente scatena, ad ogni ciclo nuovi round-trip sul database, se l'oggetto StateProvince non è ancora stato caricato (non presente nel DataContext).

E il risultato del profiler è impietoso...

Questo semplice caso è lampante e mette in evidenza come, senza conoscere la modalità con cui Linq2Sql "mappa" le nostre query, rischiamo veramente di mettere sotto-stress il database, facendolo lavorare nel modo a lui meno congeniale.

Per un corretto caricamento (in questo caso), bisogna istruire il DataContext affinchè i round-trip sul database siano limitati e quindi le n-select precedenti siano effettivamente un'unica richiesta sulle tabelle degli indirizzi e degli stati legate tramite una join.

Così facendo il risultato la query creata da Linq2Sql, diciamo così, mi piace di più:

exec sp_executesql N'SELECT
[t0].[AddressID],
...
[t1].[StateProvinceID]
...
FROM [Person].[Address] AS [t0] INNER JOIN [Person].[StateProvince] AS [t1] ON [t1].[StateProvinceID] = [t0].[StateProvinceID]
WHERE [t0].[City] LIKE @p0',N'@p0 nvarchar(2)',@p0=N'A%'

posted @ lunedì 28 gennaio 2008 9.34 | Feedback (1)
venerdì 25 gennaio 2008
[WinForm] Rendere trasparente un controllo

Esistono due tipologie di trasparenza per il background di un controllo:

  • Il controllo è reso trasparente per rendere visibile la form sottostante.
    Per ottenere questo risultato basta semplicemente impostare il valore della property BackColor del controllo a Color.Transparent, come nell'esempio seguente:

 

  • Il controllo è reso trasparente in modo da rendere visibile il desktop sottostante.
    Per ottenere questo risultato, bisogna impostare la property TransparencyKey della form che contiene il controllo e la proprietà BackColor del controllo stesso, al medesimo valore, come nell'esempio seguente:

posted @ venerdì 25 gennaio 2008 2.16 | Feedback (0)
lunedì 21 gennaio 2008
[WinForm] RichTextBox & AllowDrop

Questa sera, implementando la funzione di Drag&Drop verso un oggetto di tipo RichTextBox, mi sono accorto di una strana scelta di "design" relativa a questo oggetto.
La proprietà AllowDrop (e così anche gli eventi DragEnter e DragDrop), derivata dalla classe base Control, oltre a essere ridefinita (e fin qui, "poco male") è anche "marchiata" come Browsable = false, rendendola invisibile a design-time nella finestra delle proprietà.

Certo, non è un gran problema...è possibile nel costruttore della Form, piuttosto che nell'evento Load, impostare il tutto molto semplicemente nel modo seguente:

...ma questa proprio non l'ho capita!

Alla prossima
-melkio-

posted @ lunedì 21 gennaio 2008 10.54 | Feedback (0)
lunedì 17 dicembre 2007
[C#] Debuggare funzionalità a design-time

Creare Component e ExtenderProvider custom significa implementare funzionalità che forniscono comportamenti "ad-hoc" sia a design-time sia a run-time.
E' quindi necessario verificare che i comportamenti degli oggetti creati soddisfino le specifiche richieste in entrambe le modalità.

Per testare le funzionalità a run-time...nulla di nuovo: breakpoint nel codice e "press F5".
Per debuggare le funzionalità a design-time, invece, è necessario utilizzare una seconda istanza di Visual Studio per "hostare" l'istanza di Visual Studio che contiene il codice sorgente dei nostri "custom component" in modo tale da poter usufruire delle funzionalità del debugger anche a design-time.
Per fare questo bisogna seguire poche, semplici operazioni:

  • tasto destro del mouse sul progetto che contiene i nosti componenti custom e quindi 'Properties' e poi 'Debug'
  • impostare la 'Start Action' su 'Start External Program' e impostare le opzioni di avvio (Start Option) in modo tale che il parametro passato non sia altro che il path completo della nostra solution, come mostrato nell'immagine seguente:

 

Premendo F5, e quindi avviando l'applicazione in modalità debug, viene avviata una nuova istanza di Visual Studio, che altro non fa che caricare la nostra solution, contenente il codice da debuggare. Impostando breakpoint a destra e a manca ;-) abbiamo la possibilità di debuggare le funzionalità dei nostri componenti anche a design time.

Alla prossima...
-melkio-

posted @ lunedì 17 dicembre 2007 5.10 | Feedback (0)
martedì 4 dicembre 2007
[MbUnit] CombinatorialTest & TupleValidatorMethod

Da un po', su suggerimento di Claudio, ho migrato dal pur utile NUnit al sempre più indispensabile MbUnit.
Non che NUnit non soddisfasse le mie necessità, ma solamente utilizzando MbUnit mi sono accorto quante feature utili mette a disposizione questo framework di test, permettendomi di scrivere molte meno righe di codice.

Per fare un esempio, quando sto testando che il setting di una proprietà di una mia classe, che accetta come "parametro di input" un enumerativo  faccia quello che deve, a rigor di logica e soprattutto per completezza dovrei testare che per ogni valore possibile dell'enumerativo, il test sia 'Passed'.
Perfetto!!! MbUnit fornisce un attributo, CombinatorialTest appunto, che, a fronte di un unico metodo, esegue ricorsivamente il test per tutti i valori dell'enumerativo (o di una factory costruita ad-hoc) in questione.

Nell' esempio precedente, il metodo di test 'TestMyEnumProperty' viene eseguito quattro volte, una per ogni valore dell'enumerativo.
Questo approccio, oltre a permettermi di scrivere in un unico metodo il test di tutti i valori, mi solleva dalla preoccupazione di mantenere allineata la mia batteria di test qualora aggiungessi o togliessi possibili valori all'enumerativo.

Utilizzando l'esempio precedente, immaginiamo di voler eseguire il test per tutti i valori dell'enumerativo, tranne che per il valore 'None'...nessun problema, MbUnit fornisce un attributo, TupleValidatorMethod, che permette di filtrare i valori secondo una logica personalizzata.
Ecco come si utilizza:

Basta "istruire" l'attributo CombinatoriaTest del fatto che prima di eseguire i test con ogni valore dell'enumerativo deve eseguire un metodo che estrae/filtra quali valori soddisfano le mie aspettative e quali, invece, no.
Il metodo "FilterNonValue" ha una firma specifica:

  • il parametro di input deve essere dello stesso tipo del parametro che viene passato al metodo di test (nel nostro caso MyEnum)
  • il tipo di ritorno deve essere un Boolean

Prima di eseguire i test, ognuno con il valore specifico dell'enumerativo, la lista di possibili valori viene filtrata attraverso il metodo FilterNoneValue, che contiene la nostra logica di "acceptance value". Solo i valori che soddisfano tale logica (il metodo FilterNoneValue ritorna, quindi, true) vengono effettivamente passati al test.

Nel nostro caso, il test (TestMyEnumProperty), che prima veniva eseguito quattro volte, ora verrà eseguito solamente tre volte con i valori 'Value1', 'Value2' e 'Value3'.

 

Insomma +1 per MbUnit!

posted @ martedì 4 dicembre 2007 22.18 | Feedback (0)
venerdì 30 novembre 2007
[C#] Il compilatore C# e un comportamento bizzaro

La scorsa settimana, durante un corso, un ragazzo mi ha fatto notare un comportamento del compilatore C#, che a prima vista può sembrare alquanto bizzarro.
Il pezzo di codice incriminato è il seguente (o meglio "simula", più o meno fedelmente, quanto proposto in aula):

Compilando questo codice, il compilatore ci "sbatte" in faccia il seguente errore:

"A local variable named 'text' cannot be declared in this scope because it would give a different meaning to 'text', which is already used in a 'child' scope to denote something else"

Ma come?
Pensavo proprio che lo scope della terza variabile di nome text, "prendesse vita" nel momento in cui veniva dichiarata in modo da non "disturbare" la creazione delle altre due variabili con lo stesso nome in blocchi differenti.

Bhe, sicuramente è un comportamento particolare del compilatore, giustificato/giustificabile leggendo attentamente le specifiche del linguaggio, che recitano, bene o male (ho fatto copia e incolla ... quindi credo di avere poche possibilità di errore :-P), così:

"The scope of a local variable declared in a local-variable-declaration is the block in which the declaration occurs. It is an error to refer to a local variable in a textual position that precedes the local-variable-declarator of the local variable. Within the scope of a local variable, it is a compile-time error to declare another local variable or constant with the same name."

Ovvero, la visibilità della terza variabile di nome text, seppur definita successivamente alle altre due, caratterizzate tra l'altro da due scope 'child' (rettangoli rossi), è tutto il blocco nella quale tale variabile è stata definita (rettangolo blu).

La mia opinione personale è che tale comportamento è da ascrivere ad un controllo che il compilatore fa sul codice che noi scriviamo per garantirne una minore possibilità di errore. Anche perchè, per ovviare a tale "problema" basta esplicitare la nostra necessità di avere tre variabili con lo stesso nome nello stesso metodo (ma chi mai potrebbe avere una tale necessità? :-S) identificando un ulteriore blocco che "limiti" la visibilità della terza variabile text:

Sperando di non aver interpretato male le specifiche, mi auguro di aver soddisfatto la curiosità di Luigi ;-)
Alla prossima

-melkio-

posted @ venerdì 30 novembre 2007 11.00 | Feedback (3)
venerdì 28 settembre 2007
[C#] DateTime.TryParse

Oggi mi sono imbattuto in un comportamento del framework che, almeno all'inizio, mi è sembrato alquanto strano...come si può capire dal titolo del post mi sto riferendo al metodo TryParse della classe DateTime e a come viene "parserizzata" la stringa in input a tale metodo, per verificare se si è in presenza o meno di una data corretta...

Dovendo verificare che la stringa contenuta in un TextBox fosse una data formattata correttamente, mi sono trovato in una strana situazione:

 ...
 
if (DateTime.TryParse(TextBox.Text, out date))
 ...

il valore ritornato dalla funzione era true, ma la TextBox conteneva un valore che io ritenevo, per lo meno, anomalo...ovvero "13/1" . Ma come? Un bug?!?!? (come al solito a pensare male...)

Altro che bug...la funzione è fin troppo intelligente ...infatti prima di "alzare bandiera bianca" e affermare che la stringa in ingresso è tutt'altro che una data, le prova proprio tutte cercando di "ricondurre" l'input ad una data accettabile...

Nel caso in oggetto, la stringa "13/1" viene interpretata come la data "13/01/2007 00:00:00"...
...ma non solo, continuando a giochicchiarci mi sono reso conto anche dei seguenti risultati:

 DateTime.TryParse("13/1/2", out result);     // true & result = 13/01/2002 00:00:00
 
DateTime.TryParse("13/1/22", out result);    // true & result = 13/01/2022 00:00:00
 
DateTime.TryParse("13/1/200", out result);   // true & result = 13/01/0200 00:00:00

Bhe non ho scoperto nulla di nuovo...bastava leggere la documentazione!

This method attempts to ignore unrecognized data and parse s completely. It ignores unrecognized data if possible and fills in missing month, day, and year information with the current time. If s contains only a date and no time, this method assumes the time is 12:00 midnight. Any leading, inner, or trailing white space character in s is ignored. The date and time can be bracketed with a pair of leading and trailing NUMBER SIGN characters ('#', U+0023), and can be trailed with one or more NULL characters (U+0000).

"...sovvertiamo le gerarchie..."

posted @ venerdì 28 settembre 2007 9.59 | Feedback (1)
News
Habemus Logo...

Habemus Logo...il logo del mio blog



Il mio profilo su LinkedIn

View Alessandro Melchiori's profile on LinkedIn



i like it...

Xobni outlook add-in for your inbox