Pubblicato un articolo dove vengono mostrare le caratteristiche multitouch di WPF4.
Sicuramente il dispositivi Touch saranno il futuro per quanto riguarda l’interfacciamento uomo-macchina, quindi cominciare fin da subito a capire come funziona e conoscere gli strumenti a disposizione è indubbiamente un vantaggio.
Buona lettura: http://www.wpfitalia.it/Contenuti/VediArticoli/tabid/83/ArticleId/46/Il-supporto-al-multi-touch-in-WPF-4.aspx
Visualizzazione post con etichetta Tips and Tricks. Mostra tutti i post
Visualizzazione post con etichetta Tips and Tricks. Mostra tutti i post
mercoledì 2 novembre 2011
mercoledì 19 ottobre 2011
Client Profile : framework 3.5 Sp1
Con la SP1 del framework 3.5 e' stata introdotto il profile client.
<supportedRuntime version="v2.0.50727" sku="Client"/>
</startup>
Come ben sapete, .NET Framework è il prerequisito imprescindibile per l'esecuzione di applicazioni .NET e dev'essere incluso nelle procedure di distribuzione (o scaricato da Internet) affinchè i nostri utenti possano utilizzare le nostre applicazioni. Il problema di base è che .NET Framework 3.5 full package ha delle dimensioni effettivamente notevoli, rispetto ai suoi predecessori.
Partendo da questo presupposto, il team di sviluppo di .NET Framework ha introdotto la possibilità di distribuire una versione ridotta del run-time (il Client Profile) dedicata esclusivamente alle applicazioni client. Tale versione ridotta (circa 28 Megabyte contro gli oltre 300 della versione completa) è costituita da un sub-set di assembly appartenenti al più completo .NET 3.5, assembly in tutto e per tutto identici a quelli del fratello maggiore. In sostanza, il Client Profile integra gli assembly inerenti le seguenti tecnologie:
- Common Language Runtime
- Windows Forms
- Windows Presentation Foundation
- Windows Communication Foundation
- ClickOnce
La logica di base è: se la mia è un'applicazione per Windows, perchè devo distribuire anche gli assembly dedicati ad ASP.NET occupando spazio inutilizzato? In questo modo le proprie applicazioni .NET per Windows potranno funzionare normalmente, risparmiando spazio. Inoltre, questo tipo di approccio è senza dubbio molto utile in ambienti Windows XP SP 2 o Windows Server 2003 (che non integrano, per default, .NET 2.0 o superiore).
Fatta questa premessa discorsiva, andiamo ora a vedere come il tutto si realizza attraverso Visual Studio 2008 in ambito Visual Basic. E' evidente come il Client Profile sia applicabile nei confronti delle tipologie di applicazioni sopra descritte. Per attivare il nostro sub-set all'interno di un'applicazione, dobbiamo innanzitutto aprire la finestra My Project, selezionare la scheda Compilazione e fare clic sul pulsante Opzioni di compilazione avanzate. Quando compare la finestra, è necessario fare clic sul segno di spunta riferito alla voce Sottoinsieme .NET Framework solo client, come nella seguente figura:
Il passaggio successivo è quello di compilare il progetto. Questo farà sì che Visual Studio 2008 sia in grado di determinare se effettivamente la nostra applicazione potrà girare sotto il Client Profile o se, invece, richiederà la presenza della versione full di .NET Framework 3.5. Nel caso in cui siano necessari assembly non compresi nel Client Profile, verranno visualizzati dei messaggi di avviso nella finestra di Output.
Il Client Profile funziona sia nelle distribuzioni ClickOnce che in quelle per Windows Installer (progetti di distribuzione). Nel momento in cui compiliamo il progetto (vedi passaggio precedente), il Client Profile diventa il prerequisito principale ed esclude il fratello maggiore, come si evince dalla seguente figura:
Nel momento in cui la procedura di distribuzione (ClickOnce o Windows Installer) viene assemblata, come di consueto viene prodotto un bootstrapper chiamato Setup.exe. Tale bootstrapper è stato specificamente migliorato per questo tipo di scenario e il suo "peso" è di circa 200 Kb. Quando avviato, Setup.exe verifica la presenza di .NET Framework 3.5 completo e, qualora non rilevato, installa la versione Client.
Un ultimo elemento da notare è che, sfruttando il client profile, al progetto viene aggiunto un file App.config, contenente un nuovo elemento Xml, che indica all'applicazione che il run-time da utilizzare è quello ridotto:
<startup><supportedRuntime version="v2.0.50727" sku="Client"/>
</startup>
mercoledì 12 ottobre 2011
Memory Leak in WPF
Non vi e' mail capitato di lavorare con immagini e nonostante non vengano piu' usate il vostro programma continua ad allocare memoria?
Una delle ragioni potrebbe essere il memory leak che affliggeva il framework 3.5. Il problema e' stato risolto con la SP1 e in particolare con la KB981107.
Un'altra ragione potrebbe essere l'uso spropositato della BitmapSource senza utilizzare il metodo freeze.
Se utilizzate variabili di tipo BitmapSource assicuratevi di farne il freeze in questo modo il GC rilascerà la memoria se non piu' utilizzata.
BitmapSource objBS = .....
if (objBS.CanFreeze) objBS.Freeze();
Se volete sapere quale framework avete installato seguite il seguente post.
Una delle ragioni potrebbe essere il memory leak che affliggeva il framework 3.5. Il problema e' stato risolto con la SP1 e in particolare con la KB981107.
Un'altra ragione potrebbe essere l'uso spropositato della BitmapSource senza utilizzare il metodo freeze.
Se utilizzate variabili di tipo BitmapSource assicuratevi di farne il freeze in questo modo il GC rilascerà la memoria se non piu' utilizzata.
BitmapSource objBS = .....
if (objBS.CanFreeze) objBS.Freeze();
Se volete sapere quale framework avete installato seguite il seguente post.
lunedì 20 giugno 2011
ListBox con immagini
Il seguente esempio mostra come fare per inserire delle immagini in una listbox. La visualizzazione prevede gli items messi in orizzontale e per ogni item e' prevvista un'immagine e un testo alla base.
Per tale scopo viene creata una listbox facente riferimento ad una risorsa "listStyle" che definisce il template per l'item da visualizzare. Si noti che viene nascosta la barra verticale, mentre si utilizzarà solo quello orizzontale.
<ListBox
ItemContainerStyle="{StaticResource listStyle}"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
A questo punto nella sezione Resources della pagine e del controllo definiamo lo stile per l'item.
<Style TargetType="{x:Type ListBoxItem}" x:Key="listStyle">
<Setter Property="Template" Value="{StaticResource listControlTemplate}" ></Setter>
<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=ActualHeight}" />
</Style>
In questo modo definiamo i SETTER e facciamo in modo che se la nostra listbox e' ridimensionabile in altezza, gli item verranno ridimensionati in modo da occupare tutta l'altezza e la larghezza verrà ridimensionata in modo proporzionale.
Definiamo quindi lo style listControlTemplate.
<ControlTemplate x:Key="listControlTemplate" TargetType="{x:Type ListBoxItem}" >
<Border x:Name="BorderImage" Margin="3,3,3,20" >
<Grid x:Name="listItemGrid" VerticalAlignment="Stretch" >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Border BorderThickness="1" Background="Black" >
<Image x:Name="coverImage2" Tag="{Binding nPage}" Source="{Binding ., Converter={StaticResource ImageConverter} }" Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Grid.Row="0" RenderTransformOrigin="1,1">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="0"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Image.RenderTransform>
</Image>
</Border>
<TextBlock Grid.Row="1" Text="{Binding nPage}" FontWeight="Bold"
HorizontalAlignment="Center" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=sfondo}" Value="1">
<Setter Property="Background" Value="Aquamarine" TargetName="BorderImage"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding sfondo}" Value="10" >
<Setter Property="Background" Value="Black" TargetName="BorderImage"></Setter>
</DataTrigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Blue" TargetName="BorderImage"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Quindi definiamo che il binding dell'immagine avviene tramite ImageConvert.
<local:ImageConverter x:Key="ImageConverter" />
Questa e' una classe nella quale possiamo prendere le nostre immagini da file, da database o altro. Ovviamente l'informazione sarà nel campo (io normalmente uso delle classi in qui definisco i campi e poi per la lista dei dati uso una ObservableCollection che collego alla listbox.
La classe viene definita in questo modo :
#region "Converter Image"
public sealed class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
try
{
BitmapSource btImage = null;
PageInfo pg = (PageInfo)value;
if (pg.IconPath != "")
{
BitmapImage ba = new BitmapImage(new Uri(pg.IconPath));
ba.BeginInit();
ba.CacheOption = BitmapCacheOption.OnLoad;
ba.EndInit();
btImage = ba;
}
else
{
btImage =new BitmapImage();
}
if (btImage.CanFreeze) btImage.Freeze();
return btImage;
}
catch
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
#endregion
La classe PageInfo contiene i campi di cui faccio binding nella listbox; nel campo IconPath c'e' il path dell'immagine.
Ricordarsi di fare il Freeze dell'immagine altrimenti il GC non libererà mai la memoria anche nel caso si pulisca la listview.
Per tale scopo viene creata una listbox facente riferimento ad una risorsa "listStyle" che definisce il template per l'item da visualizzare. Si noti che viene nascosta la barra verticale, mentre si utilizzarà solo quello orizzontale.
<ListBox
ItemContainerStyle="{StaticResource listStyle}"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
A questo punto nella sezione Resources della pagine e del controllo definiamo lo stile per l'item.
<Style TargetType="{x:Type ListBoxItem}" x:Key="listStyle">
<Setter Property="Template" Value="{StaticResource listControlTemplate}" ></Setter>
<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=ActualHeight}" />
</Style>
In questo modo definiamo i SETTER e facciamo in modo che se la nostra listbox e' ridimensionabile in altezza, gli item verranno ridimensionati in modo da occupare tutta l'altezza e la larghezza verrà ridimensionata in modo proporzionale.
Definiamo quindi lo style listControlTemplate.
<ControlTemplate x:Key="listControlTemplate" TargetType="{x:Type ListBoxItem}" >
<Border x:Name="BorderImage" Margin="3,3,3,20" >
<Grid x:Name="listItemGrid" VerticalAlignment="Stretch" >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Border BorderThickness="1" Background="Black" >
<Image x:Name="coverImage2" Tag="{Binding nPage}" Source="{Binding ., Converter={StaticResource ImageConverter} }" Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Grid.Row="0" RenderTransformOrigin="1,1">
<Image.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="1" ScaleY="1"/>
<SkewTransform AngleX="0" AngleY="0"/>
<RotateTransform Angle="0"/>
<TranslateTransform X="0" Y="0"/>
</TransformGroup>
</Image.RenderTransform>
</Image>
</Border>
<TextBlock Grid.Row="1" Text="{Binding nPage}" FontWeight="Bold"
HorizontalAlignment="Center" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=sfondo}" Value="1">
<Setter Property="Background" Value="Aquamarine" TargetName="BorderImage"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding sfondo}" Value="10" >
<Setter Property="Background" Value="Black" TargetName="BorderImage"></Setter>
</DataTrigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Blue" TargetName="BorderImage"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Quindi definiamo che il binding dell'immagine avviene tramite ImageConvert.
<local:ImageConverter x:Key="ImageConverter" />
Questa e' una classe nella quale possiamo prendere le nostre immagini da file, da database o altro. Ovviamente l'informazione sarà nel campo (io normalmente uso delle classi in qui definisco i campi e poi per la lista dei dati uso una ObservableCollection che collego alla listbox.
La classe viene definita in questo modo :
#region "Converter Image"
public sealed class ImageConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
try
{
BitmapSource btImage = null;
PageInfo pg = (PageInfo)value;
if (pg.IconPath != "")
{
BitmapImage ba = new BitmapImage(new Uri(pg.IconPath));
ba.BeginInit();
ba.CacheOption = BitmapCacheOption.OnLoad;
ba.EndInit();
btImage = ba;
}
else
{
btImage =new BitmapImage();
}
if (btImage.CanFreeze) btImage.Freeze();
return btImage;
}
catch
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
#endregion
La classe PageInfo contiene i campi di cui faccio binding nella listbox; nel campo IconPath c'e' il path dell'immagine.
Ricordarsi di fare il Freeze dell'immagine altrimenti il GC non libererà mai la memoria anche nel caso si pulisca la listview.
martedì 3 maggio 2011
Selezionare il contenuto di una TextBox al Focus
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:
E’ possibile ora aggiungere nello xaml il comportamento aggiungendo il behavior:
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(); } }
<TextBox Text="Selezionami per provare" behaviors:TextBoxBehaviors.SelectOnFocus="True" />
Etichette:
Codice,
Esempio,
TextBox,
Tips and Tricks
martedì 15 marzo 2011
ListView Riordinabile
Una delle esigenze più frequenti durante lo sviluppo di applicazioni con
Windows Presentation Foundation è la rappresentazione di dati nel classico
formato Grid. In Windows Presentation Foundation, almeno fino alla versione 3.5
del framework, viene
utilizzato il controllo ListView.
Il controllo ListView di WPF, infatti, mette a disposizione un tipo di visualizzazione di tipo, appunto, GridView che permette la gestione di più colonne. Nell’esempio seguente viene definito un controllo ListView con modalità di visualizzazione GridView composta da 4 colonne in binding con alcune proprietà dell’entità Person.
Nel listato, come noterete, viene definito l’handler dell’evento Click di GridViewColumnHeader dove verrà gestito, appunto, il riordinamento della colonna cliccata.
Per ottenere l’ordinamento della collection utilizzeremo un metodo chiamato Sort che, attraverso l’uso di una CollectionViewSource effettua l’ordinamento per il campo passato come parametro al metodo (sortBy).
Passiamo ora alla gestione dell’evento di click sull’header di una colonna della ListView.
Come potete vedere, la logica presente nell’evento è abbastanza semplice. Qui, infatti, viene, innanzitutto, intercettato l’intestazione cliccata, e, in base alla direzione di ordinamento (Ascending o Descending) viene applicato un template diverso per visualizzare una freccetta nell’intestazione per identificarnet, appunto, l’orientamento.
Ovviemente, in questo evento, viene richiamata il metodo Sort dichiarato in precedenza. Il codice XAML per la definizione dei template da utilizzare per la visualizzazione delle freccetta di indicazione nelle intestazioni è il seguente.
Concludendo, quindi, ad un click su qualsiasi intestazione di colonna, viene effettuato l’ordinamento per la colonna in oggetto e sostituito il template dell’intestazione stessa affinchè venga in essa visualizzata una freccetta per identificarne la direzione dell’ordinamento effettuato.
Il controllo ListView di WPF, infatti, mette a disposizione un tipo di visualizzazione di tipo, appunto, GridView che permette la gestione di più colonne. Nell’esempio seguente viene definito un controllo ListView con modalità di visualizzazione GridView composta da 4 colonne in binding con alcune proprietà dell’entità Person.
<ListView x:Name="listPersons" ItemsSource="{StaticResource persons}" BorderThickness="0" Margin="5" GridViewColumnHeader.Click="GridViewColumnHeader_Click" > <ListView.View> <GridView AllowsColumnReorder="True"> <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Path=ID}" Width="50" /> <GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding Path=FirstName}" Width="100"/> <GridViewColumn Header="LastName" DisplayMemberBinding="{Binding Path=LastName}" Width="100"/> <GridViewColumn Header="City" DisplayMemberBinding="{Binding Path=City}" Width="80" /> </GridView> </ListView.View> </ListView>
Nel listato, come noterete, viene definito l’handler dell’evento Click di GridViewColumnHeader dove verrà gestito, appunto, il riordinamento della colonna cliccata.
Per ottenere l’ordinamento della collection utilizzeremo un metodo chiamato Sort che, attraverso l’uso di una CollectionViewSource effettua l’ordinamento per il campo passato come parametro al metodo (sortBy).
private void Sort(string sortBy, ListSortDirection direction) { ICollectionView dataView = CollectionViewSource.GetDefaultView(listPersons.ItemsSource); dataView.SortDescriptions.Clear(); SortDescription sd = new SortDescription(sortBy, direction); dataView.SortDescriptions.Add(sd); dataView.Refresh(); }
Passiamo ora alla gestione dell’evento di click sull’header di una colonna della ListView.
GridViewColumnHeader _lastSelectedHeader = null; ListSortDirection _lastDirection = ListSortDirection.Ascending; void GridViewColumnHeader_Click(object sender, RoutedEventArgs e) { GridViewColumnHeader selectedHeader = e.OriginalSource as GridViewColumnHeader; ListSortDirection direction; if (selectedHeader != null) { if (selectedHeader.Role != GridViewColumnHeaderRole.Padding) { if (selectedHeader != _lastSelectedHeader) { direction = ListSortDirection.Ascending; } else { if (_lastDirection == ListSortDirection.Ascending) { direction = ListSortDirection.Descending; } else { direction = ListSortDirection.Ascending; } } string header = string.Empty; if (selectedHeader.Column.CellTemplateSelector != null) { header = selectedHeader.Column.Header.ToString(); } else if (selectedHeader.Column.DisplayMemberBinding != null) { header = ((Binding)selectedHeader.Column.DisplayMemberBinding).Path.Path; } else { return; } Sort(header, direction); if (direction == ListSortDirection.Ascending) { selectedHeader.Column.HeaderTemplate = FindResource("HeaderTemplateArrowUp") as DataTemplate; } else { selectedHeader.Column.HeaderTemplate = FindResource("HeaderTemplateArrowDown") as DataTemplate; } // Remove arrow from previously sorted header if (_lastSelectedHeader != null && _lastSelectedHeader != selectedHeader) { _lastSelectedHeader.Column.HeaderTemplate = null; } _lastSelectedHeader = selectedHeader; _lastDirection = direction; } } }
Come potete vedere, la logica presente nell’evento è abbastanza semplice. Qui, infatti, viene, innanzitutto, intercettato l’intestazione cliccata, e, in base alla direzione di ordinamento (Ascending o Descending) viene applicato un template diverso per visualizzare una freccetta nell’intestazione per identificarnet, appunto, l’orientamento.
Ovviemente, in questo evento, viene richiamata il metodo Sort dichiarato in precedenza. Il codice XAML per la definizione dei template da utilizzare per la visualizzazione delle freccetta di indicazione nelle intestazioni è il seguente.
<DataTemplate x:Key="HeaderTemplateArrowUp"> <DockPanel> <TextBlock HorizontalAlignment="Center" Text="{Binding}"/> <Path x:Name="arrow" StrokeThickness = "1" Fill="Black" Data="M 5,10 L 15,10 L 10,5 L 5,10"/> </DockPanel> </DataTemplate> <DataTemplate x:Key="HeaderTemplateArrowDown"> <DockPanel> <TextBlock HorizontalAlignment="Center" Text="{Binding }"/> <Path x:Name="arrow" StrokeThickness="1" Fill="Black" Data= "M 5,5 L 10,10 L 15,5 L 5,5"/> </DockPanel> </DataTemplate>
Concludendo, quindi, ad un click su qualsiasi intestazione di colonna, viene effettuato l’ordinamento per la colonna in oggetto e sostituito il template dell’intestazione stessa affinchè venga in essa visualizzata una freccetta per identificarne la direzione dell’ordinamento effettuato.
Iscriviti a:
Post (Atom)

