TypeConverter je třída ve jmenném prostoru System.ComponentModel. Jak již název napovídá, slouží ke konverzi mezi dvěma datovými typy. V praxi se většinou jedná o konverzi mezi řetězcem a vlastním datovým typem. TypeConverter zajistí převod datového typu na řetězec (ten se pak zobrazí v PropertyGridu) a zpět. Tato funkcionalita je velmi důležitá pro to, aby nám PropertyGrid umožnil editovat neframeworkové typy stejně, jako frameworkové.
Praxe
Každý prvek, který dědí z Control, dědí i vlastnost Location. Její hodnota je datového typu Point. Point má vlastnosti X a Y. Pokud si na formulář umístíme nějaký takový prvek (e.g. Button) a přejdeme na vlastnost Location, uvidíme jí v Properties reprezentovanou řetězcem "X, Y". Location můžeme rozkliknout na X a Y a editovat každou složku zvlášť, ale také můžeme do Location napsat přímo řetězec se souřadnicemi. Pak nastupují TypeConvertery. PropertyGrid předá TypeConverteru řetězec a ten zkontroluje zda je ve správném formátu (pokud ne, vyhodí výjimku) a překonvertuje na Point. Výsledný Point vrátí a PropertyGrid jej nastaví objektu, který edituje.
Třídu TypeConverter rozšiřuje několik dalších typů konverterů v System.ComponentModel. Například:
- ArrayConverter
- ByteConverter
- BooleanConverter
- CollectionConverter
- ... a mnoho dalších
Tyto konvertery jsou psané na míru pro určité datové typy. Nás ale bude zajímat hlavně třída ExpandableObjectConverter. Pokud náš TypeConverter bude dědit z této třídy, PropertyGrid k vlastnosti přidá PlusMinus, jež umožní její rozkliknutí na složky.
Aplikace
Vlastní konvertery se aplikují na datový typ, ne na vlastnost. Aplikují se pomocí atributu TypeConverter. My si teď vytvoříme třídu Song, která bude obsahovat některé základní informace o písni. Na ní si později ukážeme používání TypeConverterů.
Public Class Song
Private _title As String = String.Empty
Private _artist As String = String.Empty
Private _album As String = String.Empty
Public Property Title() As String
Get
Return _title
End Get
Set(ByVal value As String)
_title = value
End Set
End Property
' ... a vlastnosti Artist a Album
End Class
Teď si do našeho projektu přidáme komponentu Person z minulého dílu. Pokud jste minulý díl nečetli, můžete si zdrojové kódy stáhnout zde:
Zdrojové kódy
Otevřme se třídu Person a přidejme jí vlastnost Song (já jí umístím do kategorie Oblíbené). Tato vlastnost je typu Song. Když si sestavíme projekt, uvidíme následující:
Jak můžeme vidět, vlastnost Song (zde reprezentovaná jako 'Oblíbená skladba' pomocí DisplayName) je zašedlá a nemůžeme ji editovat. Jedná se totiž o vlastnost, která nabývá hodnoty vlastního datového typu (Song) a neexistuje pro ni TypeConverter, proto nám PropertyGrid nedokáže nabídnout její editaci.
Konverter
Napíšeme si tedy TypeConverter pro konverzi mezi řetězcem a Song. Konvence velí pojmenovávat konvertery podle datového typu, jež konvertují, pojmenujme tedy SongConverter. Konverter bude dědit z ExpandableObjectConverter a bude overridovat čtyři metody: CanConvertFrom, CanConvertTo, ConvertFrom a ConvertTo.
Public Class SongConverter
Inherits ExpandableObjectConverter
' CanConvertFrom, CanConvertTo, ConvertFrom, ConvertTo
End Class
CanConvertFrom, CanConvertTo
CanConvertFrom je funkce, jež udává, zda umí konverter konvertovat z daného datového typu. Ta naše bude vypadat takto:
Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
If sourceType Is GetType(String) Then
Return True
End If
Return MyBase.CanConvertFrom(context, sourceType)
End Function
CanConvertTo udává, pokud umí konvertor konvertovat na daný typ. PropertyGrid bude zajímat, pokud umí konvertovat na String.
Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
If destinationType Is GetType(String) Then
Return True
End If
Return MyBase.CanConvertTo(context, destinationType)
End Function
ConvertFrom, ConvertTo
ConvertFrom je funkce, která vrací výsledný objekt, který TypeConverter konvertuje. PropertyGrid bude potřebovat, aby konverter uměl konvertovat ze Stringu.
Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
If TypeOf (value) Is String Then
Dim str As String = DirectCast(value, String)
Dim rex As String = "^([^-\(\)]+) - ([^-\(\)]+) \(([^-\(\)]+)\)$"
' Za RegEx děkuji panu Jechovi. Více o RegularExpressions na konci článku.
If Regex.IsMatch(str, rex) Then
With Regex.Match(str, rex)
Return New Song(.Groups(1).Value, .Groups(2).Value, .Groups(3).Value)
End With
Else
Throw New ArgumentException(str & " don't match '" & rex & "'")
Return Nothing
End If
End If
Return MyBase.ConvertFrom(context, culture, value)
End Function
ConvertTo převádí obráceně - z objektu na řetězec (v případě PropertyGrid):
Public Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As Object
If destinationType Is GetType(String) AndAlso value IsNot Nothing Then
Dim sng As Song = DirectCast(value, Song)
Return String.Format("{0} - {1} ({2})", sng.Artist, sng.Title, sng.Album)
End If
Return MyBase.ConvertTo(context, culture, value, destinationType)
End Function
Jak vidíme, TypeConverter odvedl svou práci. Pokud změníme hodnotu vlastnosti Song na jiný řetězec ve správném formátu (Interpet - Náze Skladby (Název Alba)), aktualizují se i vnořené vlastnosti. Vlastnost je také možno rozkliknout ().
Jediný problém je, že pokud aktualizujeme nějakou subvlastnost, vlastnost Song se neaktualizuje okamžitě, ale až při editací jí samotné. Toto chování je možné snadno upravit. Stačí všem vlastnostem třídy Song přidat atribut RefreshProperties. Ten si jako parametr bere hodnotu enumerace System.ComponentModel.RefreshProperties. Jejími členy jsou None (výchozí chování), Repaint a All. Nám postačí Repaint. Ten PropertyGridu řekne, že se má překreslit. All by znamenalo, že se mají vlastnosti znovu zjistit a pak překreslit, ale to je v tomto případě zbytečné.
<RefreshProperties(RefreshProperties.Repaint)> _
Závěrem
Jak jsem slíbil v článku, řeknu také něco k RegularExpressions. Jedná se o pokročilou techniku práce s řetězci, zvlášťě pak vyhledávání a nahrazování určitých částí na základě daných pravidel. Více o RE n následující stránkách:
- regularnivyrazy.info (externí)
- regular-expressions.info (externí)
- regexlib.com (externí)
Doufám že se Vám článek líbil. Příště si ukážeme práci s UITypeEditory. O co se jedná napoví obrázek:
Zdrojové kódy