UITypeEditory

3. díl - UITypeEditory

Jan Novák       09.02.2009       VB.NET, Komponenty, WinForms       12201 zobrazení

V tomto článku si vysvětlíme používání DropDownEditorů. Po atributech a TypeConverterech se tak dostáváme k pokročilejší práci s komponentami na Design Time úrovni.

UITypeEditory jsou editory, jež k editaci vlastnosti v PropertyGridu používají ovládací prvek. Needituje je tedy přímo PropertyGrid (po vizuální stránce).

Jsou dva typy UITypeEditorů. Oba vycházejí ze stejného základu, liší se pouze grafickým zobrazením.

Ukázka DropDownEditoru - editor vlastnosti Dock

DropDownEditory zobrazují editační ovládací prvek jako nabídku pod vlastností.

Ukázka modálního editoru - dialog pro výběr písma

Modalní editory zobrazují editační ovládací prvek v dialogovém okně.

N.B.: Vlastnost může rozšiřovat TypeConverter i UITypeEditor zároveň. Ukázkou budiž vlastnost Font jakéhokoliv ovládacího prvku. Vidíme, že je možno ji expandovat a stejně tak i otevřít dialog pro výběr písma.

Teď už víme, k čemu editory slouží. Nudná teorie je za námi, teď si prakticky ukážeme, jak je využít. Budeme vycházet z komponenty Person. Stavme tedy na projektu z minulého dílu.

Zdrojové kódy

Ke komponentě přidáme vlastnost Mood (DisplayName: Aktuální nálada). Ta nabývá hodnoty vlastního datového typu Mood. Přidejte si její deklaraci níže do našeho projektu ke komponentě Person.

Private _mood As Mood
<Category("Actual"), DisplayName("Aktuální nálada"), Editor(GetType(MoodEditor), GetType(UITypeEditor))> _
Public Property Mood() As Mood
    Get
        Return _mood
    End Get
    Set(ByVal value As Mood)
        _mood = value
    End Set
End Property

Visual Studio nám podtrhne několik věcí. Jako první to bude MoodEditor v atributu Editor. Tento atribut udává, který UITypeEditor se má použít k editaci komponenty. Tím je právě MoodEditor. Popíšeme si jej dále. Druhá podrtžená věc bude Mood, jakožto datový typ vlastnosti Mood. Vytvoříme si do projektu novou třídu jménem Mood (Pravý klik na jméno projektu v Solution Exploreru > Add > New Item > Class > Mood.vb). Vytvořenou kostru třídy upravíme do této podoby:

Public Class Mood

    Private _mood As String = String.Empty
    Private _hint As String = String.Empty

    Public Sub New(ByVal mood As String, ByVal hint As String)
        _mood = mood
        _hint = hint
    End Sub

    <RefreshProperties(RefreshProperties.Repaint)> _
    Public Property Mood() As String
        Get
            Return _mood
        End Get
        Set(ByVal value As String)
            _mood = value
        End Set
    End Property
    <RefreshProperties(RefreshProperties.Repaint)> _
    Public Property Hint() As String
        Get
            Return _hint
        End Get
        Set(ByVal value As String)
            _hint = value
        End Set
    End Property

End Class

Nyní máme vytvořenou třídu Mood. Zakomentujte nebo smažte podtržené atributy a spusťte projekt. V PropertyGridu nelze vlastnost editovat. S tím jsme se setkali i u vlastnosti Song v minulém díle. Vytvoříme si tedy pro Mood TypeConverter. (Umístíme do stejného souboru)

Nezapomeneme k třídě Mood přidat atribut TypeConverter.

<TypeConverter(GetType(MoodClassConverter))> _

Nyní už samotný konverter:

Public Class MoodClassConverter
    Inherits ExpandableObjectConverter

    Public Overrides Function CanConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal sourceType As System.Type) As Boolean
        Return sourceType Is GetType(String) OrElse MyBase.CanConvertFrom(context, sourceType)
    End Function

    Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
        Return destinationType Is GetType(String) OrElse MyBase.CanConvertTo(context, destinationType)
    End Function

Než budeme overridovat další metody, zastavíme se. Funkce ConvertFrom zajišťuje převod z řetězce na objekt, zde na třídu Mood. Proč se nad tím ale pozastavuji? Třída Mood má dvě vlastnosti: Mood a Hint. Druhá vlastnost je prostá - libovolný text rozšiřující a popisující aktuální náladu. První vlastnost je nálada samotá. Ta nemůže to být libovolný řetězec. Musí se jednat o jeden z několika předem daných. Ve funkci ConvertFrom musí tedy proběhnout kontrola, zda nálada skutečně odpovídá nějakému ze sady daných řetězců. K těm se bude přistupovat poměrně z velkého počtu dalších míst v kódu. Umístíme je tedy do samotné třídy Mood. Přidáme do ní:

Shared ReadOnly Property Moods() As StandardValuesCollection
    Get
        Return New StandardValuesCollection(New String() {"Euphoria", "Happy", "Glad", "Normal", "Sad", "Confused", "Depression", "Angry"})
    End Get
End Property

Shared určuje, že se k vlastnosti dá přistupovat bez vytvoření instance třídy. Občas totiž budeme potřebovat zjistit jen zmíněnou kolekci a žádnou další funkcionalitu třídy. Možná se podivíte nad typem kolekce - StandardValuesCollection. Ten takový musí být. Dále si řekneme, proč.

    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 = "^(?[\w]+)(:(?.+))?$" ' Masce vyhoví výrazy "Nálada:Popis" i "Nálada"
            With Regex.Match(str, rex)
                If .Success Then
                    ' Pokud převáděný text vyhoví, zpracujeme jej.
                    ' Mood.Moods je přístup ke kolekci možných nálad bez vytvoření instance třídy Mood - díky Shared.
                    Dim matches = (From m As String In Mood.Moods Where m.ToLower() = .Groups("mood").Value.ToLower() Select m)
                    ' Příkaz v závorkách je LINQ příkaz. Říká, že se má projít kolekce Mood.Moods a položku, která odpovídá náladě zadané uživatelem, připojit k výsledku.
                    ' Výsledek je kolekce matches. Její počet položek bude 0, pokud je zadaná nesprávná hodnota, nebo 1, pak se funkce ukončí vrácením nové třídy Mood.
                    If matches.Count > 0 Then Return New Mood(matches(0), .Groups("hint").Value)
                ElseIf String.IsNullOrEmpty(str) Then
                    ' Pokud se jedná o prázdný řetězec, vrátíme Nothing - vlastnost nemá žádnou hodnotu.
                    Return Nothing
                End If
            End With
            ' Pokud řetězec není prázdný, ale nevyhověl, není ve správném formátu.
            Throw New ArgumentException("Entered values must match mask 'Mood:Hint' and Mood must be one of allowed values (see Mood.Moods).")
        Else
            Return MyBase.ConvertFrom(context, culture, value)
        End If
    End Function

    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) Then
            Dim mood As Mood = DirectCast(value, Mood)
            If mood IsNot Nothing Then
                ' Vrátí formát "Nálada:Popis", pokud popis není prázdný, a pokud je, vrátí "Nálada".
                Return String.Format("{0}{1}{2}", mood.Mood.ToString(), IIf(String.IsNullOrEmpty(mood.Hint), String.Empty, ":"), mood.Hint)
            Else
                Return String.Empty
            End If
        Else
            Return MyBase.ConvertTo(context, culture, value, destinationType)
        End If
    End Function

End Class

Nyní jsme dosáhli toho, co bychom měli umět již z minulého dílu. Vlastnost Mood je možné rozkliknout. Zkusíme tak, že spustíme a do Mood napíšeme například "Normal". Tím se vlastnost naplní nově vytvořenou třídou Mood s vlastností Mood="Normal" a Hint prázdnou. TypeConverted funguje. Pokud máte položku stále zašedlou, ujistěte se, že jste opravdu přidali třídě Mood atribut TypeConverter.

Ještě než se pustíme do psaní UITypeEditoru pro třídu Mood, ukážeme si, jak zajistit, aby bylo v PropertyGridu možné zadávat do vlastnosti Mood u třídy Mood jen řetězce, které jsou povolené. Použijeme TypeConverter. Konkrétně StringConverter.

K vlastnost Mood u třídy Mood přidáme atribut TypeConverter.

<RefreshProperties(RefreshProperties.Repaint), TypeConverter(GetType(MoodPropertyConverter))> _

Teď se pustíme do psaní konverteru. Bude to poněkud netradiční konverter. Bude overridovat jiné metody, než na které jsme zvyklí.

Public Class MoodPropertyConverter
    Inherits StringConverter

    Public Overrides Function GetStandardValuesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
        Return True ' Říká, že máme vlastní kolekci hodnot, které chceme používat
    End Function

    Public Overrides Function GetStandardValuesExclusive(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
        Return True ' A zakazuje, aby uživatel mohl do pole psát. Musí si jednoduše jednu z možností vybrat.
    End Function

    Public Overrides Function GetStandardValues(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.ComponentModel.TypeConverter.StandardValuesCollection
        Return Mood.Moods ' Vrací kolekci řetězců. Správně by měla být deklarována zde, v konverteru (proto ten krkolomný datový typ), ale pro přehlednost a praktičnost jsme jí umístil do třídy Mood.
    End Function

End Class

Nyní se dostáváme k hlavnímu tématu našeho článku. K UITypeEditorům. Konkrétně k DropDownEditoru, který si vytvoříme k editaci datového typu Mood.

Jako první musíme mít nějakou komponentu, která se bude zobrazovat. Přidejte do projektu UserControl jménem MoodView. Vypadat by měl asi takto:

Podoba komponenty pro editaci nálady. Je na ní TrackBar, pod ním PictureBox a Label a pod nimi TextBox.

Doporučuji přepsat Designer Vaší komponenty, abychom pracovali všichni na stejném základě.

MoodView.Designer.vb: (pokud tento soubor ve své Solution hledáte marně, klikněte na ikonu s popisem 'Show All Files' v Solution Exploreru).

<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class MoodView
    Inherits System.Windows.Forms.UserControl

    'UserControl overrides dispose to clean up the component list.
    <System.Diagnostics.DebuggerNonUserCode()> _
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            If disposing AndAlso components IsNot Nothing Then
                components.Dispose()
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
     _
    Private Sub InitializeComponent()
        Me.components = New System.ComponentModel.Container
        Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MoodView))
        Me.TrackBar1 = New System.Windows.Forms.TrackBar
        Me.PictureBox1 = New System.Windows.Forms.PictureBox
        Me.Label1 = New System.Windows.Forms.Label
        Me.TextBox1 = New System.Windows.Forms.TextBox
        Me.ImageList1 = New System.Windows.Forms.ImageList(Me.components)
        CType(Me.TrackBar1, System.ComponentModel.ISupportInitialize).BeginInit()
        CType(Me.PictureBox1, System.ComponentModel.ISupportInitialize).BeginInit()
        Me.SuspendLayout()
        '
        'TrackBar1
        '
        Me.TrackBar1.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.TrackBar1.AutoSize = False
        Me.TrackBar1.Location = New System.Drawing.Point(3, 3)
        Me.TrackBar1.Maximum = 7
        Me.TrackBar1.Name = "TrackBar1"
        Me.TrackBar1.Size = New System.Drawing.Size(144, 30)
        Me.TrackBar1.TabIndex = 0
        '
        'PictureBox1
        '
        Me.PictureBox1.Location = New System.Drawing.Point(3, 39)
        Me.PictureBox1.Name = "PictureBox1"
        Me.PictureBox1.Size = New System.Drawing.Size(51, 27)
        Me.PictureBox1.TabIndex = 1
        Me.PictureBox1.TabStop = False
        '
        'Label1
        '
        Me.Label1.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.Label1.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(238, Byte))
        Me.Label1.Location = New System.Drawing.Point(60, 39)
        Me.Label1.Name = "Label1"
        Me.Label1.Size = New System.Drawing.Size(87, 27)
        Me.Label1.TabIndex = 2
        Me.Label1.Text = "Normal"
        Me.Label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
        '
        'TextBox1
        '
        Me.TextBox1.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
                    Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.TextBox1.Location = New System.Drawing.Point(3, 69)
        Me.TextBox1.Multiline = True
        Me.TextBox1.Name = "TextBox1"
        Me.TextBox1.Size = New System.Drawing.Size(144, 40)
        Me.TextBox1.TabIndex = 3
        '
        'ImageList1
        '

        '
        'MoodView
        '
        Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
        Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
        Me.Controls.Add(Me.TextBox1)
        Me.Controls.Add(Me.Label1)
        Me.Controls.Add(Me.PictureBox1)
        Me.Controls.Add(Me.TrackBar1)
        Me.Name = "MoodView"
        Me.Size = New System.Drawing.Size(150, 112)
        CType(Me.TrackBar1, System.ComponentModel.ISupportInitialize).EndInit()
        CType(Me.PictureBox1, System.ComponentModel.ISupportInitialize).EndInit()
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub
    Friend WithEvents TrackBar1 As System.Windows.Forms.TrackBar
    Friend WithEvents PictureBox1 As System.Windows.Forms.PictureBox
    Friend WithEvents Label1 As System.Windows.Forms.Label
    Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
    Friend WithEvents ImageList1 As System.Windows.Forms.ImageList

End Class

Otevřete Designer komponenty a ve vlastnostech komponenty ImageList1 zvolte vlastnost Images. Nahrajte následující obrázky (ve správném pořadí!):

Obrazové podklady

Dále by bylo vhodné nastavit u ImageList1 vlastnosti ColorDepth na 32 bitů a ImageSize na [51; 27]. Teď je naše komponenta graficky připravena. Přeneseme se do kódu a zajistíme funkcionalitu.

Public Class MoodView

    ' Do konstruktoru předáme editovaný objekt.
    Public Sub New(ByVal mood As Mood)
        InitializeComponent() ' Připravíme všechny komponenty.
        If mood IsNot Nothing Then ' Pokud editovaný objekt není prázdný, přizpůsobíme komponentu podle jeho vlastností.
            TrackBar1.Value = mood.Index ' Ohlásí chybu, jak ji napravit si ukážeme dále.
            TextBox1.Text = mood.Hint
            TrackBar1_Scroll()
        Else ' V opačném případě nastavíme komponentu do výchozího stavu.
            TrackBar1.Value = 3
            TrackBar1_Scroll()
        End If
    End Sub

    ' Přes tuto vlastnost se pak budeme dotazovat na změněný objekt.
    Public ReadOnly Property Mood() As Mood
        Get
            Return New Mood(Mood.Moods(Me.TrackBar1.Value), Me.TextBox1.Text)
        End Get
    End Property
    ' Tato vlastnost vrátí obrázek náležící k dané vlastnosti.
    Shared ReadOnly Property Icon(ByVal index As Integer) As Image
        Get
            Return New MoodView(Nothing).ImageList1.Images(index)
        End Get
    End Property

    Private Sub TrackBar1_Scroll() Handles TrackBar1.Scroll
        PictureBox1.Image = ImageList1.Images(Me.TrackBar1.Value)
        Label1.Text = Mood.Moods(Me.TrackBar1.Value)
    End Sub
End Class

Komponenta je hotová, ale ještě jí nemůžeme vyzkoušet. Musíme nejdřív vytvořit Editor, který ji bude hostovat.

Vrátíme se do souboru Mood.vb a přidáme novou třídu:

Public Class MoodEditor
    Inherits UITypeEditor

    Public Overrides Function GetEditStyle(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.Drawing.Design.UITypeEditorEditStyle
        Return UITypeEditorEditStyle.DropDown ' Určuje, že se má zobrazit nabídka, nikoliv dialog.
    End Function

    Public Overrides Function EditValue(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
        Using editor As MoodView = New MoodView(DirectCast(value, Mood)) ' Vytvoříme instanci ovládacího prvku MoodView a předáme jí editovaný objekt.
            ' Vytvoříme providera, který ovládací prvek zobrazí a bude čekat, dokud se nad ovládacím prvkem nestiskneme Enter.
            DirectCast(provider.GetService(GetType(IWindowsFormsEditorService)), IWindowsFormsEditorService).DropDownControl(editor)
            Return editor.Mood ' Pomocí vlastnosti Mood získáme zpět pozměnený objekt a o zbytek (dosazení do vlastnosti) se postará UITypeEditor.
        End Using
    End Function

    Public Overrides Function GetPaintValueSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
        Return True ' Tato vlastnost určuje, že se má u vlastnosti vykreslit malý obdélníček s grafickou reprezentací její hodnoty (jako například u ForeColor.)
    End Function

Opět se zastavím uprostřed kódu. Následující metoda se stará o vykreslení obdélníčku. My do ní vykreslím emotikon, který vykreslujeme do PictureBoxu v MoodView. K obrázku se dostaneme přes vlastnost Icon u MoodView. Zjištění indexu zajistíme novou vlastností do třídy Mood.

<Browsable(False)> _
Public ReadOnly Property Index() As Integer
    Get
        For i As Integer = 0 To Moods.Count - 1
            If Moods(i) = _mood Then
                Return i
            End If
        Next
    End Get
End Property

Upravili jsme třídu Mood a pokračujeme v psaní editoru:

    Public Overrides Sub PaintValue(ByVal e As System.Drawing.Design.PaintValueEventArgs)
        If e.Value Is Nothing Then ' Pokud je hodnota vlastnosti nulová, vykreslíme červený kříž
            e.Graphics.DrawLine(Pens.Red, e.Bounds.Left, e.Bounds.Top, e.Bounds.Right - 1, e.Bounds.Bottom - 1)
            e.Graphics.DrawLine(Pens.Red, e.Bounds.Right - 1, e.Bounds.Top, e.Bounds.Left, e.Bounds.Bottom - 1)
        Else
            ' Pokud ne, přes vlastnost Icon ovládacího prvku MoodView získáme obrázek na indexu, který odpovídá indexu nálady v kolekci.
            e.Graphics.DrawImage(MoodView.Icon(DirectCast(e.Value, Mood).Index), e.Bounds)
        End If
    End Sub

    Public Overrides ReadOnly Property IsDropDownResizable() As Boolean
        Get
            Return False ' Nechceme, aby bylo možné ovládací prvek dimenzovat.
        End Get
    End Property

End Class

UITypeEditor je na světě. Spusťte aplikaci. Jak vidíte, nic se nezměnilo. Žádný DropDownEditor se neobjevil. Pokud jste četli pozorně, vzpomenete si, co jsme na začátku zakomentovali, aby nám to necházelo chyby. Ano, správně, některé atributy vlastnosti Mood třídy Person. Jak tedy vypadá kompletní sada jejich atributů?

<Category("Actual"), DisplayName("Aktuální nálada"), Editor(GetType(MoodEditor), GetType(UITypeEditor))> _
Public Property Mood() As Mood
    ...

Teď by mělo vše pracovat tak, jak má. Pokud tomu tak není, neváhejte se na mne obrátit v diskusi.

Zdrojové kódy

Závěrem

Pokud byste se chtěli dozvědět, jak zobrazit místo DropDownEditoru modální dialog, vězte, že je to velmi jednoduché. Stačí ve třídě MoodEditor, metodě GetEditStyle nastavit Modal a ve funkci EditValue volat na provideru metodu ShowDialog. Samozřejmě místo ovládacího prvku si musíme vytvořit formulář.

To je pro dnešní díl vše. Příště si povíme něco o tom, jak vylepšit práci s ovládacími prvky přímo v Designeru.

 

hodnocení článku

4 bodů / 4 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Všechny díly tohoto seriálu

3. UITypeEditory 09.02.2009
2. TypeConvertery 28.12.2008
1. Atributy jmenného prostoru System.ComponentModel 23.12.2008

 

Mohlo by vás také zajímat

Řešené příklady v ASP.NET - díl 1.: Aplikace pro zamlouvání sedadel (část 1)

V této části vytvoříme databázi, napíšeme základní infrastrukturu a nakonfigurujeme přihlašování uživatelů pomocí knihovny Altairis Web Security.

Windows Presentation Foundation (WPF) - díl 5.: Device independent pixels

Co je to DPI a proč by vás to mělo zajímat? Jak se aplikují automatické transformace při změně této hodnoty? Článek je prvním pohledem tohoto seriálu do transformací a pozičního systému WPF.

Windows Presentation Foundation (WPF) - díl 6.: Základy pozicování

Pozicování je velká přednost technologie WPF. Dovoluje připravovat dynamické rozložení prvků s předvídatelným chováním při změně nejen velikosti okna, ale i elementů uvnitř něj. V tomto díle se věnuji základním principům pozicování.

 

 

Nový příspěvek

 

Diskuse: UITypeEditory

Předem chci poděkovat za velice inspirativní článek. Několikrát jsem ho již četl a využil.

Rád bych tímto dolpnil malý nedostatek.

Při Rebuildu projektu se z komponenty Person ztratí nastavení property Mood.

Designer neumí pracovat s třídou jako vlastností komponenty, pokud nemá definovaný konstruktor bez parametrů.

Je tedy potřeba dolpnit konstruktor bez parametrů do třídy Mood:

	Public Sub New()
		_mood = "Normal"
		_hint = String.Empty
	End Sub

designer pak umí automaticky uložit nastavení v designeru formuláře Form1.Designer.vb

		Dim Mood1 As HowToWriteComponents_Part3.Mood = New HowToWriteComponents_Part3.Mood



		'Person1
		'
		Me.Person1.Birth = New Date(CType(0, Long))
		Me.Person1.Mail = Nothing
		Mood1.Hint = ""
		Mood1.Mood = "Confused"
		Me.Person1.Mood = Mood1
		Me.Person1.Name = "Person1"
		Me.Person1.Song = Nothing

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: UITypeEditory

Předem chci poděkovat za velice inspirativní článek. Několikrát jsem ho již četl a využil.

Rád bych tímto dolpnil malý nedostatek.

Při Rebuildu projektu se z komponenty Person ztratí nastavení property Mood.

Designer neumí pracovat s třídou jako vlastností komponenty, pokud nemá definovaný konstruktor bez parametrů.

Je tedy potřeba dolpnit konstruktor bez parametrů do třídy Mood:

	Public Sub New()
		_mood = "Normal"
		_hint = String.Empty
	End Sub

designer pak umí automaticky uložit nastavení v designeru formuláře Form1.Designer.vb

		Dim Mood1 As HowToWriteComponents_Part3.Mood = New HowToWriteComponents_Part3.Mood



		'Person1
		'
		Me.Person1.Birth = New Date(CType(0, Long))
		Me.Person1.Mail = Nothing
		Mood1.Hint = ""
		Mood1.Mood = "Confused"
		Me.Person1.Mood = Mood1
		Me.Person1.Name = "Person1"
		Me.Person1.Song = Nothing

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: UITypeEditory

Dobry den,

nevite nahodou jak se dostat uvnitr kodu komponenty do jakehosi "kontextu". (Potrebuji zkusit pouzit Converter na vygenerovani objektu (ktery je internal - (Zkousim pouzit vestaveny UI-editor pro data binding) a jeho metoda potrebuje takovyto kontext)

Moc se v tom tematu kolem designu a komponentnim modelu jeste nevyznam, ale snad je mi rozumet... diky predem,

Mojmir.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Zdravím, kontext je předáván jako první parametr přetíženým metodám editoru. Tedy např. takto:

    Public Overrides Function GetEditStyle(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.Drawing.Design.UITypeEditorEditStyle
        ' viz. parametr context
    End Function

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Diky za informaci, nevedel jsem o tom. Zatim mi to ale nestaci. Potreboval bych ziskat nejaky "spravny" kontext v metode uvnitr implementace komponenty a ne jejiho editoru.

Jinak pred chvili jsem se dostal k jedne kuse informaci, jak se dostat na k Designer hostu, mozna je to cesta, ale nevim ...

IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost));

(omlouvam se ale jsem zvykly na C#)

Takze stale mi vrta hlavou: Co vlastne ten "kontext" predstavuje a je mozne nejak ziskat jeho instanci v metode komponenty?

Diky, Mojmir.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

V prvé řadě se omlouvám za dlouhodobou nereakci, důvodem byl nepřístup k počítači. S Vaším problémem bohužel nehnu, takhle hluboko do vývoje komponent nevidím. Ve spoustě věcí nemám sám jasno a naslepo střílet nechci, tím bych Vám stejně nijak nepomohl. Můžu Vám tedy bohužel doporučit pouze proklepnutí Googlu.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

context představuje kopii aktuálně editované komponenty, ke které je pak možné přistupovat pomocí veřejných vlastností.

Nadávno jsem potřeboval vytvořit Button se stavy (každý stav definuje text, barvu textu a pozadí) a v designeru zabrazit vlastnost výběru stavu v režimu Combobox pro aktuální tlačítko. Sice dlouhá doba po dotazu, ale snad pomůže někomu jako inspirace.

#Region "Button State Component"

#Region "Button State Class"
<Designer(GetType(ButtonStateArrayDesigner))> _
Public Class ButtonState
	Inherits Button

#Region "Properties"

#Region "Text List For Selected State TypeConverter"
	Private _TextList() As Integer
	<Browsable(False)> _
	Public ReadOnly Property TextList() As Integer()
		Get
			Dim i As Integer

			ReDim Preserve _TextList(_StringArray.GetLength(0) - 1)
			For i = 0 To _StringArray.GetLength(0) - 1
				_TextList(i) = i
			Next
			'_ForeColorDict.Keys.CopyTo(_TextList, 0)
			Return _TextList
		End Get
	End Property
#End Region

#Region "State Text"
	Private _StringArray(-1) As String
	<Category("Button States"), DisplayName("Text")> _
	Public Property State_StringArray() As String()
		Get
			Return _StringArray
		End Get
		Set(ByVal value As String())
			_StringArray = value
		End Set
	End Property
#End Region

#Region "State Fore Color"
	Private _FColorArray(-1) As Color
	<Category("Button States"), DisplayName("Fore Colors")> _
	Public Property State_ForeColorArray() As Color()
		Get
			Return _FColorArray
		End Get
		Set(ByVal value As Color())
			_FColorArray = value
			'ReDim Preserve _TextList(_ForeColorDict.Count - 1)
			'_ForeColorDict.Keys.CopyTo(_TextList, 0)
		End Set
	End Property
#End Region

#Region "State Back Color"
	Private _BColorArray(-1) As Color
	<Category("Button States"), DisplayName("Back Colors")> _
	Public Property State_BackColorArray() As Color()
		Get
			Return _BColorArray
		End Get
		Set(ByVal value As Color())
			_BColorArray = value
			'ReDim Preserve _TextList(_ForeColorDict.Count - 1)
			'_ForeColorDict.Keys.CopyTo(_TextList, 0)
		End Set
	End Property
#End Region

#Region "State Selected"
	Private _StateSelected As Integer = -1
	<Category("Appearance"), DisplayName("State Selected"), TypeConverter(GetType(StateSelectedConverter))> _
	Property StateSelected() As String
		Get
			Return CStr(_StateSelected)
		End Get
		Set(ByVal value As String)
			If (_FColorArray.GetLength(0) > 0) Then
				_StateSelected = CInt(value)
				'ForeColor = _ForeColorDict.Item(_StateSelected)
				ForeColor = _FColorArray(_StateSelected)
				'BackColor = _BackColorDict.Item(_StateSelected)
				BackColor = _BColorArray(_StateSelected)
				Text = _StringArray(_StateSelected)
			End If
		End Set
	End Property
#End Region

#End Region

End Class
#End Region

#Region "State Selected Converter"
Public Class StateSelectedConverter
	Inherits StringConverter

	Public Overrides Function GetStandardValuesSupported(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
		Return True	' Říká, že máme vlastní kolekci hodnot, které chceme používat
	End Function

	Public Overrides Function GetStandardValuesExclusive(ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
		Return True	' A zakazuje, aby uživatel mohl do pole psát. Musí si jednoduše jednu z možností vybrat.
	End Function

	Public Overrides Function GetStandardValues(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.ComponentModel.TypeConverter.StandardValuesCollection
		Dim VarStates As ButtonState = DirectCast(context.Instance, ButtonState)
		Return New StandardValuesCollection(VarStates.TextList)	' Vrací kolekci řetězců. Správně by měla být deklarována zde, v konverteru (proto ten krkolomný datový typ), ale pro přehlednost a praktičnost jsme jí umístil do třídy Mood.
	End Function
End Class
#End Region

#Region "Button State Designer"
Friend Class ButtonStateArrayDesigner
	Inherits Windows.Forms.Design.ControlDesigner

	Private m_Verbs As DesignerVerbCollection

	' DesignerVerbCollection is overridden from ComponentDesigner
	Public Overrides ReadOnly Property Verbs() As DesignerVerbCollection
		Get
			If m_Verbs Is Nothing Then
				' Create and initialize the collection of verbs
				m_Verbs = New DesignerVerbCollection()
				m_Verbs.Add(New DesignerVerb("State Add", AddressOf OnStateAdd))
				m_Verbs.Add(New DesignerVerb("State Del", AddressOf OnStateDel))
			End If
			Return m_Verbs
		End Get
	End Property

	Sub New()
	End Sub	'New

	Private Sub SetVerbs()

		Dim VarControl As ButtonState = DirectCast(Control, ButtonState)
		Dim Length As Integer = VarControl.State_ForeColorArray.GetLength(0)
		Dim Index As Integer = CInt(VarControl.StateSelected)
		Select Case Length
			Case 0
				Verbs(1).Enabled = False
				Verbs(2).Enabled = False
			Case 1
				Verbs(1).Enabled = False
				Verbs(2).Enabled = True
			Case Else
				Verbs(1).Enabled = True
				Verbs(2).Enabled = True
		End Select

	End Sub

	Private Sub OnStateAdd(ByVal sender As Object, ByVal args As EventArgs)
		Dim i, ArrayLength, ArrayIndex As Integer

		Dim VarControl As ButtonState = DirectCast(Control, ButtonState)
		Dim VarFColorArray As Color() = VarControl.State_ForeColorArray
		Dim VarBColorArray As Color() = VarControl.State_BackColorArray
		Dim VarStringArray As String() = VarControl.State_StringArray
		Dim NewFColorArray As Color()
		Dim NewBColorArray As Color()
		Dim NewStringArray As String()

		ArrayIndex = CInt(VarControl.StateSelected)
		ArrayLength = VarFColorArray.GetLength(0)

		'Change Array length 
		ReDim Preserve NewFColorArray(ArrayLength)
		ReDim Preserve NewBColorArray(ArrayLength)
		ReDim Preserve NewStringArray(ArrayLength)
		'Copy From Actual To New Arrays

		For i = 0 To ArrayLength - 1
			NewFColorArray(i) = VarFColorArray(i)
			NewBColorArray(i) = VarBColorArray(i)
			NewStringArray(i) = VarStringArray(i)
		Next
		'Setting Last New Items
		NewFColorArray(ArrayLength) = VarControl.ForeColor
		NewBColorArray(ArrayLength) = VarControl.BackColor
		NewStringArray(ArrayLength) = VarControl.Text
		'Set New Arrays to Control
		VarControl.State_ForeColorArray = NewFColorArray
		VarControl.State_BackColorArray = NewBColorArray
		VarControl.State_StringArray = NewStringArray
		VarControl.StateSelected = CStr(ArrayLength)
		'System.Windows.Forms.MessageBox.Show("The first designer verb was invoked.")
	End Sub	'OnAddState

	Private Sub OnStateDel(ByVal sender As Object, ByVal args As EventArgs)
		Dim i, ArrayLength, ArrayIndex As Integer
		Dim VarControl As ButtonState = DirectCast(Control, ButtonState)
		Dim VarFColorArray As Color() = VarControl.State_ForeColorArray
		Dim VarBColorArray As Color() = VarControl.State_BackColorArray
		Dim VarStringArray As String() = VarControl.State_StringArray
		Dim NewFColorArray As Color()
		Dim NewBColorArray As Color()
		Dim NewStringArray As String()

		ArrayIndex = CInt(VarControl.StateSelected)
		If ArrayIndex >= 0 Then

			ArrayLength = VarFColorArray.GetLength(0)

			'Change Array length 
			ReDim Preserve NewFColorArray(ArrayLength - 2)
			ReDim Preserve NewBColorArray(ArrayLength - 2)
			ReDim Preserve NewStringArray(ArrayLength - 2)
			'Copy From Actual To New Arrays

			For i = 0 To ArrayIndex - 1
				NewFColorArray(i) = VarFColorArray(i)
				NewBColorArray(i) = VarBColorArray(i)
				NewStringArray(i) = VarStringArray(i)
			Next

			For i = ArrayIndex + 1 To ArrayLength - 1
				NewFColorArray(i - 1) = VarFColorArray(i)
				NewBColorArray(i - 1) = VarBColorArray(i)
				NewStringArray(i - 1) = VarStringArray(i)
			Next


			'Set New Arrays to Control
			VarControl.State_ForeColorArray = NewFColorArray
			VarControl.State_BackColorArray = NewBColorArray
			VarControl.State_StringArray = NewStringArray
			VarControl.StateSelected = CStr(0I)
		End If

		'System.Windows.Forms.MessageBox.Show("The first designer verb was invoked.")
	End Sub	'OnAddState

End Class	'MyDesigner
#End Region


#End Region

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Diskuse: UITypeEditory

Nevíte někdo jak přepsat vnitřní metodu, která generuje defaultní ikonu každému tlačítku v toolbaru v design modu.

Díky

Jarda

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: UITypeEditory

<marquee>www.cikcik.tk</marquee>

nahlásit spamnahlásit spam -1 / 1 odpovědětodpovědět

Diskuse: UITypeEditory

Super článek, děkuju. Je dobře, že se někdo pustil do tak opomíjeného tématu jako jsou designéry.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Díky za pochvalu. Návrh uživatelských rozhraní a ovládacích prvků mě velmi baví, a rád se o své skromné znalosti podělím.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback