WPF: Proprietà e TypeConverter
Scritto da
Massimo Bonanni
il
mercoledì 15 dicembre 2010
Linguaggio:
•
Framework:
•
Livello: 100
Download sorgenti
In questo articolo analizzeremo il meccanismo di assegnazione
dei valori delle proprietà nei controlli WPF.
Il modo più semplice per assegnare il valore ad una proprietà di
un controllo XAML, è quello di valorizzare il corrispondente
attributo del tag XML come mostrato nel seguente esempio:
<TextBlock Background="Yellow" FontSize="14">
</TextBlock>
In questo caso impostiamo con il colore giallo
lo sfondo del TextBlock e con il valore 14 la
dimensione del font.
Se prendiamo in esame la classe TextBlock notiamo
che la proprietà Background è di tipo Brush mentre la proprietà
FontSize è di tipo Double.
Come fa il parser XAML ad assegnare correttamente i valori da
noi impostati negli attributi alle rispettive proprietà visto che
tali valori sono di tipo String?
Per queste operazioni intervengono i TypeConverter la cui classe
di base (TypeConverter) esiste fin dalla prima versione del
framework ma che trovano larghissimo impiego in WPF.
Un TypeConverter mette a disposizione dei metodi che permettono
la conversione di un oggetto in un altro e viceversa. In
questo caso, essendo i valori delle proprietà indicati con delle
stringhe, i metodi trasformano la stringhe in oggetti.
Quando il parser XAML incontra l'assegnazione di una proprietà
attraverso un attributo, esegue i seguenti passi:
- Esamina la dichiarazione della proprietà per verificare se è
presente l'attributo TypeConverterAttribute che indica quale classe
si occupa della conversione. Se tale attributo è presente, il
framework utilizza il TypeConverter indicato per effettuare la
conversione ed assegnare la proprietà;
- Se la proprietà non è decorata con l'attributo
TypeConverterAttribute, il framework verifica la presenza
dell'attributo TypeConverterAttribute nella definizione della
classe tipo della proprietà. Se presente, il framework utilizza la
classe definita in questo attributo per la conversione e la
relativa assegnazione;
- Se non sono verificati i primi due passi, si ottiene un
errore.
Vediamo concretamente quanto detto nel caso del tag
precedente.
Nel caso della proprietà Background, questa non è decorata con
l'attributo TypeConverterAttribute, come possiamo vedere
utilizzando Reflector:

quindi il parser XAML analizza la classe Brush per vedere quale
TypeConverter utilizzare per la conversione della stringa
"Yellow":

La classe Brush è decorata con l'attributo
TypeConverterAttribute (come possiamo vedere nella figura
precedente) che comunica al framework di utilizzare BrushConverter
per la conversione dal tipo String.
Il framework, dunque, istanzia un oggetto di classe
BrushConverter e richiama il metodo ConvertFrom() a cui passa la
stringa che abbiamo impostato nell'attributo Background del tag
TextBlock.
Nel caso dell'attributo FontSize, invece, il framework recupera
il converter da utilizzare direttamente dalla proprietà FontSize
del TextBlock:

In particolare, il framework utilizzerà il FontSizeConverter per
convertire il valore da String a Double.
I TypeConverter possono essere utilizzati anche a livello di
codice. Per esempio, per convertire una stringa in un FontSize
possiamo scrivere:
VB
Dim fontSizeConv = New FontSizeConverter
Dim fontSize = fontSizeConv.ConvertFrom("14")
C#
FontSizeConverter fontSizeConv = new FontSizeConverter ();
object fontSize = fontSizeConv.ConvertFrom("14");
Vediamo ora un esempio pratico di come vengono impostate le
proprietà tramite i TypeConverter.
Estendiamo un controllo TextBlock che chiameremo MyTextBlock ed
inseriamo una proprietà pubblica, MyProperty, di tipo List(of
Double).
VB
Imports System.Windows.Controls
Imports System.ComponentModel
Public Class MyTextBlock
Inherits TextBlock
<TypeConverter (GetType (ListOfDoubleConverter ))>
Public Property MyProperty As List (Of Double )
End Class
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.ComponentModel;
using Converters;
namespace Controls
{
public class MyTextBlock : TextBlock
{
[TypeConverter (typeof (ListOfDoubleConverter ))]
public List <double > MyProperty { set ; get ; }
}
}
Per permettere a chi utilizza il nostro controllo di poter
impostare la proprietà come attributo del tag XAML (supponiamo che
lo possa fare separando i valori con il carattere ";"), è
necessario decorare la proprietà MyProperty con l'attributo
TypeConverterAttribute. Nel caso specifico, diciamo al framework
che, ogni volta che deve impostare la proprietà con un oggetto che
non è dello stesso tipo della proprietà stessa, deve richiamare
un'istanza della classe ListOfDoubleConverter.
VB
Imports System.ComponentModel
Public Class ListOfDoubleConverter
Inherits TypeConverter
Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext,
ByVal culture As System.Globalization.CultureInfo,
ByVal value As Object) As Object
Dim retList As List(Of Double) = Nothing
If TypeOf value Is String Then
Dim strArray = CType(value, String).Split(";"c)
retList = New List(Of Double)
Array.ForEach(Of String)(strArray, Sub(s)
Dim dVal As Double = 0
If Double.TryParse(s, dVal) Then
retList.Add(dVal)
Else
Throw New ArgumentOutOfRangeException()
End If
End Sub)
End If
Return retList
End Function
End Class
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace Converters
{
public class ListOfDoubleConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture,
object value)
{
List<Double> retList = new List<Double>();
if (value is String)
{
String[] strArray = ((String)value).Split(';');
Array.ForEach<String>(strArray, delegate(string s)
{
Double dVal = 0;
if (Double.TryParse(s, out dVal))
{
retList.Add(dVal);
}
else
{
throw new ArgumentOutOfRangeException();
}
});
}
return retList;
}
}
}
In questo caso, vista la natura didattica del codice, ci siamo
limitati ad implementare l'override del metodo ConvertFrom in cui,
nel caso in cui il valore da convertire sia stringa, provvede a
scomporre il valore stesso e creare la lista che verrà
ritornata al chiamante (se tutti i singoli valori sono convertibili
in double).
A questo punto possiamo creare la nostra finestra in cui
utilizzare il controllo:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:ClassLibrary;assembly=ClassLibrary"
Title="MainWindow" Height="350" Width="525">
<Grid>
<my:MyTextBlock MyProperty="2;3,5;6;1,9"></my:MyTextBlock>
</Grid>
</Window>
Il flusso di esecuzione è schematizzato nella seguente
immagine:

Tags: WPF,windows presentation