WPF: Proprietà e TypeConverter

Scritto da  Massimo Bonanni il mercoledì 15 dicembre 2010
Linguaggio: C#,VB   •  Framework: 3.5,4.0   •  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:

  1. 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à;
  2. 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;
  3. 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:

 Fig.01

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

Fig.02

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:

Fig.03

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:

Fig.04


Tags: WPF,windows presentation

 
x