Archivio per settembre 2011

Lightswitch Tip#1 (ADV): custom control extension

Sicuramente una delle feature molto apprezzate di Lightswitch è la possibilità di creare proprie estensioni. E’ utile capire tuttavia come queste funzionano.

In primo luogo occorre specificare che un custom control è suddiviso in due componenti distinte:

  • L’implementazione del controllo, caratteristica che utilizza l’utente finale
  • La descrizione del controllo, che ne definisce le caratteristiche usate a design-time da Lightswitch.

Con questo post vorrei sottolineare come le due parti siano completamente disgiunte.
Il file di descrizione (lsml) viene usato dall’IDE di Lightswitch per presentare al developer un plus del controllo stesso, il suo comportamento, le sue proprietà, la sua struttura… in generale tutto quello che viene passato all’implementazione del controllo tramite IContentItem.
Guardate il seguente esempio:

<Control Name="ColumnChart"
  SupportedContentItemKind="Collection"
  ChildView="ChartSeriesCollection"           
  DesignerImageResource="SindControls.Charts.ColumnChart::ColumnChartImage">
  <Control.Attributes>
    <DisplayName Value="ColumnChart" />
  </Control.Attributes>
  <Control.SupportedDataTypes>
  </Control.SupportedDataTypes>
</Control>

<Control Name="ChartSeriesCollection"
  SupportedContentItemKind="Group"
  ParentView="ColumnChart"
  ChildView="ChartSeries"
  IsHidden="True"
  DesignerImageResource="SindControls.Charts.ColumnChart::SeriesCollectionImage">
  <Control.Attributes>
    <DisplayName Value="Series Collection"/>
  </Control.Attributes>
</Control>

<Control Name="ChartSeries"
  SupportedContentItemKind="Value"
  ParentView="ChartSeriesCollection"
  DesignerImageResource="SindControls.Charts.ColumnChart::SeriesImage">
  <Control.Attributes>
    <DisplayName Value="Series"/>
  </Control.Attributes>
  <Control.SupportedDataTypes>
    <SupportedDataType DataType=":Double"/>
    <SupportedDataType DataType=":Decimal"/>
    <SupportedDataType DataType=":Int16"/>
    <SupportedDataType DataType=":Int32"/>
    <SupportedDataType DataType=":Int64"/>
  </Control.SupportedDataTypes>
</Control>

In questo caso potreste aspettarvi la definizione via codice di 3 controlli, ColumnChart, ChartSeriesCollection and ChartSeries… beh, non è vero! Smile

Nel mio caso ho implementato solo il controllo ColumnChart. Il designer time prenderà in considerazione la tipologia dei figli, le proprietà, i tipi supportati e altro. Il mio controllo utilizzerà queste informazioni recuperandole da IContentItem.ChildItems nelle modalità e tempi in cui ritiene opportuno.

Pubblicità

, ,

Lascia un commento

Selezionare il contenuto di una TextBox WPF al focus

Riporto qui la soluzione presentata da Donelle su stackoverflow con una piccola modifica.
Innanzitutto il link originale: http://stackoverflow.com/questions/660554/how-to-automatically-select-all-text-on-focus-in-wpf-textbox

La modifica consiste nell’includere il comportamento come un behavior. Ecco il codice:

public static class TextBoxBehaviors
{
    public static void SetSelectOnFocus(UIElement element, Boolean value)
    {
        element.SetValue(SelectOnFocusProperty, value);
    }
    public static bool GetSelectOnFocus(UIElement element)
    {
        return (bool)element.GetValue(SelectOnFocusProperty);
    }
    public static readonly DependencyProperty SelectOnFocusProperty =
        DependencyProperty.RegisterAttached("SelectOnFocus", typeof(bool), typeof(TextBoxBehaviors),
        new FrameworkPropertyMetadata(false, SelectOnFocusPropertyChangedCallback));

    private static void SelectOnFocusPropertyChangedCallback(DependencyObject depObj, DependencyPropertyChangedEventArgs eventArgs)
    {
        TextBox tb = depObj as TextBox;
        if (tb != null)
        {
            if ((bool)eventArgs.OldValue == false && (bool)eventArgs.NewValue == true)
            {
                //attach events      
                tb.PreviewMouseLeftButtonDown += SelectivelyIgnoreMouseButton;
                tb.GotKeyboardFocus += SelectAllText;
                tb.MouseDoubleClick += SelectAllText;
            }
            else if ((bool)eventArgs.OldValue == true && (bool)eventArgs.NewValue == false)
            {
                //detach events     
                tb.PreviewMouseLeftButtonDown -= SelectivelyIgnoreMouseButton;
                tb.GotKeyboardFocus -= SelectAllText;
                tb.MouseDoubleClick -= SelectAllText;
            }
        }
    }
    private static void SelectivelyIgnoreMouseButton(object sender, MouseButtonEventArgs e)
    {
        DependencyObject parent = e.OriginalSource as UIElement;
        while (parent != null && !(parent is TextBox))
            parent = VisualTreeHelper.GetParent(parent);

        if (parent != null)
        {
            var textBox = (TextBox)parent;
            if (!textBox.IsKeyboardFocusWithin)
            {
                textBox.Focus();
                e.Handled = true;
            }
        }
    }
    private static void SelectAllText(object sender, RoutedEventArgs e)
    {
        var textBox = e.OriginalSource as TextBox;
        if (textBox != null)
            textBox.SelectAll();
    }
}

E’ possibile ora aggiungere nello xaml il comportamento aggiungendo il behavior:

 <TextBox Text="Selezionami per provare" behaviors:TextBoxBehaviors.SelectOnFocus="True" />

,

Lascia un commento

Metodo usato per installare Windows 8 Developer Preview

Segnalo qui un metodo abbastanza semplice e veloce per installare e avviare Windows 8 su un VHD (senza quindi partizionare il disco fisico):

http://www.pitorque.de/MisterGoodcat/post/Installing-Windows-8-Developer-Preview-as-bootable-VHD.aspx

A differenza di altri metodi, questo non comporta il download di AIK, guadagnando un bel po’ di tempo!

Ricordo che, virtualizzando il disco fisico di windows 8, le performance non sono le ottimali. Tuttavia, a quanto leggo, dovrebbe esserci solo un peggioramento del 3-5%.

, ,

Lascia un commento

INotifyPropertyChanged serve davvero? Sì! Ma le cose si fanno complicate…

Leggendo per caso il blog di Gianni Giaccaglini (http://blogs.wpfitalia.it/GianniGiaccaglini/archive/2011/09/08/Binding-ADO-tramite-classe-ad-hoc.aspx) riguardo il binding verso dataset, ho notato un capitolo, “INotifyPropertyChanged server davvero?” e mi sono ricordato di un problema simile (forse lo stesso) che mi è capitato tempo fa.
Iniziamo dal codice xaml e il datacontext:

<StackPanel>
    <TextBox Text="{Binding Path=PrimaProp}" />
    <TextBox Text="{Binding Path=PrimaProp}" />
    <TextBox Text="{Binding Path=SecondaProp}" />
    <TextBox Text="{Binding Path=SecondaProp}" />
    <Button Content="Cambia" Click="Button_Click" />
</StackPanel>

public class MyDataContext
{
    public string PrimaProp { get; set; }
    public string SecondaProp { get; set; }
}

Qualcuno noterà che MyDataContext (nel codebehind viene impostata un istanza come DataContext) non implementa INotifyPropertyChanged.
Modificando una textbox ci si aspetterebbe che le altre non cambino perchè non si notifica alla vista che la proprietà è stata cambiata… E invece tutto funziona!
Ai tempi pensai che l’engine di wpf fosse abbastanza intelligente da usare un oggetto binding uguale se viene scritto uguale in più parti… invece no Smile.
Se proviamo infatti a impostare via codice il binding (es: textbox1.SetBinding(TextBox.TextProperty, new Binding(“PrimaProp”))), con nuove istanze di Binding su diversi oggetti, continua ancora a funzionare.

Proviamo a cambiare la proprietà del datacontext da codice tramite il bottone con:
(this.DataContext as MyDataContext).SecondaProp=”modifica da codice”… NON FUNZIONA!

Non sapendo dove andare a parare, ho cercato con google, e infine ho trovato la soluzione indicata qui: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/9365bb6a-b411-4967-9a03-ae2a810fb215/

Sunto della soluzione:
– Se un oggetto CLR implementa INotifyPropertyChanged, è cura del developer lanciare l’evento NotifyPropertyChanged
– Se un oggetto CLR NON implementa INotifyPropertyChanged, il binding lavora sul TypeDescriptor dell’oggetto. Esso va a registrare alla modifica delle proprietà tramite PropertyDescriptor.AddValueChanged().

Quindi:
1) come far andare il codice precedente riguardo il bottone:
”semplice” (?!?) basta recuperare la PropertyDescriptor dell’oggetto e impostare così la proprietà:
var propertyDescriptor = TypeDescriptor.GetProperties(this.DataContext).Find(“SecondaProp”, false);
propertyDescriptor.SetValue(this.DataContext, “ModificaDaCodice”);

2) come far smettere di andare il precedente esempio:
Implementiamo l’interfaccia INotifyPropertyChanged e propaghiamo l’evento solo per PrimaProp. Questa funziona, ma SecondaProp no Smile.

Performance (mentre l’ho fatto ho sentito l’esigenza di un altro post)

Se eseguo il codice nel thread UI, le performance sono molto simili.
Se eseguo il codice in un altro thread, l’utilizzo di INotifyPropertyChanged risulta più rapido rispetto a PropertyDescriptor.

Conclusioni:

Data la flessibilità e la maggior facilità implementativa, la mia preferenza ricade indubbiamente a INotifyPropertyChanged. Le performance inoltre sembrerebbero avvalorare tale scelta. Quindi Me.NofifyPropertyChanged(“you”);

,

Lascia un commento

Creare un Value Custom Control riutilizzabile in LightSwitch

Segnalo la pubblicazione del mio primo articolo riguardo Microsoft LightSwitch 2011.

Nell’articolo si mostra come creare, tramite LS extensibility toolkit, un custom control utilizzabile dal design di LS.

Link: http://www.lightswitch.it/Contenuti/VediArticoli/tabid/136/ArticleId/60/Creare-un-Value-Custom-Control-riutilizzabile-in-LightSwitch.aspx

Il controllo è stato pubblicato anche nella VisualStudioGallery: http://visualstudiogallery.msdn.microsoft.com/6b554673-518a-4b94-94ff-300d5b50e20a, potete così scaricarlo direttamente da visual studio tramite Extension Manager.

Inoltre è possibile scaricare LS_SnapSlider.zip con articolo e codice qui: https://skydrive.live.com/?cid=086302121a4df1d2&sc=documents&id=86302121A4DF1D2%21126

Lascia un commento