ASP.NET MVC – Caricare un file JAVASCRIPT EMBEDDED.

25 luglio 2012

Appunto qui per ricordarmene in futuro (visto che dimentico tutto ultimamente). Ho realizzato una libreria di classi in cui, oltre ad oggetti C#, sono presenti diversi file JAVASCRIPT il cui contenuto è necessario perché le classi possano funzionare correttamente all’interno di un’APP MVC.

Come è possibile caricare questi file all’interno di un progetto di questo tipo? Con pochi è semplici passaggi il lavoro è fatto.

  1. Selezionare il file JAVASCRIPT che è necessario importare all’interno del progetto libreria.
  2. Dal menu contestuale (click destro del mouse), selezionare la voce Proprietà.
  3. Nella finestra delle Proprietà modificare Build Action: Embedded Resource.
  4. Nel file AssemblyInfo.cs della del progetto libreria aggiungere una nuova riga di codice:
    [assembly: System.Web.UI.WebResource("YourNamespace.Resource.js, "application/x-javascript")]

Questi passaggi bastano per poter incorporare la libreria all’interno di un’applicazione ASP.NET Web Forms utilizzando ClientScript.GetWebResourceUrl(Type, resourceName), con MVC è necessario eseguire ulteriori passaggi per poter rendere tutto funzionante. Realizziamo per questo scopo un oggetto con cui deve essere possibile ottenere l’URL dei file embedded molto semplicemente.

public static class ResLocator
{
	private const string UrlLocatorMethodName = "GetWebResourceUrlInternal";

	public static string Resolve(Type assemblyObjectType, string resourceName) 
	{
		MethodInfo resourceLocatorMethod = assemblyObjectType.GetMethod(UrlLocatorMethodName, 
			BindingFlags.NonPublic | BindingFlags.Static);
		string url = string.Format("/{0}", resourceLocatorMethod.Invoke(
			null,
			new object[] { Assembly.GetAssembly(assemblyObjectType), resourcePath, false })
		);

		return url;
	}
}

Il metodo Resolve accetta in input un Type che deve necessariamente riferirsi ad un tipo di oggetto contenuto nel progetto libreria e resourceName, cioè il nome della risorsa da includere. Successivamente, referenziando il namespace in cui è contenuto l’helper all’interno della nostra MasterPage, è possibile includere lo script di riferimento in questo modo:

<script 
	type="text/javascript" 
	src="@ResLocator.Resolve(typeof(SampleAssemblyObject), "YourNamespace.Resource.js)" />

Completati questi passaggi ed avviata la nostra applicazione MVC, il risultato dell’inclusione è simile a questo:

/WebResource.axd?d=HWyLh7g77XDqhYfNG0fioE3hSIzYB4uYmoUs3cJSuFRbnk9cZT1AWVuijZ81&t=634788092174760080

Spero possa essere utile anche ad altri per eventuali EMBEDDED RESOURCES!

.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.

ASP.NET MVC – ViewData vs TempData

30 luglio 2010

Questo Framework mette a disposizione diverse funzionalità, una delle tante, che ritengo davvero molto utile, è la possibilità di far transitare dati tra diverse richieste http, questa possibilità si materializza attraverso l’utilizzo di diverse proprietà all’interno della pagina web: ViewData e TempData. Non nascondo la mia iniziale confusione nel capire a cosa servissero due variabili che hanno quasi lo stesso nome, ma poi tutto ha avuto un senso più chiaro e logico!

In due parole: TempData è come ViewData con una sola differenza: TempData può contenere informazioni che vengono conservate per le (massimo) due richieste successive, oltre, il suo contenuto viene automaticamente distrutto. Quindi può essere utilizzata per passare messaggi di errore e roba del genere tra diverse Action, mentre ViewData permette un interazione diretta tra Controller e View.

un mio amico ha chiarito ancor meglio il concetto: in parole povere, il tempdata permette di passare dati tra controller mentre il viewdata è adibito a quelle logiche che restano racchiuse nella stretta corrispondenza view-cotroller (yetanothergeek).

Un esempio concreto può chiarire il concetto, supponiamo di dover implementare due diversi comportamenti:

  • Un login.
  • Il caricamento di dati all’interno di DropDownList presenti in una View.

Per quanto riguarda il Login supponiamo di avere un UserController che implementa diverse view tra le quali c’è appunto Login:

public class UserController {
    ...
    public ActionResult Login(string username, string password) {
	if(!IFormProvider.Authenticate(username, password))
             TempData["LoginErrors"] = "Autenticazione fallita.";
	return RedirectToAction("Index", "Home");
    }
}

La view non è fisicamente presente ma si occupa di verificare che username e password inseriti siano validi, nel caso in cui questa condizione non si verifichi, viene caricato un valore in TempData, LoginErrors, che contiene un avviso per l’utente, successivamente viene effettuato un RedirectToAction che realmente equivale ad un Response.Redirect, se avessimo utilizzato ViewData per memorizzare questo messaggio di errore, la Redirect non ci avrebbe permesso di memorizzare l’avviso per l’utente, TempData invece mantiene lo stato dell’informazioni per la prossima richiesta e se necessario anche per la successiva! Quindi è chiaro che sarà utile per noi sfruttare questo contenitore quando sappiamo di dover spostarci da una Action ad un’altra.

Ora passiamo al secondo esempio in cui è necessario passare dal Controller, informazioni aggiuntive alla View (supponendo di avere già una View con un Model fortemente tipizzato), sempre in UserController abbiamo un’altra view che chiamiamo List, in questa, sono presenti l’elenco completo degli utenti ed un filtro di ricerca con possibilità di filtrare per Ruolo, quindi, oltre a fornire l’elenco degli utenti trovati, dobbiamo fornire un elenco di ruoli che devono essere caricati nella DropDownList:

public class UserController {
    private DatabaseDataContext db = new DatabaseDataContext();
    ...
    private void BindRequiredData() {
	// Elenco completo dei ruoli
	ViewData["Roles"] = new SelectList(db.Roles, "RoleID", "Name");
    }

    [HttpGet]
    public ActionResult List() {
        BindRequiredData();
    }

    [HttpPost]
    public ActionResult List(int RoleID) {
	BindRequiredData();

        // Prelevo gli utenti.
        var users = db.Users.Where(u => u.RoleID == RoleID);

        return View(users.ToList());
    }
}

[ad#ad-lungo]

Ho implementato ambedue le viste, GET e POST, ho preparato un metodo privato che provvede a caricare in ViewData l’elenco dei Ruoli, successivamente nella pagina web posso sfruttare questa informazioni per la generazione della DropDownList. In questo caso si nota come l’utilizzo di ViewData è più adatto al contesto, il suo ciclo di vita equivale a quello della richiesta che, una volta completata, rilascerà ogni risorsa utilizzata.

Condivido queste informazioni in quanto io per prima mi sono ritrovato ad avere falsi problemi di ViewData non funzionante, pretendevo di poter trasmettere messaggi tra diverse Action utilizzando direttamente questo contenitore, poi scoprendo TempData ed analizzando analogie e differenze, tutto è risultato molto più chiaro!

SqlCommand – How to

27 luglio 2010

Leggendo un pò di statistiche sulle visite mi sono accorto che molte persone arrivano qui cercando informazioni su SqlCommand rimandendo ovviamente deluse dal fatto che realmente, informazioni su questo argomento, non ce ne sono. Per questo motivo ho pensato di realizzare un piccolo esempio per spiegare come utilizzare questo comando. Ogni sviluppatore alle prime armi con .Net si ritrova a dover utilizzare questo oggetto per collegare base dati SQL Server e applicazione. Possiamo benissimo dire che sia alla base di ogni attivita tra Server di database e tecnologia Microsoft .Net

Da dove iniziare? Dai presupposti ovviamente: abbiamo una base dati e vogliamo eseguire una query di lettura di dati da una tabella [Contatti] in cui ci sono le colonne [Nome] e [Cognome]. È necessario prima di tutto configurare una stringa di connessiome, dove? Nel web.config, ci posizioniamo in questo file e modifichiamo la configurazione come segue:

 <connectionStrings>
  <add name="Connessione"
       connectionString="server=localhost;
          initial catalog=Rubrica;Integrated Security=SSPI" />
 </connectionString>

La stringa di connessione impostata dichiara una connessione ad un server localhost, con accesso diretto al database Rubrica con autenticazione integrata (username e password correntemente in uso per il login su sistema operativo) Integrated Security=SSPI.
Il passo successivo è richiamare la nostra ConnectionString ed utilizzarla per stabilire una connessione con il server di database è reperire i dati della select.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
public sealed class SqlCommandDemo {
   public DataTable ExecuteDemo() {
      DataTable dt = null;
      // Configurazione della connessione.
      SqlConnection connection = new SqlConnection(
         ConfigurationManager.ConnectionStrings["Connessione"].ConnectionString);
      // Inizializzazione della connessione.
      SqlCommand command = new SqlCommand(
          "SELECT [Nome], [Cognome] FROM [Contatti]",
          connection);
      command.CommandType = CommandType.DirectText;
      try {
          // Apertura della connessione
          connection.Open();
          SqlDataReader dr = command.ExecuteReader(CommandBehavior.SingleResult);
          dt = new DataTable();
          dt.Load(dr);
      }
      catch (SqlException e) {
          // Nel caso in cui la connessione o la stessa query fallisce.
      }
      finally {
          // Rilasciamo tutte le risorse non più
          // necessarie evitando lavoro in più il Garbige Collector.
          if( connection.State == ConnectionState.Open)
              connection.Close();
          connection.Dispose();
          connection = null;

          command.Dispose();
          command = null;
      }
      return dt;
   }
}

[ad#ad-lungo]
Il procedimento è relativamente semplice: inizializzo una SqlConnection, un oggetto preposto per la gestione della connessione con il server di database, successivamente utilizzo l’SqlCommand per istanziare il comando come DirectText ad indicare che sto per effettuare una query diretta sulla base dati. Il resto delle operazioni viene eseguito in un blocco try-catch-finally per gestire eventuali errori che si possono verficare in fase di connessione al server o nell’esecuzione vera e propria della query. Come al solito, spero che queste informazioni siano di aiuto per voi, per qualsiasi domanda i commenti sono a vostra completa disposizione!

ExcelLibrary – Excel could not find anything to print – Impossibile trovare dati da stampare

13 luglio 2010

Qualche giorno fa’ mi sono ritrovato a dover gestire un problema molto antipatico: esportare dati in Excel (partendo da un file già presente in cui è contenuto lo schema dei dati con particolari formule) all’interno di una web application; fin qui nessun problema se non fosse per la mancanza della suite office installata sul server in cui l’applicazione gira! Ho risolto utilizzando una libreria di classi chiamata ExcelLibrary, nella presentazione viene descritta cosi:

The aim of this project is provide a native .NET solution to create, read and modify Excel files without using COM interop or OLEDB connection.

Currently .xls (BIFF8) format is implemented. In future .xlsx (Excel 2007) may also be supported.

Come chiaramente si legge l’inconveniente è che non supporta Excel 2007, problema inesistente per me in quanto devo gestire file in formato 97/2003. Scarico i sorgenti della libreria, ben strutturata, scritta molto bene (è sempre meglio dare un occhiata, anche veloce, per capire con che “tipo” di codice si ha a che fare). Utilizzarla è semplice e con pochi passaggi sono in grado di effettuare le operazioni necessarie per elaborare ed esportare i file. Il tutto viene testato direttamente sul server che risponde perfettamente a tutte le richieste.

Pochi giorni dopo la pubblicazione della release: mi chiama una mia collega che lavora a stretto contatto con il cliente per cui abbiamo fornito questo servizio: Roberto c’è un problema, quando esportiamo i dati e successivamente vogliamo stamparli, viene visualizzato un messaggio di errore:

Impossibile trovare dati da stampare.

Excel could not find anything to print.

All’inizio non riesco proprio a capire quale sia il problema, poi provo a cercare sulla pagina ufficiale della libreria e noto subito di non essere l’unico ad avere questo problema. Leggendo nella sezione Issues qualcuno (un mio collega definisce questo tipo di persona: anime pie) si è interessato alla questione fornendo una soluzione funzionante. Il problema è nella generazione dei file Excel, quando viene creato un nuovo documento per DEFAULT non viene selezionato nessun Worksheet, per tale motivo quando lo stesso file viene aperto con Excel si ottiene il messaggio di errore prima descritto proprio perché l’applicativo non è in grado di capire quale sorgente dati utilizzare per la stampa! Questo difetto lo si può notare aprendo un file generato con ExcelLibrary: tutti gli sheet sono deselezionati.

La soluzione consiste nel modificare i sorgenti della libreria e ricompilare. Fortunatamente è stato molto semplice, i passi da seguire potete trovarli qui (Grazie alla famosa Anima Pia). Visto che ho già effettuato questa operazione ho pensato di fornire il lavoro già fatto, testato è funzionante: [download id="1"]

[ad#ad-lungo]