Differenze tra Web Site e Web Application Project in Visual Studio

9 marzo 2011

Secondo me è fondamentale capire quali siano le reali differenze tra questi due tipi di progetti, io personalmente sono abituato ad utilizzare sempre e solo Web Application Project ma mi è capitato di lavorare su progetti in cui è stato utilizzato un “Web Site” (guarda caso proprio un progetto in Team). Conoscerne le differenze è un buon punto di partenza per poter capire quale delle due soluzione è più giusto scegliere in base al contesto.

Web Site

Un progetto “Web Site” non è altro che un insieme di file raggruppati all’interno di cartelle e sotto cartelle il cui tipo varia dalla semplice immagine, al file di testo per finire con i file di codice dell’applicazione. Non esiste un progetto reale associato ma una cartella root in cui tutto è contenuto. Un progetto di questo tipo è compilato dinamicamente mentre l’applicazione è in esecuzione e non contiene al suo interno una cartella bin.

Vantaggi:

  • non è necessario avere un file di progetto o una cartella virtuale per la sua esecuzione
  • il sito può essere facilmente distribuito facendo un copia incolla dei file da cui è composto

Svantaggi:

  • non essendoci il file di progetto non è possibile avere un file di questo tipo con cui, facendo doppio click, è possibile aprire il progetto ottenendo l’elenco completo di tutto il materiale da cui è composto.
  • per DEFAULT tutti i file che sono presenti all’interno della cartella del progetto sono da considerare parte stessa di questo, per tale motivo, se alcuni di questi vogliamo escluderli, dobbiamo inserire un suffisso per i file in questione: .exclude
Web Application Project

Questo tipo di progetto è molto più con un semplice “Web Site”. Pensato più in la nel tempo rispetto al suo predecessore, è considerato come un Add On di Visual Studio, aggiunto per la prima volta a partire da VS 2005, viene predisposto anche nei nuovi ambienti (Visual Studio 2008 e 2010).

Vantaggi:

  • Tutto il codice viene compilato in un singolo Assembly il cui contenuto viene piazzato all’interno della cartella bin.
  • Escludere file che non servono direttamente nel progetto è più semplice, essendoci una soluzione ed un file di progetto che ne identifica la struttura.

Svantaggi:

  • Condividere il lavoro con altri colleghi risulta più complesso nel caso si utilizzi Microsoft Visual Source Safe e roba simile.

Chiariti questi macro concetti diventa più facile decidere, in ogni caso la documentazione MSDN mette a disposizione molta più roba in merito, il mio articolo vuole essere un riassunto molto “short” di ambedue le possibilità. A voi la scelta developer!

.Net – Windows Form Applications & Context Sensitive Help

2 marzo 2011

Esistono diverse strategie con cui è possibile realizzare un Help che realmente possa essere utile. Il .Net Framework, per le applicazioni “Windows Form” mette a disposizione una serie di strumenti che è possibile utilizzare a proprio piacimento per la realizzazione di una documentazione in linea, tema la cui importanza non va assolutamente sottovalutata perché un buon software è sempre corredato da una buona documentazione.

Oggi voglio mostrarvi come ho realizzato una guida in linea sensibile al contesto (Context Sensitive Help) all’interno di una Client Application realizzata con il .Net Framework.

* Tralasciando la spiegazione di come si realizza una guida in linea (Microsoft Compiled HTML Help), argomento su cui ci sarebbe molto da discutere, questo post vuole spiegare come integrare un file di questo tipo all’interno di un’applicazione Windows Form.

Partiamo dai presupposti, un oggetto di tipo System.Windows.Forms.Form espone una serie di proprietà, tra queste troviamo anche un evento chiamato HelpRequested che lo sviluppatore può eventualmente implementare, a riguardo la documentazione parla chiaro:

L’evento HelpRequested viene solitamente generato quando l’utente preme il tasto F1 o fa clic su un pulsante della Guida sensibile al contesto.

L’implementazione risulta molto semplice, nei form in cui vogliamo fornire aiuto possiamo decidere di invocare un metodo che possa, in base al contesto, darci informazioni utili reperite direttamente dal file di guida.

Inoltre è necessario sapere che un file di guida “Microsoft Compiled HTML Help” contiene al suo interno una serie di file html compressi, questi file vengono associati ad un identificatore univoco definito in un file Header (scritto in C) e che riporta il riferimento ad ogni link, questo file viene utilizzato in fase di compilazione della guida e serve propri per indicizzare i contenuti della nostra documentazione per poterla successivamente interrogare utilizzando un TopicId.

Ora, all’interno dei Windows Form sono si presenti delle proprietà per la gestione dell’Help ma non sono quelle di cui noi abbiamo bisogno (in pratica il .Net Framework mette a disposizione una serie di Tooltip che vengono visualizzati per dare aiuto). Supponiamo di aver definito nel nostro file di guida un argomento chiamato “Come effettuare il login”, a questo è stato assegnato l’indice 1000, come fare per ottenere un focus su questo argomento nella guida in linea direttamente dall’applicazione? La soluzione che ho applicato è molto semplice, ogni controllo, all’interno di un applicazione Windows espone una proprietà chiamata Tag che è di tipo Object, lo scopo di questa è proprio memorizzare informazioni aggiuntive nel nostro form come per esempio oggetti, stringhe o quanto ci possa tornare più utile, io ho deciso di inserire in questo campo il TopicId che dovrebbe essere richiamato se l’utente chiede aiuto durante l’esecuzione dell’applicazione, successivamente ho realizzato un oggetto che ho chiamato HelpProvider il cui compito è attivare l’help in linea su un determinato form:

internal sealed class HelpProvider 
{
	//Nome del file guida
	private const string ChmFileName = "Docs.chm";
	private Form _form;

	internal Form Form 
	{ 
		get 
		{ 
			return (this._form); 
		} 
	}

	private HelpProvider()
	{
		this._form = null;
	}

	internal HelpProvider(Form form)
	{

		HelpNamespace = Path.Combine(
			Environment.CurrentDirectory,
			ChmFileName);
		
		_form = form;
		_form.HelpRequested += new HelpEventHandler(OnHelpRequestedAction);
	}

	internal void TryAsk()
	{
		Control ctrl = null;
		if ((this._form.ActiveControl != null) 
			&& 
		    (!IsNull(this._form.ActiveControl)))
			ctrl = this._form.ActiveControl;
		else
			ctrl = _form;

		if (ctrl.Tag == null)
		{
			Program.ShowWarning("Unable to retrive help information for this argument.");
			return;
		}

		Help.ShowHelp(ctrl, HelpNamespace, HelpNavigator.TopicId, ctrl.Tag as string);
	}

	/// <summary>
	/// Event Handled when an help request is execute by pressing F1
	/// </summary>
	/// <param name="sender" />
	/// <param name="hlpevent" />
	private void OnHelpRequestedAction(object sender, HelpEventArgs hlpevent)
	{
		this.TryAsk();

		hlpevent.Handled = true;
	}
}

Il funzionamento è molto semplice, questo oggetto riceve in ingresso un Form su cui imposta l’evento  HelpRequested per poter successivamente intercettare le richieste da parte dell’utente, resta in attesa, quando una richiesta viene inoltrata, viene scatenato l’evento OnHelpRequestedAction in cui viene invocato il metodo TryAsk.

Questo metodo effettua diverse operazioni, prima di tutto legge il valore del Tag del controllo che gli viene passato, se questo valore è diverso da NULL,  invoco un metodo, ShowHelp, che fornisce la classe Help del .Net Framework in cui passo:

  • ctrl: Controllo che chiede Aiuto (un form o un qualsiasi controllo presente nella maschera corrente)
  • HelpNamespace: nome del file di guida da invocare per la documentazione
  • HelpNavigator.TopicId: modalità di lettura della documentazione,cioè, quando apro la guida, cosa devo fare esattamente, se cercare per argomento, per indice etc.
  • ctrl.Tag: identificativo dell’argomento di cui vogliamo le informazioni.

L’HelpProvider, può essere facilmente utilizzato in questo modo: nel form in cui vogliamo rendere attiva la funzionalità di help, dichiariamo una variabile privata di tipo HelpProvider che andiamo ad inizializzare nel costruttore del nostro Form come nell’esempio che segue:

public class MyWindowsApp : System.Windows.Forms.Form {
	private HelpProvider _helpProvider;
	
	public MyWindowsApp() {
		_helpProvider = new HelpProvider(this);
	}
}

Con queste poche righe di codice abbiamo attivato un help in linea completo e funzionante!

Ricordate che per funzionare è necessario che i controlli e / o direttamente i form per cui devono essere disponibili informazioni, devono avere la variabile Tag valorizzata con un certo indice equivalente ad un TopicId del nostro file “Windows Compiled HTML Help”, in caso contrario l’help non viene mostrato.

Errore con SqlDataReader & Output Parameters in Stored Procedure

17 febbraio 2011

Molto spesso mi capita di dover lavorare su progetti realizzati in .Net 2.0 in cui viene fatto largo uso del namespace System.Data.SqlClient, è sempre molto spesso mi capita di incontrare un problema molto antipatico di cui dimentico puntualmente la soluzione!

Il problema è questo: supponiamo di voler implementare la chiamata ad una Stored Procedure il cui compito è:

  • restituire un elenco di risultati
  • restituire dei valori in output

La Stored Procedure accetta in input N parametri e restituisce in output altri M parametri. Per l’esempio che voglio proporvi ne utilizzeremo uno che si chiama @RowCount. Nella maggior parte dei casi viene commesso un errore logico durante la scrittura del codice:

//...
command = DataFactory.GetProcedure(SearchProcedure);
command.Parameters.Add("@RowCount", SqlDbType.Int, 4).Direction = ParameterDirection.Output;
DataFactory.OpenConnection();		

reader = command.ExecuteReader();
// L'errore che commettiamo è qui:
pi.RowCount = (int)command.Parameters["@RowCount"].Value;

//...

Cosa succede? In questa porzione di codice vengono effettuate diverse operazioni:

  • Viene inizializzato un comando
  • Viene specificato un parametro in output che dovrà essere valorizzato nella stored procedure e restituito nella variabile di output
  • Viene eseguita la query ed il risultato viene caricato in un SqlDataReader
  • Viene prelevato il valore del parametro in output

Il problema si può verificare a partire dalla descrizione specificata in grassetto (nell’elenco) e si verifica proprio se eseguiamo il passo successivo.

In pratica non è possibile prelevare dei valori in output ad una stored procedure, tramite un SqlCommand, se alla connessione è associato un SqlDataReader aperto, fin quando non invochiamo il metodo Close() dell’oggetto reader, le variabili in output della SqlParameterCollection non vengono caricate, motivo per cui la porzione di codice sopra mostrata genera un eccezione in riferimento al fatto che il .Value è NULL. Se invece proviamo a modificare il codice inserendo un reader.Close() prima di prelevare i valori in output, il tutto funziona correttamente!

Per informazioni più dettagliate sull’argomento potete cliccare qui. Nello specifico la frase che più ci interessa (e che mi salva la vita ogni volta) è questa:

If your Command contains output parameters or return values, they will not be available until the DataReader is closed.

WPF – Dependency Properties

3 febbraio 2011

Quando inizi a lavorare con WPF, ti capita fin da subito di dover avere a che fare con le DependencyProperties. Sono molto simili alle proprietà che normalmente utilizziamo sotto .Net ma un po’ più complesse e potenti.

La differenza principale è che il valore di una proprietà è direttamente leggibile da un membro privato della classe o dalla classe stessa che la implementa, mentre il valore di una DependencyProperty viene risolto dinamicamente quando viene chiamato il metodo GetValue che viene ereditato da DependencyObject.

Quando il valore di una DependencyProperty viene settato, questo non viene memorizzato subito nell’oggetto contenitore ma in un dizionario di chiavi e valori che viene fornito da DependencyObject. La chiave corrisponde proprio al nome della proprietà ed il valore corrisponde al contenuto di questa.

I vantaggi che si possono ottenere utilizzando le dependency properties sono molti:

  • Si eliminano Memory Footprint: se pensiamo che nella maggior parte dei casi il valore delle proprietà dei nostri oggetti rimane invariato per tutta l’esecuzione del programma aumentando in ogni caso l’utilizzo di memoria per l’applicazione, le dependency properties risolvono questo problema memorizzando solo le proprietà che vengono modificate. I valori di default vengono memorizzati una sola volta all’interno delle proprietà di dipendenza.
  • Ereditarietà dei valori: quando si accede ad una proprietà, questa viene letta utilizzando una strategia di risoluzione secondo la quale se si verifica la situazione in cui nessun valore è impostato, la proprietà di dipendenza continua a cercare seguendo uno schema ad albero fin quando non trova un valore. Se per esempio in un controllo imposto la proprietà FontWeight, questa viene impostata anche per tutti gli elementi figlio eccetto quelli per cui non predisponiamo personalmente un comportamento diverso.
  • Meccanismo di notifica: le dependency properties hanno un meccanismo automatico di notifica. In pratica è possibile impostare una dependency property in modo tale da poter ricevere una notifica nel caso in cui questa cambi.
Strategie di risoluzione dei valori

Ogni volta che accediamo ad una dependency property, internamente si scatena un meccanismo automatico in grado di risolvere il valore della proprietà, questo meccanismo segue uno schema logico secondo cui prova a cercare un certo tipo di valore fino ad arrivare ad un Default, lo schema di seguito chiarisce meglio il concetto:

ResolvePropertyStrategy

Ogni controllo registra un set di DependencyProperty nella classe statica. Ognuna di queste consiste in una chiave che deve essere univoca per tipo oltre ad una serie di metadati che contengono le chiamate di callbacks ed i valori di Default. Tutti i tipi che vogliono utilizzare le DependencyProperties devono derivare da DependencyObject. Questa classe base definisce un dizionario di chiavi – valore che contengono le proprietà della classe che eredita. Una chiave in ingresso è una chiave specificata con una DependencyProperty.

Quando viene effettuata una chiamata ad una DependencyProperty, .Net esegue dietro le quinte una chiamata GetValue(DependencyProperty) per accedere al valore. Questo metodo utilizza una strategia di risoluzione che segue a grandi linee lo schema esposto sopra. Se un valore locale è presente, viene letto questo direttamente dal dizionario, al contrario, se non è impostato, viene effettuata una ricerca ad albero in tutte i controlli ereditati. Se nessun valore viene trovato, viene impostato il valore di Default descritto nei metadati.

Questo schema può chiarire meglio l’idea di ciò che accade:

image

Come si crea una DependencyProperty

Per creare una DependencyProperty è necessario aggiungere un campo statico di tipo DependencyProperty sul tipo di riferimento e successivamente chiamare DependencyProperty.Register() per creare un istanza dell’oggetto. Il nome della DependencyProperty deve avere come suffisso Property, è una convenzione di WPF. Per renderlo accessibile come una normale proprietà .Net è necessario creare un Property Wrapper. Questo non fa altro che settare e prelevare internamente il valore della proprietà utilizzando rispettivamente GetValue() e SetValue() dal DependencyObject passando la DependencyProperty come chiave.

// Dependency Property
public static readonly DependencyProperty CurrentTimeProperty = 
     DependencyProperty.Register( "CurrentTime", typeof(DateTime),
     typeof(MyClockControl), new FrameworkPropertyMetadata(DateTime.Now));
 
// .NET Property wrapper
public DateTime CurrentTime
{
    get { return (DateTime)GetValue(CurrentTimeProperty); }
    set { SetValue(CurrentTimeProperty, value); }
}

Ogni DependencyProperty permette di impostare un evento di callback per notifiche di cambiamento, validazione etc. Queste chiamate vengono registrate come viene mostrato di seguito:

new FrameworkPropertyMetadata( DateTime.Now, 
                       OnCurrentTimePropertyChanged, 
                       OnCoerceCurrentTimeProperty ),
                       OnValidateCurrentTimeProperty );

Nella chiamata di callback si verifica se il valore è valido, se questa chiamata restituisce false viene sollevata un eccezione.

private static bool OnValidateTimeProperty(object data)
{
    return data is DateTime;
}
Chiamate di Callback

La chiamata di callback è un metodo statico che viene chiamato ogni qual volta il valore della proprietà cambia. Il nuovo valore viene passato tramite EventArgs, l’oggetto di cui questo valore è cambiato, viene passato come source:

private static void OnCurrentTimePropertyChanged(DependencyObject source, 
        DependencyPropertyChangedEventArgs e)
{
    MyClockControl control = source as MyClockControl;
    DateTime time = (DateTime)e.NewValue;
    // Put some update logic here...
}
Readonly DependencyProperties

Alcune dependency property in WPF sono in sola lettura. Sono utilizzate spesso per riportare lo stato di un controllo come IsMouseOver, si può dedurre che in questi casi non ha alcun senso impostare un setter.

Ma la domanda principale, che molte volte, durante lo studio di questo argomento, mi sono posto è: perché utilizzare dependency properties al posto delle normali proprietà che è possibile utilizzare con .Net?

La prima importante ragione è che con .Net non è possibile impostare dei trigger sulle proprietà.

Create una proprietà in sola lettura è molto simile a creare una proprietà in .Net. All’interno della chiamata DepedencyProperty.Register() si può chiamare DependencyProperty.RegisterReadonly(). Viene restituita una DependencyPropertyKey, questa dovrebbe essere memorizzata in una variabile privata o protetta (Statica) ed in sola lettura. La chiave ti permette di avere accesso al valore e settarlo come normalmente si farebbe con una Property. La seconda cosa da fare è impostare una DependencyProperty pubblica assegnata a DependencyPropertyKey.DependencyProperty. Questa proprietà è in sola lettura e può essere richiamata dall’esterno.

// Register the private key to set the value
private static readonly DependencyPropertyKey IsMouseOverPropertyKey = 
      DependencyProperty.RegisterReadOnly("IsMouseOver", 
      typeof(bool), typeof(MyClass), 
      new FrameworkPropertyMetadata(false));
 
// Register the public property to get the value
public static readonly DependencyProperty IsMouseoverProperty = 
      IsMouseOverPropertyKey.DependencyProperty;    
 
// .NET Property wrapper
public int IsMouseOver
{
   get { return (bool)GetValue(IsMouseoverProperty); }
   private set { SetValue(IsMouseOverPropertyKey, value); }
}

Per concludere, l’argomento può sembrare di difficile comprensione nei primi momenti (tristissimi momenti) ma subito dopo, se si ha modo di poter vedere in azione queste funzionalità la comprensione risulta molto più facile. Spero che questa guida possa essere di aiuto a qualcuno che come me, alle prime esperienze con nuove tecnologie, si trovi spiazzato e disorientato!

Per qualsiasi domanda ci sono i commenti!

Il mio 2010

14 dicembre 2010

Un anno di incazzature, sorprese, delusioni, idee e nuovi progetti ma la sensazione di “non-completezza” è sempre li in agguato. Personalmente ho vissuto questo 2010 pieno di aspettative, con tante idee per la testa, alcune realizzate, altre in parte e poi ci sono tutta quella infinità di progetti che vorrebbero prender vita ma la cui fattibilità è sempre messa in discussione grazie a quelle che sono le 24 ore che tutti noi abbiamo a disposizione e che pur troppo non bastano mai!

Un anno fatto di tanti momenti in cui il nervosismo prende il sopravvento, in cui tanta gente, maleducata e scortese pensa di potersi prendere gioco di te, un anno in cui hai la possibilità di capire che il cliente ha ragione quando paga, non se hai la fortuna di poter scegliere con chi avere a che fare e di selezionare i tuoi lavori. Allo stesso tempo ho avuto modo di lavorare con persone che ti apprezzano e valorizzano, clienti che sono in grado di trasmetterti quella soddisfazione che solo tu, dopo tutto il lavoro che hai fatto, sei in grado di poter percepire.

Un anno fatto di sorprese, una ragazza che ogni giorno continua a sopportarmi nonostante le mie passioni e la mia ostinazione nel volerle sempre parlare dei miei progetti, delle mie ambizioni e dei miei piani futuri, mai stanca del mio continuo parlare; dei genitori ed una sorella fantastica sempre vicini ed in prima linea per questo ragazzo che ha sempre la testa da un’altra parte!

Un anno fatto di idee e progetti, scaturiti dalla voglia di spingersi sempre più verso qualcosa di grande e soddisfacente. Un periodo in cui ho avuto la fortuna di poter collaborare con persone straordinarie accomunati da un obiettivo. Forza di volontà, ottimismo e voglia di conoscenza sono alla base di questo 2010.

Un anno all’insegna della “non-completezza”, quando hai tanto in testa e poco tempo per fare tutto, in alcuni momenti hai la sensazione di non farcela, poi, grazie sempre a quelle persone che ti sostengono e ti sono vicine, una spinta ad essere più forte ed attivo di prima arriva ed è proprio in quei momenti che ritrovi lo spirto giusto per rimetterti a fare quello che hai lasciato a metà.

Un amico, tempo fa, mi ha “regalato” un consiglio, un suo modo di fare che si è rivelato per me grandioso: un foglio, una penna e tanta attenzione nel catturare ogni idea che la testa tira fuori, annotarle e tenerle sempre d’occhio, capire quando è il momento di di mettersi all’opera. Nella mia stanza c’è un foglio, appeso al muro, cosi visibile che nemmeno mia madre può fare a meno di notarlo, tanti punti da completare, tante cose da fare, tante X sopra ad ogni frase per indicare ciò che sono riuscito a fare e ciò che ancora è in attesa di essere fatto. Mi basta guardarlo per sentirmi soddisfatto di quello che sono oggi pensando che domani quella lista sarà ancora più lunga e con più X ad indicare ogni mio nuovo traguardo.

Da quando ho imparato a camminare mi piace correre. (Friedrich Nietzsche)