DarioSantarelli.Blog("UgiDotNet");

<sharing mode=”On” users=”*” />
posts - 144, comments - 118, trackbacks - 2

My Links

News

Dario Santarelli
This is my personal blog. These postings are provided "AS IS" with no warranties, and confer no rights.


My status



Dario Santarelli's Facebook profile
View Dario Santarelli's profile on LinkedIn

Add to Technorati Favorites

Tag Cloud

Archives

Post Categories

.NET

ASP.NET

My English Blog

Web 2.0

lunedì 27 aprile 2009

LINQ to CSV

Veramente utile!!! :D

http://www.codeproject.com/KB/linq/LINQtoCSV.aspx

Technorati Tag: ,

posted @ lunedì 27 aprile 2009 16.35 | Feedback (0) | Filed Under [ .NET ]

giovedì 2 aprile 2009

[WPF] ICommand e CanExecute

Per chi sviluppa utilizzando il pattern M-V-VM l’interfaccia ICommand costituisce la base per costruire un’infrastruttura che permetta alla View di invocare comandi applicativi direttamente nel ViewModel. L’interfaccia mette a disposizione tre semplici membri: due metodi Execute(...), CanExecute(...) ed un evento CanExecuteChanged. E’ facile immaginare come in progetti di una certa consistenza sia necessario costruirsi delle implementazioni spesso particolari dell’interfaccia ICommand, cosa che da una parte implica tanta buona volontà, dall’altra ripaga quando si dormono sonni tranquilli pensando ad una View completamente svincolata dalla logica applicativa.
In genere un’ utile implementazione di ICommand poggia sull’ intercettazione dell’evento CanExecuteChanged in modo da poter esporre alla View informazioni sul fatto che il nostro command sia o meno eseguibile ( si pensi ad esempio ad un Button enabled o meno ). A mio modo di vedere, una limitazione dell’interfaccia ICommand risiede nel metodo CanExecute(…) , il quale appunto è un metodo ( con annesso parametro opzionale ) e non una proprietà, cosa che non mette a disposizione il classico pattern Property/PropertyChanged sfruttabile nei data binding tra ViewModel e View. 
E’ semplice ovviare a questa situazione: ad esempio si può fare in modo che il nostro ICommand implementi una sorta di proprietà “wrapper” per esporre un sistema binding-friendly CanExecute/CanExecuteChanged.
Vediamo un esempio pratico. In una mia applicazione WPF avevo bisogno di realizzare un VM per una Window di Login e volevo “bindare” la proprietà CanLogin del VM ( che semplicemente ritorna true se UserName e Password sono entrambi valorizzati ) direttamente con il metodo CanExecute(…) del mio ICommand.

Ho quindi realizzato un’implementazione di ICommand (chiamata senza fantasia CanExecuteCommand) che accetta:

  1. Un Delegate da eseguire all’interno dell’ Execute(…)
  2. Un oggetto di tipo Binding che rappresenta il binding tra una proprietà “wrapper” per il metodo CanExecute(…) dell’ ICommand (nello specifico CanExecuteAction) e una proprietà del ViewModel (nello specifico CanLogin) in modo da poter scatenare l’evento CanExecuteChanged proprio al variare della proprietà CanLogin del ViewModel.

Difficile da spiegare, facile da implementare :D

Command:


public class CanExecuteCommand : DependencyObject, ICommand
{
  
Action<object> _executeDelegate;
  
public event EventHandler CanExecuteChanged;

  
public bool CanExecuteAction
  
{
    
get { return (bool)GetValue(CanExecuteActionProperty); }
    
set { SetValue(CanExecuteActionProperty, value); }
  
}

  
public static readonly DependencyProperty CanExecuteActionProperty =
          DependencyProperty.Register("CanExecuteAction", typeof(bool), typeof(CanExecuteCommand),
                                      new PropertyMetadata(OnCanExecuteActionChanged));

  
public CanExecuteCommand(Action<object> executeDelegate) : this(executeDelegate, null) { }

  
public CanExecuteCommand(Action<object> executeDelegate, Binding canExecuteActionBinding)
  
{
     
_executeDelegate = executeDelegate;
     
if (canExecuteActionBinding != null) BindingOperations.SetBinding(this, CanExecuteActionProperty, canExecuteActionBinding);           
  
}

  
#region ICommand Members

  
public void Execute(object parameter) { _executeDelegate(parameter); }

  
public bool CanExecute(object parameter) { return CanExecuteAction; }

  
#endregion

  
private void RaiseCanExecuteActionChanged()
  
{
    
if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs());
  
}

  
static void OnCanExecuteActionChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
  
{
   
(dependencyObject as CanExecuteCommand).RaiseCanExecuteActionChanged();
  
}                      
}



ViewModel:

 

public class LoginViewModel : ILoginViewModel, INotifyPropertyChanged
{
 
private string _username;
 
private string _password;

 
public event PropertyChangedEventHandler PropertyChanged;

 
public ICommand ReadyCommand { get; private set; } // In Binding con la View

 
#region ILoginViewModel Members

 
public string UserName // In Binding con la View
 
{
   
get { return _username; }
   
set
   
{
     
if (_username != value)
     
{
       
_username = value;
       
RaisePropertyChanged("UserName");
       
RaisePropertyChanged("CanLogin");
     
}
   
}
 
}

 
public string Password // In Binding con la View
 
{
   
get { return _password; }
   
set
   
{
     
if (_password != value)
     
{
       
_password = value;
       
RaisePropertyChanged("Password");
       
RaisePropertyChanged("CanLogin");                   
     
}
    
}
  
}

 
public bool CanLogin
 
{
   
get { return !string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password); }
 
}

 
public void Login()
 
{
   
//... Do Login...           
 
}

 
#endregion

 
public LoginViewModel()
 
{
   
Binding binding = new Binding("CanLogin");
   
binding.Source = this;
   
ReadyCommand = new CanExecuteCommand((arg) => Login(), binding);           
 
}

 
private void RaisePropertyChanged(string propertyName)
 
{
  
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
 
}      
}

 

Technorati Tag: ,

posted @ giovedì 2 aprile 2009 20.18 | Feedback (0) | Filed Under [ WPF ]

giovedì 19 marzo 2009

[Silverlight/WPF] Resource file e Access Modifier

Più volte nello sviluppo Silverlight/WPF mi sono imbattuto nella necessità di utilizzare i file di risorsa (.resx) e devo dire che le prime volte mi sono trovato un po’ in difficoltà dal momento che non riuscivo a capire per quale arcano i miei binding dichiarativi nel codice XAML verso le mie risorse non funzionassero affatto. Andiamo per ordine: supponendo di definire un file di risorsa in questo modo ...


 
... in fase di design Visual Studio notifica uno “strano” errore,

 

indipendentemente dal fatto che si utilizzi Public o Internal come access modifier (più precisamente se si selezionasse Internal l’errore diverrebbe AG_E_PARSER_BAD_TYPE). Mantenendo l’access modifier come “Public” ed andando a vedere il codice autogenerato da Visual Studio ci si imbatte in questa piccola “finezza”:



Classe public e costruttore internal!!!

Personalmente ho sempre ovviato al problema nel modo più semplice, impostando il costruttore come public per ogni file di risorsa. Oggi invece mi imbatto in questo post che propone invece una soluzione più elegante a cui non avevo proprio pensato, ovvero realizzare semplicemente una classe pubblica con una singola proprietà che espone la classe associata al file di risorsa. SEMPLICE MA GENIALE!!!

 

Technorati Tag: ,

posted @ giovedì 19 marzo 2009 23.02 | Feedback (0) | Filed Under [ WPF Silverlight ]

lunedì 16 marzo 2009

[Silverlight 2] Visibility e VisualStateManager

Una caratteristica di un controllo visuale che spesso e volentieri risulta gradevole è la possibilità di mostrare/nascondere il contenuto del controllo stesso tramite un effetto FadeIn/FadeOut. In genere, questo effetto è realizzato tramite una semplice animazione che lavora sull’Opacity del controllo (portandola da 1 a 0 e viceversa) e che magari imposta a Visible/Collapsed la proprietà Visibility subito dopo l’animazione in modo da riempire/liberare istantaneamente un’area specifica dello schermo.  In molti casi risulta dunque utile legare l’ effetto FadeIn/FadeOut proprio alla proprietà Visibility del controllo, in modo da ottenere automaticamente le animazioni desiderate al momento della valorizzazione della proprietà (che altrimenti non effettuerebbe ovviamente alcun effetto visuale di transizione). Un modo che ritengo molto interessante per ottenere questo tipo di scenario sfrutta il VisualStateManager : nel seguente esempio vediamo come sia possibile effettuare l’override della proprietà Visibility di un UserControl per agganciare gli effetti di FadeIn/FadeOut agli stati Visible/Collapsed definiti tramite VisualStateManager.

MyControl.xaml

<UserControl xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows" ...>
    <Grid x:Name="LayoutRoot">
     
    
<vsm:VisualStateManager.VisualStateGroups>
        
<vsm:VisualStateGroup x:Name="CommonStates">
           <vsm:VisualState x:Name="Visible">
            
<Storyboard>
              
<DoubleAnimation Duration="0:0:.5" Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="Opacity"
                               
From="0" To="1" AutoReverse="False" Completed="VisibleCompleted" />
            
</Storyboard>
          
</vsm:VisualState>
          
<vsm:VisualState x:Name="Collapsed">
            
<Storyboard>
              
<DoubleAnimation Duration="0:0:.5" Storyboard.TargetName="LayoutRoot" Storyboard.TargetProperty="Opacity"
                               
From="1" To="0" AutoReverse="False" Completed="CollapsedCompleted" />
            
</Storyboard>
          
</vsm:VisualState>
        
</vsm:VisualStateGroup>
     
</vsm:VisualStateManager.VisualStateGroups>

      ... 

    </Grid>
</UserControl>


MyControl
.xaml.cs

public partial class MyControl : UserControl
{
 
private Visibility _visibility;       

 
public event EventHandler VisibilityChanged;        
 

 
public MyControl() { InitializeComponent(); }

 
public new Visibility Visibility
 
{
    
get { return _visibility; }
    
set
    
{
      
if (_visibility != value)
      
{
        
_visibility = value;
        
OnVisibilityChanged();
      
}
     
}
 
}

 
protected virtual void OnVisibilityChanged()
 
{
    
if (this.Visibility == Visibility.Visible)
    
{
      
base.Visibility = this.Visibility;
      
VisualStateManager.GoToState(this, "Visible", true);
    
}
    
else VisualStateManager.GoToState(this, "Collapsed", true);            
 
}

 
private void CollapsedCompleted(object sender, EventArgs e)
 
{
    
base.Visibility = this.Visibility;
    
FireVisibilityChanged();
 
}       

 
private void VisibleCompleted(object sender, EventArgs e) { FireVisibilityChanged(); }

 
private void FireVisibilityChanged()
 
{
    
if (VisibilityChanged != null) VisibilityChanged(this, new EventArgs());  
 
}
}

Come si può notare, nell’esempio si è voluto implementare anche un evento VisibilityChanged che viene scatenato ogni volta che l’animazione FadeIn/FadeOut ha terminato la propria esecuzione. Infatti, la transizione visuale da uno stato Visible ad uno stato Collapsed può essere relativamente lunga e di conseguenza potrebbe essere utile sapere esattamente quando il nostro controllo è completamente visibile/nascosto.

 

Technorati Tag:

posted @ lunedì 16 marzo 2009 22.04 | Feedback (0) | Filed Under [ Silverlight ]

lunedì 9 marzo 2009

jQuery e ASP.NET Ajax: $(document).ready() VS asynchronous postback

Molto spesso capita di dover integrare, anche solo per semplici miglioramenti grafici, dei plugin jQuery all’interno delle nostre pagine ASP.NET. Gran parte delle volte l’impatto è indolore mentre in alcuni casi specifici occorre far riferimento ad accorgimenti non banali, soprattutto quando c’è di mezzo ASP.NET Ajax.
Il classico esempio si verifica quando ci si aspetta che la funzione $(document).ready() venga chiamata correttamente anche dopo un asynchronous postback tramite UpdatePanel. Infatti, la funzione $(document).ready() permette di determinare il momento in cui il DOM della pagina è caricato dal browser. Tuttavia, in caso di asynchronous postback il DOM può essere eventualmente modificato e non ricaricato nuovamente. Quindi il nostro codice javascript nella forma:

$(document).ready(function()
{
 
// codice...
});


... non verrà eseguito!
Un workaround molto semplice per questo tipo di situazione è quello di sfruttare l’evento client-side endRequest generato dopo un postback asincrono, quando il controllo viene  restituito al browser. Agganciando il nostro codice jQuery che usa $(document).ready() all’interno di un opportuno event Handler per tale evento possiamo risolvere il problema. Ecco un semplice esempio:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
 
<title>jQuery .ready() function and asynchronous postback</title>       
</head>
<body>
 
<form id="mainForm" runat="server">
 
<div>
   
<asp:ScriptManager ID="scriptManager" runat="server">
     
<Scripts>
      
<asp:ScriptReference Path="http://code.jquery.com/jquery-latest.js" />
     
</Scripts>
   
</asp:ScriptManager>
   
<asp:UpdatePanel ID="updatePanel" runat="server">
     
<ContentTemplate>                
        
<asp:Button ID="btnTest" runat="server" Text="Do Async PostBack" />
     
</ContentTemplate>
   
</asp:UpdatePanel>
  
</div>
 
</form>

   
 
<script type="text/javascript">
   
   
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);
   
   
function EndRequestHandler(sender, args)
   
{
     
if (args.get_error() == undefined) Test();           
   
}

   
function Test()
   
{
     
$(document).ready(function()
     
{
       
alert('$(document).ready() called!!!');
     
});
   
}

   
Test();                    
           
 
</script>
 
</body>
</html>


Technorati Tag: ,,,

posted @ lunedì 9 marzo 2009 21.58 | Feedback (1) | Filed Under [ ASP.NET AJAX ]

martedì 3 marzo 2009

[Entity Framework] Disabilitare il change tracking dell’ObjectStateManager

(da MSDN) ObjectStateManager tracks query results, and provides logic to merge multiple overlapping query results. It also performs in-memory change tracking when a user inserts, deletes, or modifies objects, and provides the change set for updates. This change set is used by the change processor to persist modifications.


La classe ObjectStateManager in generale gestisce lo stato dei nostri EntityObject. In quei casi in cui i nostri dati devono essere semplicemente presentati in modalità read only può essere utile disabilitare il change tracking dell’ ObjectContext, ottenendo un incremento di performance talvolta significativo. 
In particolare, per disabilitare il change tracking occorre utilizzare l’opzione MergeOption.NoTracking dell’enumeration MergeOption, che permette di lavorare con Entità aventi EntityStateDetached’. Fondamentalmente esistono due modi per disabilitare il change tracking:

  1. Utilizzando l’oggetto ObjectQuery. Esiste infatti un overload del costruttore che permette di specificare l’opzione MergeOption desiderata.
  2. A livello di EntitySet

Vediamo entrambi gli esempi:

Caso 1

using (NorthwindObjectContext context = new NorthwindObjectContext())
{
 
ObjectQuery<Customer> queryCustomers = new ObjectQuery<Customer>("SELECT VALUE c FROM NorthwindObjectContext.Customers AS c", context, MergeOption.NoTracking);
 
foreach (Customer customer in queryCustomers)   
 
{
   
Console.WriteLine("{0} - {1}", customer.ContactName, customer.EntityState.ToString());    
 
}
}


Caso 2

using (NorthwindObjectContext context = new NorthwindObjectContext())
{   
 
context.Customers.MergeOption = MergeOption.NoTracking;    
 
foreach (Customer customer in context.Customers)   
 
{
   
Console.WriteLine("{0} - {1}", customer.ContactName, customer.EntityState.ToString());    
 
}
}


Technorati Tag:

posted @ martedì 3 marzo 2009 21.31 | Feedback (0) |

venerdì 27 febbraio 2009

Da WindowsForms a WPF via MVP

Qualche giorno fa mi sono imbattutto in una simpatica applicazione Windows Forms che permette di disegnare su una lavangna virtuale il posizionamento in campo di due squadre di calcio. Un'applicazione per allenatori insomma :D. In breve, il funzionamento è semplicemente basato sull'aggiunta e sullo spostamento di pedine (rosse o blu) su un campo di calcio virtuale con eventuale possibilità di ingrandirne/rimpicciolirne le dimensioni.
Guardando il sorgente ho osservato come l’intera applicazione fosse stata concepita e sviluppata "alla Windows Forms" con l'intera logica piazzata all'interno di ciascun event handler dei vari elementi dell'UI.
Ho quindi pensato di esercitarmi nel fare un po' di refactoring, ma soprattutto di analizzare alcuni aspetti prettamente grafici nel porting di una applicazione WindowsForms ad una versione WPF.
Dal punto di vista architetturale, prima di passare alla versione MVVM ho anzitutto ritenuto opportuno inserire un “passaggio intermedio” nel refactoring, soffermandomi su una versione MVP. Il tutto per mettere sostanzialmente in risalto al mio cervello i principali punti di differenza tra i due pattern. Inoltre, come già accennato, la fase di analisi ha toccato alcune potenzialità grafiche di WPF, come il Drag & Drop/Move e lo Zoom.

In questo post vorrei condividere con voi come mi sono mosso, sperando di ricevere magari qualche utile consiglio ;D

Anzitutto sono partito definendo un mini-core, ovvero un'interfaccia "marker" IView per le View

public interface IView { }


ed una classe generica astratta per il presenter:

public abstract class PresenterBase<T> where T : IView
{
 
public T View { get; set; }
}


A questo punto ho iniziato il porting della Form principale dell'applicazione di partenza (che si chiamava SoccerBlackBoardForm) realizzando la corrispondente Window WPF (SoccerBlackBoardWindow) che implementa un'interfaccia IBlackboardView dedotta analizzando il codice dell'applicazione originale. Infine ho realizzato una classe presenter BlackboardPresenter per le View che implementano IBlackboardView. Il tutto è rappresentato sinteticamente nel seguente class Diagram:

Omettendo per brevità il codice dell'interfaccia IBlackboardView, riporto direttamente lo scheletro del mio refactoring dal momento che si tratta di codice abbastanza autoesplicativo:

1. La costruzione della Window SoccerBlackBoardWindow ha letteralmente svuotato di logica la corrispondente versione Windows Forms fino ad avere la seguente forma:

SoccerBlackBoardWindow.xaml

<Window ...>  

    <Grid>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="100"></ColumnDefinition>

            <ColumnDefinition Width="*"></ColumnDefinition>

        </Grid.ColumnDefinitions>

        <StackPanel Grid.Column="0" Orientation="Vertical">                    

            <ListBox x:Name="lstImages">

                <ListBox.ItemTemplate>

                    <DataTemplate>

                        <Image ... Source="{Binding}"></Image>

                    </DataTemplate>

                </ListBox.ItemTemplate>

            </ListBox>           

            <Button Content="Add Image >>" Click="AddImageClick" ...></Button>

            <Button Content="Clear" Click="ClearClick" ...></Button>

        </StackPanel>

        <Canvas x:Name="drawingArea" Grid.Column="1" ...>

            <Canvas.Background>

                <ImageBrush Stretch="Fill" 
                            ImageSource
="Resources/SoccerField.png"></ImageBrush>

            </Canvas.Background>

        </Canvas>

    </Grid>

</Window>


SoccerBlackBoardWindow.xaml.cs

public partial class SoccerBlackBoardWindow : Window, IBlackboardView
{
 
private BlackboardPresenter _presenter;

 
#region IBlackboardView Members

 
public Panel DrawingArea
 
{
   
get { return drawingArea; }
 
}

 
public Point MousePosition
 
{
   
get { return Mouse.GetPosition(drawingArea); }          
 
}

 
public IList<BitmapSource> AvailableImages
 
{
   
get { return (IList<BitmapSource>)lstImages.ItemsSource; }
   
set { lstImages.ItemsSource = value; }
 
}

 
public BitmapSource SelectedImage
 
{
   
get { return (BitmapSource)lstImages.SelectedItem; }
   
set { lstImages.SelectedItem = value; }
 
}

 
public void AttachPresenter(BlackboardPresenter presenter) { _presenter = presenter; }

 

  #endregion

 

  public SoccerBlackBoardWindow()

  {

    InitializeComponent();

    _presenter = new BlackboardPresenter(this);

    _presenter.LoadImages();
 
}  

    

  protected void AddImageClick(object sender, RoutedEventArgs e) { _presenter.DrawImage(SelectedImage, new Size(100, 100), 0, 0, 0); }

 
protected void ClearClick(object sender, RoutedEventArgs e) { _presenter.ClearDrawingArea(); }     

}


2. Il presenter BlackboardPresenter è stato quindi realizzato per aggiornare la View sia in base agli eventi utente e l'interazione con il model, che in questo caso è dato da una banale collezione readonly di immagini che l'utente ha a disposizione per l'inserimento nella area di disegno (DrawingArea)
Notare come sia semplice realizzare un meccanismo di Drag & Move :D.

BlackboardPresenter.cs

public class BlackboardPresenter : PresenterBase<IBlackboardView>
{
 
private Point _tmpPosition;
 
private const int ZOOMDELTA = 10;

 
public BlackboardPresenter(IBlackboardView view) { View = view; }

 
public void LoadImages()
 
{           
   
IList<BitmapSource> availableImages = new List<BitmapSource>();           
   
availableImages.Add(new BitmapImage(new Uri("Resources/SoccerBall.png", UriKind.Relative)));
    availableImages.Add(new BitmapImage(new Uri("Resources/blueBall.png", UriKind.Relative))); 
   
availableImages.Add(new BitmapImage(new Uri("Resources/redBall.png", UriKind.Relative)));
   
View.AvailableImages = availableImages;
 
}

 
public void ClearDrawingArea(){ View.DrawingArea.Children.Clear(); }

 
public Image DrawImage(ImageSource source, Size size, int canvasLeft, int canvasTop,int zIndex)       

  {

    Image image = new Image();

           

    image.Source = source;           

    image.Height = size.Height;

    image.Width = size.Width;

 

    image.Cursor = Cursors.Hand;

 

    Canvas.SetLeft(image, canvasLeft);

    Canvas.SetTop(image, canvasTop);

    Panel.SetZIndex(image, zIndex);

 

    image.MouseMove += delegate(object o, MouseEventArgs args) { MoveImage(image); };

    image.MouseWheel += delegate(object o, MouseWheelEventArgs args) { ZoomImage(image, args.Delta); };

    image.MouseDown += delegate(object o, MouseButtonEventArgs args) { CaptureImage(image); };

    image.MouseUp += delegate(object o, MouseButtonEventArgs args) { ReleaseImage(image); }; 

          

    View.DrawingArea.Children.Add(image);

 

    return image;

  }

 

   public void ReleaseImage(Image image) { image.ReleaseMouseCapture(); }

 

   public void CaptureImage(Image image)

   {

     image.CaptureMouse();

     _tmpPosition= View.MousePosition;

   }

 

   public void MoveImage(Image image)

   {           

     if (Mouse.LeftButton == MouseButtonState.Pressed)

     {

       Canvas.SetLeft(image, Canvas.GetLeft(image) + (View.MousePosition.X - _tmpPosition.X));

       Canvas.SetTop(image, Canvas.GetTop(image) + (View.MousePosition.Y - _tmpPosition.Y));

     }

     _tmpPosition= View.MousePosition;

   }

 

   public void ZoomImage(Image image, int amount)

   {

     if (amount > 0)

     {

       image.Height += ZOOMDELTA;

       image.Width += ZOOMDELTA;

     }

     else

     {

       if ((image.Height - ZOOMDELTA > ZOOMDELTA) && (image.Width - ZOOMDELTA > ZOOMDELTA))

       {

         image.Height -= ZOOMDELTA;

         image.Width -= ZOOMDELTA;

       }

     }

   }      

}


Il risultato ottenuto è raffigurato qua sotto: non vi dico quanto è stato divertente usare questa app per spiegare alla mia compagna la tattica del fuorigioco :D.

  

Technorati Tag: ,

posted @ venerdì 27 febbraio 2009 19.43 | Feedback (1) | Filed Under [ WPF ]

martedì 24 febbraio 2009

[WCF] IOperationBehavior e IParameterInspector

Dal punto di vista della flessibilità, ritengo che WCF sia una delle tecnologie più potenti attualmente disponibili per lo sviluppo di applicazioni orientate ai servizi. In particolare, una delle caratteristiche che apprezzo maggiormente è la facilità di personalizzazione del comportamento runtime degli OperationContract sia client-side che service-side ( pratica per altro molto usata nel WCF REST Starter Kit :D ).
In questo post volevo soffermarmi proprio su questo aspetto, mostrando un semplice esempio di implementazione dell’interfaccia IOperationBehavior, che per l'appunto definisce quattro metodi finalizzati all'estensione del runtime behavior di un'operation:

  • AddBindingParameters : serve per aggiungere nuovi elementi di binding utilizzabili dall'operation in fase di esecuzione. 
  • ApplyClientBehavior : viene chiamato client-side 
    • prima che il contenuto della richiesta sia serializzato e inviato al service
    • dopo che la risposta è stata deserializzata ma prima che i valori restituiti vengano forniti al metodo proxy
  • ApplyDispatchBehavior : viene chiamato service-side dopo la deserializzazione dei parametri, ma prima che essi siano forniti all'operation del service.
  • Validate : permette di verificare che l'operation soddisfi dei criteri custom ( es. autorizzazioni ).

Vediamo ora una semplice implementazione che mette in evidenza come sia veramente semplice agganciare un OperationBehavior ad un OperationContract  tramite un attributo. Gli ambiti di applicazione sono veramente tanti: ad esempio potremmo usare questa tecnica service-side per eseguire della logica custom prima e dopo l’invocazione di un’operation da parte di un client. Nello specifico, supponiamo di voler scrivere in un file di log sul server sia i parametri di input passati all'operation che il risultato dell'invocazione. Per ottenere questo scenario necessitiamo anzitutto della definizione di un oggetto che sia in grado di ispezionare i parametri dell'operation sia prima che dopo la chiamata: in altre parole abbiamo bisogno di un IParameterInspector. Questa interfaccia definisce due metodi, BeforeCall(…) e AfterCall(…), che ci permettono di inserire della logica custom ( sempre sia client-side che service-side ) per accedere alle informazioni inerenti una operation ed i relativi parametri di input/output nelle fasi immediatamente precedenti e successive all’invocazione.
Partiamo dunque dal nostro IParameterInspector:

public class ParameterLogInspector : IParameterInspector
{
  
private FileInfo _logFileInfo;

  
public ParameterLogInspector(string fileName)
  
{
    
_logFileInfo = new FileInfo(fileName);
    
if (!_logFileInfo.Exists) _logFileInfo.Create();      
  
}

  
#region IParameterInspector Members
  

  
public object BeforeCall(string operationName, object[] inputs)
  
{
    
StringBuilder sb = new StringBuilder();
    
foreach (object input in inputs) sb.AppendFormat("{0};", input.ToString());
    
this.Log(string.Format("Operation \"{0}\" is calling... inputs: {1}", operationName, sb.ToString()));
    
return null;
  
}       

   public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
  
{
    
this.Log(string.Format("Operation \"{0}\" called. returnValue: {1}",operationName,returnValue.ToString()));            
  
}
  
  
#endregion

  
private void Log(string message)
  
{
    
using (StreamWriter sw = _logFileInfo.AppendText())
    
{
      
sw.WriteLine(string.Format("{0}> {1}", DateTime.Now.ToString(), message));
    
}
  
}
}


Quindi, ci posizioniamo all'interno del metodo ApplyDispatchBehavior(...) semplicemente per aggiungere il nostro IParameterInspector alla collection ParameterInspectors dell'oggetto DispatchOperation (che rappresenta l'operation service-side) :


public class ParametersLog : Attribute, IOperationBehavior
{
 
public string FileName { get; set; }

 
#region IOperationBehavior Members

  ...

 
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
 
{
  
dispatchOperation.ParameterInspectors.Add(new ParameterLogInspector(this.FileName));
 
}

  ...
 

 
#endregion
}


Il gioco è fatto: non rimane che decorare l'OperationContract di interesse con il nostro attributo ParametersLog per ottenere l' estensione del runtime che ad ogni chiamata scriverà in un file di log i valori dei parametri di input/output .

[ServiceContract]
public interface IAuthentication
{
 
[OperationContract]
 
[ParametersLog(FileName="C:/log.txt")]
 
UserInfo Login(string userName, string password);

 
[OperationContract]
 
void Logout(string userName);
}


Technorati Tag:

posted @ martedì 24 febbraio 2009 15.02 | Feedback (1) | Filed Under [ WCF ]

sabato 21 febbraio 2009

Pong con il designer WindowsForms

Clamoroso :D

http://blogs.msdn.com/mitsu/archive/2009/02/13/coding4fun-understanding-winforms-designmode.aspx

posted @ sabato 21 febbraio 2009 20.27 | Feedback (1) | Filed Under [ About Me... 100% delirio!!! ]

EntityObject e DTO: copiare proprietà comuni via extension method

Quando si sviluppano architetture N-tier che prevedono uno o più service layer è sempre consigliabile l’utilizzo di DTO per scambiare dati tra livelli, indipendentemente dal (fuorviante) fatto che ogni DataContract esposto da un servizio (es. WCF) possa essere in mapping 1:1 con i business objects (es. EntityObject). Un DTO infatti è solitamente un POCO le cui proprietà in molti casi si possono mappare sia nel nome che nel tipo in un sottoinsieme di quelle di uno o più oggetti di business.
Ad ogni modo, al fine di aumentare la produttività nel valorizzare un DTO che possiede proprietà in comune nel nome e nel tipo con uno o più EntityObject, può avere senso implementare un extension method che via reflection copi il valore delle proprietà di un oggetto sorgente nelle proprietà di un oggetto destinazione appunto qualora tali proprietà abbiano stesso nome e tipo.

public static void CopyProperties<T1, T2>(this T1 source, T2 destination)
{           
   if (source == null) throw new ArgumentNullException("Source object can’t be null.");
  
if (destination == null) throw new ArgumentNullException("Destination object can’t be null.");

  
PropertyInfo[] destinationPropertyInfos = destination.GetType().GetProperties();
  
foreach (PropertyInfo propertyInfo in destinationPropertyInfos)
  
{
    
if (propertyInfo.CanWrite)
    
{
      
source.GetType().GetProperties()
          
.Where(p => p.CanRead && (p.Name == propertyInfo.Name && p.PropertyType == propertyInfo.PropertyType))
          
.ToList()
          
.ForEach(p => propertyInfo.SetValue(destination, p.GetValue(source, null), null));
    
}
  
}
}


Quindi, supponendo di avere il seguente object model di partenza...

 

...è possibile scrivere qualcosa del genere, al fine di evitare la noiosa scrittura delle solite righe di inizializzazione della classe DTO partendo dall’ Entity Objecy:

Customer customerEntity = null;
using (NorthwindEntities entities = new NorthwindEntities())
{
 
customerEntity = entities.Customers.First(c => c.CustomerID == "ALFKI");                
}

CustomerDTO customerDTO = new CustomerDTO();
customerEntity.CopyProperties(customerDTO);    


Technorati Tag: ,

posted @ sabato 21 febbraio 2009 1.33 | Feedback (0) |

lunedì 16 febbraio 2009

[Entity Framework] EntityClient

EntityClient (namespace System.Data.EntityClient) è un provider che l’EntityFramework mette a disposizione per accedere ai dati descritti da unEntity Data Model (EDM), ovvero una sorta di gateway per query sul modello concettuale e non sulla relativa rappresentazione fisica. 
Le query invocabili dall’ EntityClient vanno dunque scritte in un dialetto di SQL chiamato “EntitySQL”: a runtime, la pipeline compila il testo EntitySQL in un command tree che viene passato al data provider sottostante per la generazione del comando SQL nativo. Dietro le quinte, infatti, EntityClient utilizza i provider ADO.NET per accedere alle sorgenti dati ( es. SqlClient per SQL Server ).  L’aspetto interessante è che i risultati di una query EntitySQL possono non limitarsi a semplici tabelle, bensì possono contenere anche gerarchie complesse.
In generale, l’uso di EntityClient è consigliabile in applicazioni model-based che richiedono particolari performance nella misura in cui non si necessita della materializzazione delle Entities a partire da resultset, bensì di un IDataReader consumabile direttamente magari per materializzazioni di oggetti specifici.
In qualità di provider ADO.NET a tutti gli effetti, EntityClient segue i soliti pattern Connection-Command-Parameter-DataReader, etc. dove le suddette classi sono precedute dal prefisso “Entity”. Ecco un esempio di paginazione dati usando EntityClient:

public List<string> GetProductNames(int pageNumber, int pageSize)
{
 
List<string> productNames = new List<string>();
 
using (EntityConnection conn = new EntityConnection("name=NorthwindEntities"))
 
{
   
string cmdText = string.Format("SELECT VALUE v FROM NorthwindEntities.Products AS v ORDER BY v.ProductName SKIP {0} LIMIT {1}", (pageNumber * pageSize).ToString(), pageSize);                  

   
using (EntityCommand cmd = new EntityCommand(cmdText, conn))
   
{
     
conn.Open();    
     
using(EntityDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))               
     
{
       
while (reader.Read()) productNames.Add(reader["ProductName"].ToString());
     
}                                                
   
}
  
}
  
return productNames;
}

 

Technorati Tag:

posted @ lunedì 16 febbraio 2009 0.03 | Feedback (1) |

venerdì 13 febbraio 2009

Caricamento dinamico di JavaScript con jQuery AJAX

Una delle tecniche che permettono di aumentare le performance di un' applicazione web si basa sul caricamento "on-demand" di javascript via Ajax. In questo modo si possono ridurre le latenze di precaricamento di troppe librerie javascript alla prima GET di una pagina. Infatti, in alcuni scenari specifici risulta più consigliabile scaricare "pezzi" di script solo quando se ne ha bisogno. Fino a non molto tempo fa ho avuto modo di testare alcune librerie free come Include e AJILE che permettono di raggiungere questo obiettivo senza troppi problemi. Ma da quando sono passato a jQuery le cose sono cambiate :D.
Vorrei semplicemente mostrare un paragone tra l'utilizzo classico di XMLHTTP e l'utilizzo di Ajax/jQuery.getScript.
Supponiamo di voler caricare dinamicamente ed eseguire una function javascript "HelloWorld()"che si trova in un file HelloWorld.js

function HelloWorld() { alert( 'Hello World!' ); }


al click di un button nella nostra pagina.

1. Esempio con XMLHTTP

<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns ="http://www.w3.org/1999/xhtml">

<head>    

    <script type ="text/javascript">

    function CallExternalScriptMethod(scriptUrl, targetCall)

    {

        var AJAX = null;

        if (window.XMLHttpRequest) AJAX = new XMLHttpRequest();

        else AJAX = new ActiveXObject("Microsoft.XMLHTTP");

 

        AJAX.onreadystatechange = function() {

            if (AJAX.readyState == 4 && AJAX.status == 200) {

                eval(AJAX.responseText);

                eval(targetCall);

            }

        }

                  

        AJAX.open("GET", scriptUrl, true);

        AJAX.send(null);

    }                                 

</script>

<title></title>

</head >

<body>

    <div>

       <input type="button" id="btnGO" value="GO!!!"
              onclick="CallExternalScriptMethod('HelloWorld.js','HelloWorld()')" />

    </div>    

</body>

</html>


2. Esempio con Ajax/jQuery.getScript

<! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns ="http://www.w3.org/1999/xhtml">

<head>

    <script type ="text/javascript" src ="http://code.jquery.com/jquery-latest.js"></script>

    <script type ="text/javascript">

        function CallExternalScriptMethod(scriptUrl, targetCall) 
        { 
            

            $.getScript(scriptUrl, function() {

                    eval(targetCall);
           
});  

        }

    </script>

    <title></title>

</ head >

<body>

    <div>

        <input type="button" id="btnGO" value="GO!!!"
               onclick="CallExternalScriptMethod('HelloWorld.js','HelloWorld()')" />

    </div>

</body>

</html>


Come si può notare la facilità e l'eleganza di realizzazione con jQuery è disarmante. Inoltre, a partire dalla v1.2 abbiamo gratuitamente un nuovo enanchement riguardante le possibili problematiche relative a richieste cross-domain che limiterebbero l'utilizzo dell'esempio con XMLHTTP sopra riportato:

Before jQuery 1.2, getScript was only able to load scripts from the same domain as the original page. As of 1.2, you can now load JavaScript files from any domain.


Technorati Tag: ,

posted @ venerdì 13 febbraio 2009 16.30 | Feedback (0) |

martedì 10 febbraio 2009

XPUG Marche.... Chi è interessato?

Segnalo questa importante iniziativa che sta partendo su agilemovement.it : XPUG Marche.
Lo scopo è la creazione di una community attenta nel promuovere le metodologie "agili" sul territorio marchigiano. 
C
hi è interessato a dare il proprio contributo può rispondere a questo appello:

http://www.agilemovement.it/forum/topics/xpug-marche-chi-e-interessato

Per ora ci stiamo contando in modo da organizzare un incontro prima possibile , per decidere sul da farsi. 

posted @ martedì 10 febbraio 2009 16.57 | Feedback (0) |

lunedì 9 febbraio 2009

[WCF] Operazioni asincrone client-side

WCF è un framework concepito per sviluppare scenari di comunicazione completamente disaccoppiati sotto ogni punto di vista. In particolare, per quanto riguarda la sincronizzazione dei messaggi, è prevista internamente una rigida implementazione di pattern asincroni per ottenere chiamate non bloccanti tra i vari livelli strutturali. Esternamente, invece, WCF espone possibilità di programmazione asincrona sia client-side che server-side. L'aspetto interessante da approfondire in merito è come il programming model asincrono client-side non abbia nulla a che vedere con quello server-side: ovvero, un client può sostanzialmente invocare in modo asincrono un servizio indipendentemente dal fatto che esso sia implementato con un pattern sincrono piuttosto che asincrono.
Il disaccoppiamento avviene infatti a livello di trasporto quando i dati vengono serializzati/deserializzati.
Per capire l’importanza di tale feature pensiamo semplicemente ad un’ architettura N-tier che poggia su WCF: potremmo avere diverse “operation” che a loro volta utilizzano proxy client verso altre operation WCF, rendendo vitale l’utilizzo di pattern di comunicazione asincroni. 
Vediamo un semplice esempio. Supponiamo di avere il seguente ServiceContract:

[ServiceContract]
public interface IAuthentication
{
 
[OperationContract]
 
UserInfo Login(string username, string password);       

 
[OperationContract]
 
void Logout(string username);
}
   

Se lato client utilizzassimo Svcutil ( con gli opportuni parametri ) o il solito “Add Service Reference” di VisualStudio, otterremmo la generazione automatica del seguente object model:

 

Come si può osservare abbiamo una versione asincrona client-side del ServiceContract (IAuthentication), un' interfaccia <ServiceContract>Channel (IAuthenticationChannel) ed una classe Client (AuthenticationClient) che espone metodi di invocazione sia asincroni che sincroni. Nello specifico, si prospettano due possibili modalità di invocazione asincrona client-side di un’ operation:

1. Invocazione asincrona “event-based” (o “event-driven”) 
E' la modalità più semplice e raccomandata poiché richede solamente l’aggiunta di un EventHandler per ricevere una notifica all’occorrenza di una risposta. Questa modalità ( disponibile solo nel framework 3.5 e comunque solo per questo tipo di invocazione ) permette di sfruttare l’approccio asincrono event-based tramite un metodo nella forma <operationName>Async e l’intercettazione di un evento <operationName>Completed al cui interno si ha accesso al risultato dell’invocazione <operationName>CompletedEventArgs.

...

ServiceReference.AuthenticationClient client = new ServiceReference.AuthenticationClient();
client.LoginCompleted += new EventHandler<ServiceReference.LoginCompletedEventArgs>(client_LoginCompleted);
client.LoginAsync("dario.santarelli", "password");

Console.ReadLine();

client.Close();

...

protected void client_LoginCompleted(object sender, ServiceReference.LoginCompletedEventArgs e)
{
 
UserInfo info = e.Result as UserInfo;
 
Console.WriteLine(string.Format("UserName: {0} - Password: {1}",info.UserName,info.Email));
}

  

2. Invocazione asincrona via ChannelFactory
Il pattern asincrono previsto in questa modalità è quello classico previsto a partire dal framework 1.1: tramite lo split di un operation in due metodi ( Begin<operationName> e End<operationName> ), si sfruttano oggetti che implementano l’interfaccia System.IAsyncResult per rappresentare lo stato di una operazione asincrona.

...

EndpointAddress endpointAddress = new EndpointAddress("http://.../AuthenticationService.svc");
ServiceReference.IAuthenticationChannel channelClient = ChannelFactory<ServiceReference.IAuthenticationChannel>.CreateChannel(new BasicHttpBinding(), endpointAddress);
IAsyncResult result = channelClient.BeginLogin("dario.santarelli", "password", LoginCallBack, channelClient);

Console.ReadLine();

channelClient.Close();
channelClient.Dispose();

...  

protected void LoginCallBack(IAsyncResult ar)
{
 
UserInfo result = ((ServiceReference.IAuthenticationChannel)ar.AsyncState).EndLogin(ar);
 
Console.WriteLine("Result: {0}", result.UserName);
}


Conclusioni
In entrambe le soluzioni presentate, una nostra application può invocare un’operazione in maniera asincrona anche se il servizio è implementato in maniera sincrona, allo stesso modo con cui una applicazione può usare lo stesso pattern per invocare in maniera asincrona un metodo sincrono locale. “Come” è poi implementato l’ OperationContract è assolutamente insignificante per il client.


Risorse MSDN:

 

Technorati Tag: ,

posted @ lunedì 9 febbraio 2009 23.24 | Feedback (0) | Filed Under [ WCF ]

martedì 3 febbraio 2009

Visual Studio Testing Extensions

Segnalo il progetto “Visual Studio Testing Extensions” su CodePlex. Si tratta di una library concepita principalmente per migliorare la creazione di UnitTest nell’ambiente di Testing di Visual Studio. In breve, la library introduce due feature:

  • La possibilità di migliorare sensibilmente la leggibilità di uno unit test grazie all’introduzione di opportuni extension methods ( es. myInt.ShouldEqual(5);   invece di  Assert.AreEqual(myInt, 5); )
  • La possibilità di asserire exception multiple nei risultati attesi di un singolo TestMethod. Il tutto sfruttando un delegate Action(). Ad esempio:
    • Testing.ShouldThrowException<ArgumentNullException>(() => { … something throws ArgumentNullException … });


Technorati Tag:

posted @ martedì 3 febbraio 2009 20.22 | Feedback (0) |

sabato 31 gennaio 2009

[WCF] Esempio di Integration Test

Esistono molti strumenti e tecniche per effettuare un integration test di servizi WCF. Un modo che ho trovato molto semplice ed utile si ottiene sruttando il framework di unit testing di Visual Studio: infatti, utilizzando due metodi decorati rispettivamente con ClassInitialize e ClassCleanup possiamo gestire il ciclo di vita di un ServiceHost, evitando così il deploy preventivo (es. Cassini, IIS etc.) del servizio.
Nel seguente esempio viene mostrata questa tecnica supponendo di voler testare un servizio di autenticazione AuthenticationService che implementa un service contract IAuthentication, il quale definisce due metodi: Login(…) e Logout().


[TestClass]
public class AuthenticationUnitTest
{
 
private static ServiceHost _serviceHost;
 
private static string _address = "AuthenticationService";
 
private static string _baseAddress = "http://127.0.0.1:5656/";

  [ClassInitialize]
 
public static void ServiceHostInitialize(TestContext testContext)
 
{
   
_serviceHost = new ServiceHost(typeof(AuthenticationService), new[] { new Uri(_baseAddress) });
   
_serviceHost.AddServiceEndpoint(typeof(IAuthentication), new WSHttpBinding(), _address);
   
_serviceHost.Open();
 
}

 
[ClassCleanup]
 
public static void ServiceHostCleanup() { _serviceHost.Close(); }

 
[TestMethod]
 
public void AuthenticateUser()
 
{
   
string userName = "dario.santarelli";
   
string password = "password";

   
EndpointAddress endpointAddress = new EndpointAddress(string.Format("{0}{1}", _baseAddress,_address));
   
IAuthentication proxy = ChannelFactory<IAuthentication>.CreateChannel(new WSHttpBinding(), endpointAddress);

   
UserInfo userInfo = proxy.Login(userName, password);

   
Assert.IsNotNull(userInfo);
   
Assert.AreEqual(userInfo.Username, userName);

   
proxy.Logout(userName);           
  
}
}

 

Update: con questa tecnica non si intende sostituire il test in ambiente di produzione, bensì fornire un modo “base” per verificare il funzionamento di una WCF Service Library. Infatti, come si può notare, in questo caso sia lo UnitTest che il ServiceHost girano nello stesso AppDomain, il che NON garantisce by design il corretto comportamento del test come se fosse in produzione. Per avere test maggiormente efficaci, si consiglia l’utilizzo di soluzioni più “evolute” come WcfSvcHost o, meglio, direttamente un ambiente di test dedicato su IIS :D.


Technorati Tag: ,

posted @ sabato 31 gennaio 2009 16.22 | Feedback (2) | Filed Under [ WCF ]

martedì 27 gennaio 2009

Interazione tra Javascript e Silverlight: un password checker

La comunicazione tra Javascript and Silverlight è una tecnica che spesso rende molto semplice e potente l’introduzione di miglioramenti grafici nonché funzionali in applicazioni web preesistenti. In questo post viene mostrato nello specifico come integrare un password checker realizzato in Silverlight all’interno di un form ASP.NET di partenza, sfruttando javascript come “collante”.
Supponiamo by design che il password checker stabilisca 6 livelli di complessità raggruppabili in tre categorie: “Weak”,”Normal” e “Strong”:

 

1. Page.xaml
Partiamo dalla definizione del password checker in Silverlight. Gli elementi della UI di tale controllo, come mostrato nella figura sopra riportata, sono costituiti da 6 Rectangle ed un TextBlock disposti orizzontalmente all’interno di uno StackPanel.

<UserControl x:Class="SilverlightAppPasswordStrength.Page"
            
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">    
   ...
  

  <
Grid x:Name="LayoutRoot">                      
   
<StackPanel x:Name="mainPanel" Orientation="Horizontal">           
     
<Rectangle ...></Rectangle>
     
<Rectangle ... Margin="2,0,0,0"></Rectangle>
     
<Rectangle ... Margin="2,0,0,0"></Rectangle>
     
<Rectangle ... Margin="2,0,0,0"></Rectangle>
     
<Rectangle ... Margin="2,0,0,0"></Rectangle>
     
<Rectangle ... Margin="2,0,0,0"></Rectangle>
     
<TextBlock x:Name="txtPwdStrength" ... Margin="5,2,0,0"></TextBlock>           
   
</StackPanel>       
  </Grid>
</UserControl>


2. Page.xaml.cs

Nell’implementazione, il comportamento della UI è in mano ad un metodo NotifyStrengthLevel che sostanzialmente si prende carico di gestire il layout degli elementi in base al livello di complessità (da 0 a 5) restituito da un metodo GetPasswordStrengthLevel che implementa l’algoritmo di password cecking. Due note:

  • Tramite il metodo HtmlPage.RegisterScriptableObject possiamo “registrare” il nostro oggetto Page di Silverlight all’interno di una pagina (X)Html in modo da abilitare l’accesso da parte di javascript
  • Per essere invocabile via javascript, il metodo NotifyStrengthLevel dell’oggetto Page deve essere decorato con l’attributo ScriptableMember

public partial class Page : UserControl
{       
 
public Page()
 
{
   
InitializeComponent();
   
HtmlPage.RegisterScriptableObject("PwdStrengthControl", this);            
 
}

 
[ScriptableMember]
 
public void NotifyStrengthLevel(string pwd)
 
{
   
SolidColorBrush color = null;
   
string text = null;

   
int level = GetPasswordStrengthLevel(pwd);          
   
   
if (level <= 1)
   
{
     
color = new SolidColorBrush(Colors.Red);
     
text = "Weak";
   
}
   
else if (level > 1 && level <= 3)
   
{
     
color = new SolidColorBrush(Colors.Orange);
     
text = "Normal";
   
}
   
else if (level > 3 && level <= 5)
   
{
     
color = new SolidColorBrush(Colors.Green);
     
text = "Strong";
   
}
   
else throw new ArgumentException(string.Format("Invalid Strength level: {0}",level));

   
txtPwdStrength.Text = text;
   
txtPwdStrength.Foreground = color;

   
IEnumerable<Rectangle> rects = mainPanel.Children.OfType<Rectangle>();
   
foreach (Rectangle rect in rects) rect.Fill = new SolidColorBrush(Colors.LightGray);
   
for (int i = 0; i <= level; i++) rects.ElementAt(i).Fill = color;                       
 
}

 
private int GetPasswordStrengthLevel(string pwd)
 
{  

   int level = 0;

  
// Algoritmo per determinare il livello di sicurezza della password

  
return level;
 
}
}


3. Test.aspx
A questo punto, il nostro password checker in Silverlight è pronto per essere acceduto via javascript analizzando il DOM del form ASP.NET host. Nel seguente codice viene mostrata una possibile implementazione: ogni volta che si scatena l’evento client-side onkeyup sulla TextBox per l’inserimento della password, viene invocata una funzione javascript CheckPwdStrength che si occupa di gestire l’interazione con l’oggetto Silverlight. Da notare in particolare la sintassi:

document.getElementById("<silverlightControl>").Content.<ScriptableObject>.<ScriptableMember>(...);

utilizzata per invocare il metodo di interesse NotifyStrengthLevel (ScriptableMember) definito all’interno dell’oggetto Silverlight registrato.

<script type="text/javascript">
 
function CheckPwdStrength(pwd)
 
{
   
var control = document.getElementById("xamlPwdStrength");      
   
if (pwd.length == 0) control.style.visibility = 'hidden';
   
else
   
{
     
control.style.visibility = 'visible';
     
control.Content.PwdStrengthControl.NotifyStrengthLevel(pwd);               
   
}
  
}        
</script>
. . .

<
asp:TextBox ID="txtPassword" runat="server" TextMode="Password" onkeyup="CheckPwdStrength(this.value)" />

<asp:Silverlight ID="xamlPwdStrength" runat="server" Source="~/ClientBin/SilverlightAppPasswordStrength.xap" MinimumVersion="2.0.31005.0" Height="15px" />
. . .


Technorati Tag: ,

posted @ martedì 27 gennaio 2009 23.20 | Feedback (0) | Filed Under [ Silverlight ]

lunedì 26 gennaio 2009

[OT] TagGalaxy

Per gli amanti di Flickr segnalo TagGalaxy:

http://www.taggalaxy.com/

Merita veramente :D

posted @ lunedì 26 gennaio 2009 17.15 | Feedback (1) |

giovedì 22 gennaio 2009

[Silverlight 2] Simulare una MasterPage

Allo stato attuale, Silverlight 2 non possiede un modello di programmazione built-in per gestire il concetto di MasterPage così come avviene in ASP.NET a partire dalla versione 2.0. Ad ogni modo, per ovviare a tale mancanza, l'implementazione di una semplice soluzione custom che simula l'interazione tra una pagina "Master" e più pagine "Content" in Silverlight 2 non è poi così difficile.
L'obiettivo è fondamentalmente quello di definire un PlaceHolder all'interno di una pagina Master (che funge da RootVisual) in cui caricare e visualizzare varie pagine di contenuto. Per ottenere questo risultato vengono dunque definiti due UserControl: MasterPage e ContentPage.

- MasterPage
Supponiamo di definire la master page come un semplice UserControl, avente ad esempio il seguente classico layout:

<Grid x:Name="LayoutRoot">
 
<Grid.RowDefinitions>
   
<RowDefinition Height="100" />
   
<RowDefinition/>

  </Grid.RowDefinitions>

 

  <!-- Header -->

  <StackPanel Grid.Row="0" ... ></StackPanel>

       

  <Grid Grid.Row="1">

     <Grid.ColumnDefinitions>

        <ColumnDefinition Width="150" />

        <ColumnDefinition />

     </Grid.ColumnDefinitions>

 

    <!-- Left Column -->

    <StackPanel Grid.Column="0" ...>

      ...
   
</StackPanel>

           

    <!-- Center Column -->

    <StackPanel x:Name="ContentPagePlaceHolder" Grid.Column="1" ... />           

  </Grid>

</Grid>


Come si può notare, abbiamo un header (StackPanel) e due colonne di una Grid (una a sinistra per il menu ed una centrale per il contenuto). La colonna centrale contiene uno StackPanel nominato ContentPagePlaceHolder che funge appunto da PlaceHolder per le nostre pagine di contenuto.
Ecco una sintesi dell'implementazione della MasterPage:

public partial class MasterPage : UserControl

{

  public MasterPage()

  {

    InitializeComponent();

    LoadContentPages("contentPages.xml");

  }

 

  private void LoadContentPages(string contentPagesFile)

  {

     XDocument xDoc = XDocument.Load(contentPagesFile);

     foreach (XElement node in xDoc.Element("ContentPages").Descendants("Page"))

     {

       ContentPage page = Activator.CreateInstance(Type.GetType(node.Attribute("type").Value)) as ContentPage;

       page.PageName = node.Attribute("name").Value;    

       page.Visibility = Visibility.Collapsed;

       ContentPagePlaceHolder.Children.Add(page);

     }

   }

 

   public ContentPage GetContentPage(string pageName)

   {

     return ContentPagePlaceHolder.Children.OfType<ContentPage>().Single(p => p.PageName == pageName);

   }

 

   public void ViewContentPage(string pageName)

   {

      ViewContentPage(GetContentPage(pageName));

   }

 

   public void ViewContentPage(ContentPage page)

   {

     IEnumerable<ContentPage> contentPages = ContentPagePlaceHolder.Children.OfType<ContentPage>();

     foreach (ContentPage contentPage in contentPages)

     {

       if (!contentPage.Equals(page)) contentPage.Visibility = Visibility.Collapsed;

       else contentPage.Visibility = Visibility.Visible;

     }         

   } 

}


Tramite il metodo LoadContentPages(...) popoliamo semplicemente il ContentPagePlaceHolder con le content pages del nostro progetto che in questo caso specifichiamo in un file XML contentPages.xml:

<?xml version="1.0" encoding="utf-8" ?>

<ContentPages>

  <Page name="Page1" type="MasterPageSilverlight.Page1"></Page>

  <Page name="Page2" type="MasterPageSilverlight.Page2"></Page>

  <Page name="Page3" type="MasterPageSilverlight.Page3"></Page>
  ...

</ContentPages>


Poi, tramite il metodo ViewContentPage(...)  implementiamo il meccanismo di visualizzazione di una specifica ContentPage, che in questo caso è realizzato sfruttando la proprietà Visibility di ciascuna content page pre-caricata nel ContentPagePlaceHolder.

- ContentPage
La classe ContentPage rappresenta una pagina di contenuto visualizzabile all'interno del ContentPagePlaceHolder definito nella MasterPage. Per questa classe potremmo definire le seguenti proprietà:

  • Master: la MasterPage che contiene la ContentPage
  • PageName: il nome delle ContentPage che la identifica univocamente all'interno di un ContentPagePlaceHolder 
  • PageData: un dictionary utilizzabile per passare dati tra ContentPage 

public class ContentPage : UserControl
{

  public MasterPage Master

  {

    get { return Application.Current.RootVisual as MasterPage; }

  }

       

  public string PageName { get; set; }

 

  public Dictionary<string, object> PageData { get; set; }

 

  public ContentPage()

  {

    this.PageData = new Dictionary<string, object>();               

  }

}


Esempio di utilizzo

A questo punto abbiamo un semplice programming model per "switchare" content page sia a partire dalla MasterPage contenitore che a partire da una ContentPage contenuta nel ContentPagePlaceHolder della Master.
In particolare, 
- all'interno della MasterPage possiamo selezionare la ContentPage da visualizzare nel  ContentPagePlaceHolder nel modo seguente:

ContentPage contentPage = GetContentPage("Page2");

page.PageData["Info"] = "MyData..."; // Eventuale passaggio di dati alla pagina

ViewContentPage(contentPage);


- all'interno di una ContentPage, invece, possiamo utilizzare la reference alla MasterPage che la contiene per comunicare con un'altra ContentPage sorella:

ContentPage page = Master.GetContentPage("Page2");
page.PageData["Info"] = "MyData..."; // Eventuale passaggio di dati alla pagina
Master.ViewContentPage(page);

Technorati Tag: ,

posted @ giovedì 22 gennaio 2009 14.58 | Feedback (0) | Filed Under [ Silverlight ]

giovedì 15 gennaio 2009

jQuery 1.3 released!

Per gli amanti di jQuery ( che proprio ieri ha compiuto il terzo anno di vita :D ) segnalo questo interessante post che mette in evidenza non solo le principali feature della nuova release 1.3, ma soprattutto il lavoro che è stato dedicato al miglioramento delle performance per i browser più utilizzati.

Degna di nota è l’introduzione del nuovo API browser disponibile per Safari, Firefox 3 e IE7, scaricabile ed utilizzabile offline come una applicazione Adobe AIR.

Technorati Tag:

posted @ giovedì 15 gennaio 2009 19.23 | Feedback (0) |

Powered by: