TecnoLogicamenteCorretti - Il Blog di Fabio Carucci

gennaio 2007 Entries

L'arte di lavorare con le WebPart - #14

Può essere necessario, a volte, inibire ad un utente la possibilità di effettuare il drag'n'drop di una webpart da una zona ad un'altra.

Possiamo farlo mediante la classe WebPartZoneBase e, in particolare, utilizzando la property DragDropEnabled(True|False) che passa il valore alla omonima property della classe WebPartChrome.

L'arte di lavorare con le WebPart - #13

Uno dei metodi canonici che il WebPart Framework mette a disposizione per disabilitare verbi è quello di utilizzare l'override sul metodo FilterWebPartVerbs e passargli la collection di verbi che si vuole abilitare/disabilitare unitamente alla webpart:

protected override WebPartVerbCollection FilterWebPartVerbs(WebPartVerbCollection verbs, WebPart webPart) { WebPartVerbCollection wpvc = base.FilterWebPartVerbs(verbs, webPart); foreach (WebPartVerb verb in wpvc) { if (verb == this.Zone.EditVerb) verb.Enabled = webPart.AllowEdit; } return wpvc; }

ciò che probabilmente non riusciremo a fare, però, è nascondere il verbo "Edit" per una serie di motivi, primo fra tutti WebPartChrome.ShouldRenderVerb che sembra "ignorare" l' AllowEdit=False soltanto per il verbo "Edit".

Per suggerire un possibile workaround rimando al mio post #3 fermo restando il valido uso del codice suddetto per i restanti verbi.

L'arte di lavorare con le WebPart - #12

Quando creiamo verbi custom potremmo avere la necessità di associare azioni particolari, come ad esempio la funzione di narrazione del testo per utenti disabili, che non prevede form di dialogo o da compilare ma soltanto una attivazione o disattivazione della suddetta funzionalità.

Ebbene, ciò significa che accanto al verbo, nel menù a tendina, dovrebbe comparire anche un simbolo di spunta o un checkbox da selezionare/deselezionare; come fare?

Utilizzando la property WebPartVerb.Checked (true|false) possiamo giungere al nostro obiettivo. Quando il suo valore è true accanto al nome del verbo apparirà una checkbox.

Da ricordare, però, che tale proprietà funziona solo con verbi custom.

Recensione: "Essential Windows Workflow Foundation"

Link al libro su Amazon

Scritto a quattro mani da un architetto e da un progettista di WF (Shukla & Schmidt), consiglio la lettura di questo libro a chi desidera acquisire i concetti che sono alla base di quello che considero essere un "framework nel framework".

L'obiettivo del libro non è quello di elencare e disquisire a 360° sui 350 e passa tipi presenti nei tre assemblies che costituiscono il Windows Workflow Foundation, infatti il libro non è un manuale o una reference guide, bensì si pone come spunto di riflessione sui concetti e principi che hanno spinto i due autori a dedicarsi alla progettazione di WF, e lo fa anche con una moltitudine di esempi di codice sparsi nel libro.

Il primo capitolo è dedicato alla descrizione di concetti, quasi astratti, del modello di programmazione di WF;

Nel secondo capitolo si inizia a parlare di activities, cosa sono nel mondo reale, come si implementano nel codice, come si creano workflows composti da activities e come si esegue una applicazione "pilotata" da workflows.

I capitoli 3 e 4 descrivono le features interne delle activities, come si aggiungono activities durante l'esecuzione di un programma, come si gestiscono le exceptions, cosa è la compensation, insomma il ciclo di vita di una activity.

Il capitolo 5 spiega come costruire applicazioni che fungono da hosting per il runtime di WF, mentre nel capitolo 6 si affronta in dettaglio come sia possibile gestire le transazioni in un ambiente come quello di WF.

Infine i rimanenti 2 capitoli danno una idea delle potenzialità di estensione del WF e dei suoi workflows.

A complemento del libro vi sono due appendici molto utili poichè elencano una serie di porzioni di codice relative ai diversi tipi di workflow e alle activity composite.

In conclusione, "Essential Windows Workflow Foundation" è un testo adatto a coloro che vogliono iniziare un percorso di studio su WF ma che hanno necessariamente basi consolidate di .NET e C# 2.0.

 

L'arte di lavorare con le WebPart - #11

Quando lavoriamo con i Verbs, per default nel menù a tendina, accanto ai verbi, non appaiono immagini che rendono più user friendly la nostra webpart.

Per ovviare a ciò, mediante l'uso della property ImageUrl contenuta nella classe WebPartVerb, possiamo ad esempio creare una zona custom e, ogni webpart che verrà inserita in essa, acquisirà i verbi di default e quelli aggiunti, nonchè le immagini (nei formati soliti) che appariranno accanto ad ogni verbo

Ecco un esempio del codice

internal class CustomWebPartVerb : WebPartVerb { private const String _copyImageUrl = "~/Copy.ico"; internal CustomWebPartVerb(WebPartEventHandler serverClickHandler) : base("CustomVerb", serverClickHandler) { } public override bool Enabled { get { return base.Enabled; } set { base.Enabled = value; } } public override string ImageUrl { get { return _copyImageUrl; } set { ; } } }

L'arte di lavorare con le WebPart - #10

Il nostro utente ha aggiunto una webpart in una zona della pagina dal catalogo di webparts che gli abbiamo messo a disposizione. Ha aggiunto dei contenuti all'interno della webpart e, dietro le quinte, glieli abbiamo salvati nello storage di persistenza di AspNet e, avendo l'esigenza di dover lavorare su determinati molteplici contenuti di una webpart, in un nostro db custom.

Il nostro amato utente si rende conto di aver sbagliato pagina e quella webpart non doveva metterla là bensì in un'altra pagina, quindi clicca sull'apposito menù a tendina della webpart e seleziona "Delete".

Sappiamo che aspnet elimina dal suo storage la webpart eliminata, ma resta il record nel nostro db custom.

Come si può sincronizzare questa operazione?

Semplicemente utilizzando l'evento OnDeleting(EventArgs e) della webpart in questione che intercetta il "Delete" nel menù a tendina e permette quindi di invocare un nostro metodo che elimina anche il record sul db custom.

Blog utile?

Se desiderate ricevere in email gli abstract dei miei post, ho attivato un servizio che trovate qui oppure nella colonna a sinistra del mio blog sotto il logo della mia certificazione.

E' una semplice procedura: inserite l'email, digitate il codice di controllo antispam e fino alla fine dei tempi riceverete una mail ogni volta che pubblicherò un post.

Poi, se proprio non ce la fate, potete sempre eliminare la sottoscrizione... :-)

Grazie!

L'arte di lavorare con Workflow Foundation - #1

Dopo aver definito e disegnato un workflow, lo poniamo in esecuzione. Immaginiamo di aver creato un workflow che ci permette di prendere le prenotazioni per mandare un nostro pony express in giro per la città a fare consegne.

Il nostro workflow tiene conto dell'orario di arrivo della richiesta, quindi gira al pony l'elenco, in ordine di arrivo, degli indirizzi a cui recarsi. Se volessimo rendere più ottimizzato il workflow potremmo inserire uno step intermedio (activity) che mette in ordine di vicinanza/lontananza gli indirizzi e fornisce una stima del tempo di arrivo del pony al cliente.

Il workflow è in esecuzione, ma grazie alla classe WorkflowChanges possiamo aggiungere a runtime la nuova activity senza dover interrompere nulla.

InvokeWorkflowActivity newWorkflow = new InvokeWorkflowActivity(); WorkflowChanges changes = new WorkflowChanges(this); Type type = typeof(ReservationWorkflow); newWorkflow.Name = "NewReservationWF"; newWorkflow.TargetWorkflow = type; DelayActivity delay = changes.TransientWorkflow.Activities["delay1"] as DelayActivity; delay.Parent.Activities.Add(newWorkflow); this.ApplyWorkflowChanges(changes);

Ci sono però delle restrizioni riguardo ai cambiamenti che possono essere compiuti su un workflow in esecuzione:

  • Le activities possono essere aggiunte o rimosse da un WF ma le proprietà di una attività esistente (regole escluse) non possono essere modificate;
  • Una activity composita non può avere al suo interno delle sotto-attività aggiunte o rimosse dinamicamente;
  • Una activity con una o più istanze nello stato di Executing, Fault, Canceling o Compensating non può essere rimossa da una istanza di un workflow in esecuzione.

Nei prossimi post scruteremo, con il passare del tempo sempre più in dettaglio, le numerose features di questo nuovo 'framework nel framework'.

[OT] Google Maps non si accontenta...

...e arriva a farti la foto.

Francamente mi sembra che iniziano ad esagerare.

Fonte: La Repubblica

L'arte di lavorare con le WebPart - #9

Quando vogliamo dare all'utente del nostro sito la possibilità di esportare una web part in formato xml sappiamo che occorre innanzitutto abilitare l'export dal web.config mediante


  

In questo modo, tuttavia, se non impostiamo la property ExportMode della web part, l'utente non otterrà l'esportazione a causa del valore di default che è None.

Se però vogliamo che l'esportazione avvenga in maniera parziale, cioè che non tutte le properties vengano esportate, dobbiamo impostare a NonSensitiveData la suddetta property, importante è che nella dichiarazione dell'attributo [Personalizable] ci ricordiamo di passare true come secondo argomento:

[Personalizable(PersonalizationScope.User, true)]

in questo modo le properties della nostra web part che avranno questo secondo argomento verranno escluse dall'esportazione.

L'arte di lavorare con le WebPart - #8

Capita, lavorando con WebParts in connessione, che vi siano una o più di esse con il ruolo sia di Provider che di Consumer.

L'effetto negativo che si può riscontrare è quello, ovviamente, dei riferimenti circolari; serve quindi un modo per determinare ciò e prendere eventuali contromisure: WebPartTracker.IsCircularConnection.

La classe WebPartTracker è sealed e ha una property, IsCircularConnection appunto.

La suddetta property non fa altro che testare se la WebPart che, unitamente al punto di connessione, abbiamo passato al WebPartTracker ha più punti di connessione e, nel caso, restituisce un valore true; a noi resta da verificare che non vi siano riferimenti circolari.

L'arte di lavorare con le WebPart - #7

Mi rifaccio al mio post precedente in cui parlo di IWebPartTable.

Se invece di una tabella dovete passare tra webparts provider e consumer una singola riga di dati, utilizzate l'interfaccia IWebPartRow; il meccanismo è il medesimo, GetRowData per passare una riga e Schema per recuperarne i dati.

Non ripeto l'esempio perchè è similissimo al precedente.

L'arte di lavorare con le WebPart - #6

Ringrazio coloro che hanno risposto gentilmente al mio Poll(icino), quindi, augurandovi un ottimo 2007 (in fondo siamo ancora all'8 gennaio), proseguo con la miniserie in oggetto.

 

Vi occorre mettere in comunicazione una webpart che contiene una tabella e passare il contenuto ad altre webparts che consumano la prima?

Bene, tutto quello che dovete fare è utilizzare l'interfaccia IWebPartTable; essa, sfruttando il meccanismo di connessione del Web Part Framework, consente con un metodo - GetTableData(TableCallback tbc) - e con una property - Schema - di spedire un DataTable verso una o più webparts

public sealed class WebPartProvider : WebPart, IWebPartTable
{
    DataTable _table;

    
public WebPartProvider()
    {
        
// codice che crea un DataTable (table), alcune DataColumn
        // e un pò di DataRow
    
}

    
public PropertyDescriptorCollection tableSchema
    {
        
get
        
{
            
return TypeDescriptor.GetProperties(table.DefaultView[0]);
        }
    }

    
public void GetTableData(TableCallback tableCallback)
    {
            tableCallback(table.Rows);
    }

    
public bool ConnPointON
    {
        
get
        
{
            
object o = ViewState["ConnPointON"];
            
return (o != null) ? (bool)o : true;
        }
        
set
        
{
            ViewState["ConnPointON"] = 
value;
        }
    }

    [ConnectionProvider("Table", 
typeof(TableProviderConnectionPoint), AllowsMultipleConnections = true)]
    
public IWebPartTable GetConnectionInterface()
    {
        
return new TableProviderWebPart();
    }
    

    
public class TableProviderConnPoint : ProviderConnectionPoint
    {
        
public TableProviderConnPoint(....) : base(....)
        {
        }

        
public override bool GetEnabled(Control control)
        {
            
return ((TableProviderWebPart)control).ConnectionPointEnabled;
        }
    }
}

Notate la firma di questo metodo public void GetTableData(TableCallback tableCallback); TableCallBack è delegato a contenere l'indirizzo del metodo che riceverà i dati (per brevità ho omesso alcuni controlli nel codice nonchè firme di metodi) mentre la property Schema contiene lo schema, appunto, dei dati da spedire e ricevere.

Ecco la seconda parte, la webpart consumer invoca il GetTableData e, nel RenderContents ne renderizza il contenuto mediante la property Schema.

public class WebPartConsumer : WebPart
{
    
private IWebPartTable provider;
    
private ICollection _tableData;
    
    
private void GetTableData(object tableData)
    {
      _tableData = (ICollection)tableData;
    }
    
    
protected override void OnPreRender(EventArgs e)
    {      
        provider.GetTableData(
new TableCallback(GetTableData));      
    }
    
    
protected override void RenderContents(HtmlTextWriter writer)
    {      
        PropertyDescriptorCollection props = _provider.Schema;
        
int count = 0;
        
if (props != null && props.Count > 0 && _tableData != null)
        {
          
foreach (PropertyDescriptor prop in props)
          {
            
foreach (DataRow o in _tableData)
            {
              writer.Write(prop.DisplayName + ": " + o[count]);
            }
           
            count = count + 1;
          }
        }              
    }
    
    [ConnectionConsumer("Table")]
    
public void SetConnectionInterface(IWebPartTable provider)
    {
      _provider = provider;
    }
    
    
public class TableConsumerConnPoint : ConsumerConnectionPoint
    {
      
public TableConsumerConnPoint(....)
        : 
base(....)
      {
      }    
    } 
}

powered by IMHO 1.3