|
HI, sorry I got it work.
VS 2005 give me this message:
Could not find type 'SettingsDialog.FireFoxBar'. Please make sure that the assembly that contains this type is referenced. If this type is a part of your development project, make sure that the project has been successfully built.
But it works anywy.
Thanks
Massimo
|
|
|
|
|
Glad you were able to get it working.
|
|
|
|
|
Hello,
is there a translation to c# available? I think there are a few more difference than just to translate the code. Setting values are not autoatically saved or put to controls and so on.
Thanks for your help
Thomas
|
|
|
|
|
I will definately look into that. As my C# isn't that strong it could take some time.
|
|
|
|
|
you can convert VB to C# with some software,
but those software need you change one VB project's setting
In project setting:
complie page: configuring: Option strict
change it from "Off" to "ON"
..............................
This need VB program writed in explit "type" mode.
|
|
|
|
|
Hi, thank you for the great example.
I want use it for the configuration of SerialPort, so I added the support of enum types.
There was an problem by calling "frmOptions.Showdialog" more than once:
Entries from last calls are not delete. So I cleared Settings and the tabPages bevore adding new.
Here is the modified code from frmOptions.vb
Public Class frmOptions
Private Settings As New SettingInfoCollection
Private _bUseTabPages As Boolean = True
''' <summary>
''' Determines whether the dialog box will use TabPages or a TreeView to display the categories.
''' </summary>
''' <value>Boolean: True - Use TabPages; False - Use TreeView</value>
''' <returns>Boolean: True - Use TabPages; False - Use TreeView</returns>
Public Property UseTabPages() As Boolean
Get
Return _bUseTabPages
End Get
Set(ByVal value As Boolean)
_bUseTabPages = value
End Set
End Property
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'##### remove old entries from last calls
Settings.Clear()
'Load the SaveOnExit value
chkSaveOnExit.Checked = My.Application.SaveMySettingsOnExit
'Setup the Setting property object for grabing all the settings
Dim sp As System.Configuration.SettingsProperty = Nothing
'Cycle through each setting and add it if appropriate.
For Each sp In My.Settings.Properties
'If the name doesn't have an underscore - we can't assign a category so we can't change it.
'If the setting is ApplicationScoped - we aren't able to change it at runtime.
'Check also if there is support on this form for the System.Type the setting is.
If sp.Name.IndexOf("_") <> -1 AndAlso IsUserScope(sp) AndAlso IsAllowedType(sp) Then
'Passed the tests create a new SettingInfo object
Dim newSetting As New SettingInfo
'Load the settings data into the object
newSetting.LoadData(sp.Name)
'Add the object to the collection
Settings.Add(newSetting)
End If
Next
'Sort the settings by category - makes the TreeView look nice
quickSort(Settings)
'If we're using tabpages hide the TreeView and main TableLayoutPanel
If _bUseTabPages Then
'Hide the TreeView mode settings controls
tvCategories.Visible = False
tlp.Visible = False
'Show the TabControl
tcMain.Visible = True
tcMain.TabIndex = 0
'Load the settings into the TabControl
LoadTabControl()
Else
'Hide the TabControl
tcMain.Visible = False
tvCategories.Visible = True
tvCategories.TabIndex = 0
tlp.Visible = True
'Load the settings into the TreeView
LoadTreeView()
End If
End Sub
#Region " Sorting Functions "
''' <summary>
''' Bootstrap starts the quick sort method of sorting a SettingInfoCollection. Sorts alphabetically by category.
''' </summary>
''' <param name="col">The SettingInfoCollection you wish to sort.</param>
Private Sub quickSort(ByRef col As SettingInfoCollection)
'Call the actual sorting method
SortIt(col, 0, col.Count - 1)
End Sub
''' <summary>
''' QuickSort subroutine that does the sorting for a SettingInfoCollection.
''' </summary>
''' <param name="SortArray">The SettingInfoCollection to sort.</param>
''' <param name="First">The starting element of the selection you wish to sort (usually 0).</param>
''' <param name="Last">Then final element of the selection you wish to sort (usually the Count - 1 of the collection).</param>
''' <remarks>Called by the quickSort subroutine</remarks>
Private Sub SortIt(ByRef SortArray As SettingInfoCollection, ByVal First As Long, ByVal Last As Long)
'Copied and modified code to support sorting of the SettingInfoCollection
'using strings
Dim Low As Long, High As Long
Dim Temp As SettingInfo = Nothing
Dim List_Separator As SettingInfo = Nothing
Low = First
High = Last
List_Separator = SortArray((First + Last) / 2).Clone
Do
Do While (SortArray(Low).Category < List_Separator.Category)
Low += 1
Loop
Do While (SortArray(High).Category > List_Separator.Category)
High -= 1
Loop
If (Low <= High) Then
Temp = SortArray(Low).Clone
SortArray.SetItem(Low, SortArray(High))
SortArray.SetItem(High, Temp)
Low += 1
High -= 1
End If
Loop While (Low <= High)
If (First < High) Then SortIt(SortArray, First, High)
If (Low < Last) Then SortIt(SortArray, Low, Last)
End Sub
#End Region
#Region " Loading Functions "
''' <summary>
''' Checks whether the dialog supports a given type for editing.
''' </summary>
''' <param name="se">A SettingsProperty object as found in the My namespace.</param>
''' <returns>Boolean: True editing is supported, false editing is not supported.</returns>
Private Function IsAllowedType(ByVal se As System.Configuration.SettingsProperty) As Boolean
'Build an array of allowed System.Types that this form supports.
Dim allowTypes() As Type = {GetType(Boolean), GetType(Byte), GetType(Char), GetType(Date), _
GetType(Decimal), GetType(Double), GetType(Integer), GetType(Long), _
GetType(SByte), GetType(Short), GetType(Single), GetType(String), _
GetType(Color), GetType(Font), _
GetType(UInteger), GetType(ULong), GetType(UShort)}
'If the type of the property is found in the array - the type is supported.
If Array.IndexOf(allowTypes, se.PropertyType) <> -1 Then Return True
'##### enum type
If (se.PropertyType.BaseType.Name = "Enum") Then Return True
'The type must not be supported if it got here
Return False
End Function
''' <summary>
''' Checks whether the Setting is writable or not.
''' </summary>
''' <param name="sp">A SettingsProperty as specified in the My namespace.</param>
''' <returns>Boolean: True setting is writable, false setting is read-only.</returns>
Private Function IsUserScope(ByVal sp As System.Configuration.SettingsProperty) As Boolean
'The scope of the setting is stored in the Attributes of the setting
'we must cycle through all settings and search for an ApplicationScopedSettingAttribute
'or a UserScopedSettingAttribute
For Each o As Object In sp.Attributes.Values
'If we find an ApplicationScopedSettingAttribute the setting isn't UserScoped so return false
If TypeOf (o) Is System.Configuration.ApplicationScopedSettingAttribute Then Return False
'If we find a UserScopedSettingAttribute then the setting is UserScoped so return true
If TypeOf (o) Is System.Configuration.UserScopedSettingAttribute Then Return True
Next
'If we didn't find either of them, it isn't UserScoped (and I'm not sure what it is) so return false
Return False
End Function
''' <summary>
''' Cycles through eact setting and makes sure the category is in the TreeView
''' </summary>
''' <remarks></remarks>
Private Sub LoadTreeView()
'If there are no settings - do not try loading the treeview
If Settings.Count = 0 Then Exit Sub
'Cycle through all of the loaded settings
For Each si As SettingInfo In Settings
'Add the categories to the root of the TreeView
AddCat(tvCategories.Nodes, si.Category.Split("."), 0)
Next
End Sub
''' <summary>
''' Adds categories/sub-categories to a TreeView.
''' </summary>
''' <param name="parent_nodes">The TreeNodeCollection to which you wish to add the category/sub-category</param>
''' <param name="fields">An array of categories/sub-categories. Index 0 is the Category everything else is a sub-category of the index before it.</param>
''' <param name="field_num">The index with which to the current TreeNodeCollection level. For a TreeNode.Nodes object this would most likely be 0.</param>
Private Sub AddCat(ByVal parent_nodes As TreeNodeCollection, ByVal fields() As String, ByVal field_num As Integer)
'If there were no fields passed - we can't add anything to the TreeView so exit the sub
'Also exit if we reached then end of the field list
If field_num > fields.GetUpperBound(0) Then Exit Sub
Dim found_field As Boolean
'Check each node of the parent to see if it category/sub-category already exists
For Each child_node As TreeNode In parent_nodes
If child_node.Text = fields(field_num) Then
'It exists so check this node for the next sub-category
AddCat(child_node.Nodes, fields, field_num + 1)
found_field = True
End If
Next child_node
'If we didn't find the node - then add it
If Not found_field Then
Dim new_node As TreeNode = parent_nodes.Add(fields(field_num))
'Check the next sub-category in the field list
AddCat(new_node.Nodes, fields, field_num + 1)
End If
End Sub
''' <summary>
''' Cycles through all the Main categories, creating a TabPage and loading the control into it.
''' </summary>
Private Sub LoadTabControl()
Dim cat As SettingCategoryCollection = Settings.GetCategories
'##### : remove Tabpages from last calls
tcMain.TabPages.Clear()
For Each str As SettingCategory In cat
Dim tp As New TabPage
tp.Text = str.MainCat
tp.Tag = str.SubCats
tp.UseVisualStyleBackColor = True
LoadTabPageContents(tp.Text, tp)
tcMain.TabPages.Add(tp)
Next
End Sub
''' <summary>
''' Loads the settings editing controls into a TabPage.
''' </summary>
''' <param name="cat">The Main category of the controls you wish to add.</param>
''' <param name="tp">A reference to the TabPage you wish to add the controls to.</param>
Private Sub LoadTabPageContents(ByVal cat As String, ByRef tp As TabPage)
'Create a new TableLayoutPanel to layout the controls for the settings
Dim ntlp As New TableLayoutPanel
ntlp.Name = cat
ntlp.Dock = DockStyle.Fill
ntlp.ColumnCount = 2
ntlp.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
ntlp.ColumnStyles.Add(New ColumnStyle(SizeType.AutoSize))
ntlp.AutoSizeMode = Windows.Forms.AutoSizeMode.GrowAndShrink
ntlp.AutoScroll = True
'Add the new TableLayoutPanel to the TabPage
tp.Controls.Add(ntlp)
'Reset the TabIndex for the dynamic controls
Dim tbIdx As Integer = 1
'Get all the settings for this TabPage
Dim sets() As SettingInfo = Settings.GetByTabCategory(cat)
'If there aren't any we must exit
If sets Is Nothing Then Exit Sub
'Some declaration to make things easier in the loop
Dim lbl As Label = Nothing
Dim dc1 As DockStyle = DockStyle.Fill
Dim dc2 As DockStyle = DockStyle.None
Dim x As RowStyle
'Cycle through this category's settings
For i As Integer = 0 To UBound(sets)
lbl = New Label
x = New RowStyle
x.SizeType = SizeType.AutoSize
lbl.AutoSize = True
'If we've reached the end of the settings - don't use the Fill DockStyle
If i = UBound(sets) Then lbl.Dock = dc2 Else lbl.Dock = dc1
lbl.TextAlign = ContentAlignment.MiddleLeft
If TypeOf (sets(i).Value) Is Boolean Then 'If the setting is a boolean use a checkbox for editing
'Setup the label
lbl.Text = sets(i).Name
'Add the label
ntlp.Controls.Add(lbl)
ntlp.RowStyles.Add(x)
'Setup the checkbox
Dim chk As New CheckBox
chk.Name = sets(i).TrueName
chk.Tag = GetTypeAbbr(sets(i).Value.GetType)
chk.Checked = sets(i).Value
chk.TabIndex = tbIdx
'Setup the checkbox help string - for after clicking on the question mark button on the form
hp1.SetHelpString(chk, "Toggles between true and false for this setting.")
hp1.SetShowHelp(chk, True)
'Add the handler for the checkbox, and add it to the form
AddHandler chk.LostFocus, AddressOf checkbox_handler
ntlp.Controls.Add(chk)
ElseIf TypeOf (sets(i).Value) Is Date Then 'If the setting is a date use a datetimepicker for editing
'Setup the label
lbl.Text = sets(i).Name
'Add the label
ntlp.Controls.Add(lbl)
ntlp.RowStyles.Add(x)
'Setup the datetimepicker
Dim dtp As New DateTimePicker
dtp.Name = sets(i).TrueName
dtp.Tag = GetTypeAbbr(sets(i).Value.GetType)
dtp.Value = sets(i).Value
dtp.Anchor = AnchorStyles.Left + AnchorStyles.Right + AnchorStyles.Top
dtp.TabIndex = tbIdx
'Setup the datetimepicker help string - for after clicking on the question mark button on the form
hp1.SetHelpString(dtp, "Drop-down that allows you to choose a date for this setting.")
hp1.SetShowHelp(dtp, True)
'Add the handler for the datetimepicker, and add it to the form
AddHandler dtp.LostFocus, AddressOf datetimepicker_handler
ntlp.Controls.Add(dtp)
ElseIf IsTxtType(sets(i).Value.GetType) Then 'If the setting is a type we edit with a textbox use a textbox for editing
'Setup the label
lbl.Text = sets(i).Name
'Add the label
ntlp.Controls.Add(lbl)
ntlp.RowStyles.Add(x)
'Setup the textbox
Dim txt As New TextBox
txt.Name = sets(i).TrueName
'If the name contains the word Password - make the textbox mask the characters
If sets(i).Name.ToLower.Contains("password") Then txt.PasswordChar = "*"
txt.Tag = GetTypeAbbr(sets(i).Value.GetType)
txt.Text = sets(i).Value
txt.Anchor = AnchorStyles.Left + AnchorStyles.Right + AnchorStyles.Top
txt.TabIndex = tbIdx
'Setup the textbox help string - for after clicking on the question mark button on the form
'Uses GetTypeName for type specific messages
hp1.SetHelpString(txt, "Text entry field that allows you to enter " & GetTypeName(sets(i).Value.GetType) & " value for this setting.")
hp1.SetShowHelp(txt, True)
'Add the handler for the textbox, and add it to the form
AddHandler txt.LostFocus, AddressOf textbox_handler
ntlp.Controls.Add(txt)
ElseIf TypeOf (sets(i).Value) Is Color Then 'If the setting is a color use a button with no text for editing
'Setup the label
lbl.Text = sets(i).Name
'Add the label
ntlp.Controls.Add(lbl)
ntlp.RowStyles.Add(x)
'Setup the button
Dim cmd As New Button
cmd.Name = sets(i).TrueName
cmd.Tag = GetTypeAbbr(sets(i).Value.GetType)
cmd.BackColor = sets(i).Value
cmd.TabIndex = tbIdx
'Setup the button help string - for after clicking on the question mark button on the form
hp1.SetHelpString(cmd, "Button that allows you to choose a color for this setting.")
hp1.SetShowHelp(cmd, True)
'Add the handlers for the button, and add it to the form
AddHandler cmd.Click, AddressOf button_click
AddHandler cmd.LostFocus, AddressOf button_handler
ntlp.Controls.Add(cmd)
ElseIf TypeOf (sets(i).Value) Is Font Then 'If the setting is a font use a button with the font name for editing
'Setup the label
lbl.Text = sets(i).Name
'Add the label
ntlp.Controls.Add(lbl)
ntlp.RowStyles.Add(x)
'Setup the button
Dim cmd As New Button
cmd.AutoSize = True
cmd.Name = sets(i).TrueName
cmd.Tag = GetTypeAbbr(sets(i).Value.GetType)
cmd.Text = CType(sets(i).Value, Font).Name
cmd.Font = sets(i).Value
cmd.UseVisualStyleBackColor = True
cmd.TabIndex = tbIdx
'Setup the button help string - for after clicking on the question mark button on the form
hp1.SetHelpString(cmd, "Button that allows you to choose a font for this setting.")
hp1.SetShowHelp(cmd, True)
'Add the handlers for the button, and add it to the form
AddHandler cmd.Click, AddressOf button_click
AddHandler cmd.LostFocus, AddressOf button_handler
ntlp.Controls.Add(cmd)
'##### enum type
ElseIf TypeOf (sets(i).Value) Is [Enum] Then 'If the setting is a enum type we fill a combobox
'Setup the label
lbl.Text = sets(i).Name
'Add the label
ntlp.Controls.Add(lbl)
ntlp.RowStyles.Add(x)
'Setup the ComboBox
Dim cbo As New ComboBox
cbo.DropDownStyle = ComboBoxStyle.DropDownList
cbo.Name = sets(i).TrueName
cbo.Tag = GetTypeAbbr(sets(i).Value.GetType)
For Each o As Object In [Enum].GetValues(sets(i).Value.GetType)
cbo.Items.Add(o)
Next
cbo.SelectedIndex = sets(i).Value
cbo.Anchor = AnchorStyles.Left + AnchorStyles.Right + AnchorStyles.Top
cbo.TabIndex = tbIdx
'Setup the help string - for after clicking on the question mark button on the form
hp1.SetHelpString(cbo, "ComboBox that allows you to enter values of type " & sets(i).Value.GetType.FullName & " for this setting.")
hp1.SetShowHelp(cbo, True)
'Add the handler for the Combobox, and add it to the form
AddHandler cbo.LostFocus, AddressOf enum_handler
ntlp.Controls.Add(cbo)
End If
'Don't keep the label at autosize - remember it's most likely docked
lbl.AutoSize = False
'Increment the TabIndex
tbIdx += 1
Next
'Update the TabIndex of controls already on the form
chkSaveOnExit.TabIndex = tbIdx + 1
cmdOK.TabIndex = tbIdx + 2
cmdCancel.TabIndex = tbIdx + 3
cmdApply.TabIndex = tbIdx + 4
End Sub
#End Region
#Region " TreeView Handling "
''' <summary>
''' Clears all the setting controls that were created at runtime from the window.
''' </summary>
Private Sub ClearOptions()
'Cleans up the dynamically added controls for the settings
Dim ctrl As Control
'While there are still dynamic controls on the form - remove them
While tlp.Controls.Count > 0
'Set an object equal to a reference of the control
ctrl = tlp.Controls.Item(0)
'Remove the handlers based on the type of the control
If TypeOf (ctrl) Is Button Then
RemoveHandler ctrl.Click, AddressOf button_click
RemoveHandler ctrl.LostFocus, AddressOf button_handler
ElseIf TypeOf (ctrl) Is CheckBox Then
RemoveHandler ctrl.LostFocus, AddressOf checkbox_handler
ElseIf TypeOf (ctrl) Is DateTimePicker Then
RemoveHandler ctrl.LostFocus, AddressOf datetimepicker_handler
ElseIf TypeOf (ctrl) Is TextBox Then
RemoveHandler ctrl.LostFocus, AddressOf textbox_handler
End If
'Remove the control from the form
ctrl.Dispose()
End While
End Sub
''' <summary>
''' Determines whether the type passed would use a textbox or not.
''' </summary>
''' <param name="typ">A system type that you wish to check.</param>
''' <returns>Boolean: True the type does use a textbox, false it doesn't use a textbox.</returns>
Private Function IsTxtType(ByVal typ As Type) As Boolean
'If the type passed is one of these we use a textbox to edit it
If GetType(Byte) Is typ Or _
GetType(Char) Is typ Or _
GetType(Decimal) Is typ Or _
GetType(Double) Is typ Or _
GetType(Integer) Is typ Or _
GetType(Long) Is typ Or _
GetType(SByte) Is typ Or _
GetType(Short) Is typ Or _
GetType(Single) Is typ Or _
GetType(String) Is typ Or _
GetType(UInteger) Is typ Or _
GetType(ULong) Is typ Or _
GetType(UShort) Is typ Then
Return True
End If
'We don't use a textbox on this type.
Return False
End Function
Private Sub tvCategories_AfterSelect(ByVal sender As System.Object, ByVal e As System.Windows.Forms.TreeViewEventArgs) Handles tvCategories.AfterSelect
'A category was selected - clear the current display
ClearOptions()
tlp.RowStyles.Clear()
tlp.RowCount = 1
'Reset the TabIndex for the dynamic controls
Dim tbIdx As Integer = 1
'Get all the settings that match this category
Dim sets() As SettingInfo = Settings.GetByCategory(tvCategories.SelectedNode.FullPath)
'If there aren't any we must exit
If sets Is Nothing Then Exit Sub
'Some declaration to make things easier in the loop
Dim lbl As Label = Nothing
Dim dc1 As DockStyle = DockStyle.Fill
Dim dc2 As DockStyle = DockStyle.None
Dim x As RowStyle
'Cycle through this category's settings
For i As Integer = 0 To UBound(sets)
lbl = New Label
x = New RowStyle
x.SizeType = SizeType.AutoSize
lbl.AutoSize = True
'If we've reached the end of the settings - don't use the Fill DockStyle.
If i = UBound(sets) Then lbl.Dock = dc2 Else lbl.Dock = dc1
lbl.TextAlign = ContentAlignment.MiddleLeft
If TypeOf (sets(i).Value) Is Boolean Then 'If the setting is a boolean use a checkbox for editing
'Setup the label
lbl.Text = sets(i).Name
'Add the label
tlp.Controls.Add(lbl)
tlp.RowStyles.Add(x)
'Setup the checkbox
Dim chk As New CheckBox
chk.Name = sets(i).TrueName
chk.Tag = GetTypeAbbr(sets(i).Value.GetType)
chk.Checked = sets(i).Value
chk.TabIndex = tbIdx
'Setup the checkbox help string - for after clicking on the question mark button on the form
hp1.SetHelpString(chk, "Toggles between true and false for this setting.")
hp1.SetShowHelp(chk, True)
'Add the handler for the checkbox, and add it to the form
AddHandler chk.LostFocus, AddressOf checkbox_handler
tlp.Controls.Add(chk)
ElseIf TypeOf (sets(i).Value) Is Date Then 'If the setting is a date use a datetimepicker for editing
'Setup the label
lbl.Text = sets(i).Name
'Add the label
tlp.Controls.Add(lbl)
tlp.RowStyles.Add(x)
'Setup the datetimepicker
Dim dtp As New DateTimePicker
dtp.Name = sets(i).TrueName
dtp.Tag = GetTypeAbbr(sets(i).Value.GetType)
dtp.Value = sets(i).Value
dtp.Anchor = AnchorStyles.Left + AnchorStyles.Right + AnchorStyles.Top
dtp.TabIndex = tbIdx
'Setup the datetimepicker help string - for after clicking on the question mark button on the form
hp1.SetHelpString(dtp, "Drop-down that allows you to choose a date for this setting.")
hp1.SetShowHelp(dtp, True)
'Add the handler for the datetimepicker, and add it to the form
AddHandler dtp.LostFocus, AddressOf datetimepicker_handler
tlp.Controls.Add(dtp)
ElseIf IsTxtType(sets(i).Value.GetType) Then 'If the setting is a type we edit with a textbox use a textbox for editing
'Setup the label
lbl.Text = sets(i).Name
'Add the label
tlp.Controls.Add(lbl)
tlp.RowStyles.Add(x)
'Setup the textbox
Dim txt As New TextBox
txt.Name = sets(i).TrueName
'If the name contains the word Password - make the textbox mask the characters
If sets(i).Name.ToLower.Contains("password") Then txt.PasswordChar = "*"
txt.Tag = GetTypeAbbr(sets(i).Value.GetType)
txt.Text = sets(i).Value
txt.Anchor = AnchorStyles.Left + AnchorStyles.Right + AnchorStyles.Top
txt.TabIndex = tbIdx
'Setup the textbox help string - for after clicking on the question mark button on the form
'Uses GetTypeName for type specific messages
hp1.SetHelpString(txt, "Text entry field that allows you to enter " & GetTypeName(sets(i).Value.GetType) & " value for this setting.")
hp1.SetShowHelp(txt, True)
'Add the handler for the textbox, and add it to the form
AddHandler txt.LostFocus, AddressOf textbox_handler
tlp.Controls.Add(txt)
ElseIf TypeOf (sets(i).Value) Is Color Then 'If the setting is a color use a button with no text for editing
'Setup the label
lbl.Text = sets(i).Name
'Add the label
tlp.Controls.Add(lbl)
tlp.RowStyles.Add(x)
'Setup the button
Dim cmd As New Button
cmd.Name = sets(i).TrueName
cmd.Tag = GetTypeAbbr(sets(i).Value.GetType)
cmd.BackColor = sets(i).Value
cmd.TabIndex = tbIdx
'Setup the button help string - for after clicking on the question mark button on the form
hp1.SetHelpString(cmd, "Button that allows you to choose a color for this setting.")
hp1.SetShowHelp(cmd, True)
'Add the handlers for the button, and add it to the form
AddHandler cmd.Click, AddressOf button_click
AddHandler cmd.LostFocus, AddressOf button_handler
tlp.Controls.Add(cmd)
ElseIf TypeOf (sets(i).Value) Is Font Then 'If the setting is a font use a button with the font name for editing
'Setup the label
lbl.Text = sets(i).Name
'Add the label
tlp.Controls.Add(lbl)
tlp.RowStyles.Add(x)
'Setup the button
Dim cmd As New Button
cmd.AutoSize = True
cmd.Name = sets(i).TrueName
cmd.Tag = GetTypeAbbr(sets(i).Value.GetType)
cmd.Text = CType(sets(i).Value, Font).Name
cmd.Font = sets(i).Value
cmd.UseVisualStyleBackColor = True
cmd.TabIndex = tbIdx
'Setup the button help string - for after clicking on the question mark button on the form
hp1.SetHelpString(cmd, "Button that allows you to choose a font for this setting.")
hp1.SetShowHelp(cmd, True)
'Add the handlers for the button, and add it to the form
AddHandler cmd.Click, AddressOf button_click
AddHandler cmd.LostFocus, AddressOf button_handler
tlp.Controls.Add(cmd)
'##### enum type
ElseIf TypeOf (sets(i).Value) Is [Enum] Then 'If the setting is a enum type we fill a combobox
'Setup the label
lbl.Text = sets(i).Name
'Add the label
tlp.Controls.Add(lbl)
tlp.RowStyles.Add(x)
'Setup the ComboBox
Dim cbo As New ComboBox
cbo.DropDownStyle = ComboBoxStyle.DropDownList
cbo.Name = sets(i).TrueName
cbo.Tag = GetTypeAbbr(sets(i).Value.GetType)
For Each o As Object In [Enum].GetValues(sets(i).Value.GetType)
cbo.Items.Add(o)
Next
cbo.SelectedIndex = sets(i).Value
cbo.Anchor = AnchorStyles.Left + AnchorStyles.Right + AnchorStyles.Top
cbo.TabIndex = tbIdx
'Setup the help string - for after clicking on the question mark button on the form
hp1.SetHelpString(cbo, "ComboBox that allows you to enter values of type " & sets(i).Value.GetType.FullName & " for this setting.")
hp1.SetShowHelp(cbo, True)
'Add the handler for the Combobox, and add it to the form
AddHandler cbo.LostFocus, AddressOf enum_handler
tlp.Controls.Add(cbo)
End If
'Don't keep the label at autosize - remember it's most likely docked
lbl.AutoSize = False
'Increment the TabIndex
tbIdx += 1
Next
'Update the TabIndex of controls already on the form
chkSaveOnExit.TabIndex = tbIdx + 1
cmdOK.TabIndex = tbIdx + 2
cmdCancel.TabIndex = tbIdx + 3
cmdApply.TabIndex = tbIdx + 4
End Sub
''' <summary>
''' Gets the four letter type abbreviation for the passed type.
''' </summary>
''' <param name="typ">A system type that you wish to get the four letter type for.</param>
''' <returns>String containing the four letter abbrieviation for the type.</returns>
''' <remarks>Some are 3 letters with a space in front to make it four characters.</remarks>
Private Function GetTypeAbbr(ByVal typ As Type) As String
'Get a four character string to represent the type
'This goes in the tag of the dynamic control and helps
'the handlers determine what type to check for in textboxes
'and buttons.
If GetType(Boolean) Is typ Then
Return "bool"
ElseIf GetType(Byte) Is typ Then
Return "byte"
ElseIf GetType(Char) Is typ Then
Return "char"
ElseIf GetType(Date) Is typ Then
Return "date"
ElseIf GetType(Decimal) Is typ Then
Return " dec"
ElseIf GetType(Double) Is typ Then
Return " dbl"
ElseIf GetType(Integer) Is typ Then
Return " int"
ElseIf GetType(Long) Is typ Then
Return "long"
ElseIf GetType(SByte) Is typ Then
Return "sbyt"
ElseIf GetType(Short) Is typ Then
Return "shrt"
ElseIf GetType(Single) Is typ Then
Return "sngl"
ElseIf GetType(String) Is typ Then
Return " str"
ElseIf GetType(Color) Is typ Then
Return " clr"
ElseIf GetType(Font) Is typ Then
Return "font"
ElseIf GetType(UInteger) Is typ Then
Return "uint"
ElseIf GetType(ULong) Is typ Then
Return "ulng"
ElseIf GetType(UShort) Is typ Then
Return "usht"
End If
Return Nothing
End Function
''' <summary>
''' Gets a string containing the type name with a describing article (a, an, the) for display in a MsgBox
''' </summary>
''' <param name="typ">A system type that you wish to get the name of.</param>
''' <returns>String containing the name of the type and an article for describing it.</returns>
Private Function GetTypeName(ByVal typ As Type) As String
'Gets a string for the message box error message when trying to determine
'if a setting was set properly
If GetType(Boolean) Is typ Then
Return "a Boolean"
ElseIf GetType(Byte) Is typ Then
Return "a Byte"
ElseIf GetType(Char) Is typ Then
Return "a Character"
ElseIf GetType(Date) Is typ Then
Return "a Date"
ElseIf GetType(Decimal) Is typ Then
Return "a Decimal (numeric)"
ElseIf GetType(Double) Is typ Then
Return "a Double (numeric)"
ElseIf GetType(Integer) Is typ Then
Return "an Integer (numeric)"
ElseIf GetType(Long) Is typ Then
Return "a Long (numeric)"
ElseIf GetType(SByte) Is typ Then
Return "a Short Byte"
ElseIf GetType(Short) Is typ Then
Return "a Short (numeric)"
ElseIf GetType(Single) Is typ Then
Return "a Single (numeric)"
ElseIf GetType(String) Is typ Then
Return "a String (text)"
ElseIf GetType(Color) Is typ Then
Return "a Color"
ElseIf GetType(Font) Is typ Then
Return "a Font"
ElseIf GetType(UInteger) Is typ Then
Return "an Unsigned Integer (numeric)"
ElseIf GetType(ULong) Is typ Then
Return "an Unsigned Long (numeric)"
ElseIf GetType(UShort) Is typ Then
Return "an Unsigned Short (numeric)"
End If
Return Nothing
End Function
#End Region
#Region " Property Handlers "
'checkbox, datetimepicker, textbox, button
'Handlers do not apply the values they set - just update the collection
'##### enum types
Private Sub enum_handler(ByVal sender As Object, ByVal e As System.EventArgs)
Settings.SetEnumByTrueName(sender.Name, sender.text)
End Sub
Private Sub checkbox_handler(ByVal sender As Object, ByVal e As System.EventArgs)
'Checkboxes can only be boolean - update the setting in the collection
Settings.SetValueByTrueName(sender.Name, sender.Checked)
End Sub
Private Sub datetimepicker_handler(ByVal sender As Object, ByVal e As System.EventArgs)
'the datetimepicker can only be a date - so update the collection
Settings.SetValueByTrueName(sender.Name, sender.Value)
End Sub
Private Sub textbox_handler(ByVal sender As Object, ByVal e As System.EventArgs)
'byte, char, decimal, double, integer, long, sbyte, short, single, string, uinteger, ulong, ushort
'is a list of everything that a textbox could contain
'check the value in the textbox to make sure it
'is of the correct type before adding it to the
'collection - if it's not the correct type tell the
'user so and select the offending value
If CType(sender.Tag, String) = GetTypeAbbr(GetType(Byte)) Then
Dim x As Byte = Nothing
If Byte.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a Byte.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(Char)) Then
Dim x As Char = Nothing
If Char.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a Char.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(Decimal)) Then
Dim x As Decimal = Nothing
If Decimal.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a Decimal.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(Double)) Then
Dim x As Double = Nothing
If Double.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a Double.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(Integer)) Then
Dim x As Integer = Nothing
If Integer.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a Integer.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(Long)) Then
Dim x As Long = Nothing
If Long.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a Long.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(SByte)) Then
Dim x As SByte = Nothing
If SByte.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a SByte.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(Short)) Then
Dim x As Short = Nothing
If Short.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a Short.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(Single)) Then
Dim x As Single = Nothing
If Single.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a Single.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(String)) Then
Settings.SetValueByTrueName(sender.Name, sender.Text)
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(UInteger)) Then
Dim x As UInteger = Nothing
If UInteger.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a UInteger.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(ULong)) Then
Dim x As ULong = Nothing
If ULong.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a ULong.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(UShort)) Then
Dim x As UShort = Nothing
If UShort.TryParse(sender.Text, x) Then
Settings.SetValueByTrueName(sender.Name, x)
Else
MsgBox("Could not convert " & sender.Text & " to a UShort.", MsgBoxStyle.Exclamation)
sender.Focus()
sender.SelectAll()
End If
End If
End Sub
Private Sub button_click(ByVal sender As Object, ByVal e As System.EventArgs)
'Buttons can contain either a Font, or a color
'check which it is and bring up the appropriate dialog
'box for editing
If CType(sender.Tag, String) = GetTypeAbbr(GetType(Color)) Then
dlgColor.Color = sender.BackColor
If dlgColor.ShowDialog = Windows.Forms.DialogResult.OK Then sender.BackColor = dlgColor.Color
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(Font)) Then
dlgFont.Font = CType(sender, Button).Font
If dlgFont.ShowDialog = Windows.Forms.DialogResult.OK Then
sender.Font = dlgFont.Font
sender.Text = dlgFont.Font.Name
End If
End If
End Sub
Private Sub button_handler(ByVal sender As Object, ByVal e As System.EventArgs)
'Buttons can contain either a Font, or a color
'check which it is to make sure we're updating
'the correct value
If CType(sender.Tag, String) = GetTypeAbbr(GetType(Color)) Then
Settings.SetValueByTrueName(sender.Name, sender.BackColor)
ElseIf CType(sender.Tag, String) = GetTypeAbbr(GetType(Font)) Then
Settings.SetValueByTrueName(sender.Name, sender.Font)
End If
End Sub
#End Region
#Region " Button Clicks "
Private Sub cmdApply_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdApply.Click
ApplySettings() 'Apply the settings don't save, let the application save when it exits
End Sub
Private Sub cmdOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOK.Click
'#####
'if cmdOK_Click is called by pressing Return/Enter key set the focus to cmdOK, so that LostFocus is called
' and the value is copied to settings
cmdOK.Focus()
ApplySettings() 'Apply the settings don't save, let the application save when it exits
'Tell the dialog it can close and everything is ok
Me.DialogResult = Windows.Forms.DialogResult.OK
End Sub
''' <summary>
''' Applies the settings in the SettingInfoCollection to the actual setting in the My namespace.
''' </summary>
Private Sub ApplySettings()
'Cycle through each setting that we could edit and
'update the real setting contained in the My namespace
For Each si As SettingInfo In Settings
My.Settings.Item(si.TrueName) = si.Value
Next
'Update the SaveOnExit value
My.Application.SaveMySettingsOnExit = chkSaveOnExit.Checked
End Sub
#End Region
End Class
Public Class SettingCategory
Public MainCat As String
Public SubCats As String
End Class
Public Class SettingCategoryCollection
Inherits CollectionBase
Public Sub Add(ByVal item As SettingCategory)
List.Add(item)
End Sub
Default ReadOnly Property Item(ByVal index As Integer) As SettingCategory
Get
Return CType(List.Item(index), SettingCategory)
End Get
End Property
Public Function IndexOf(ByVal name As String, ByVal subc As String) As Integer
For i As Integer = 0 To Count - 1
If CType(List.Item(i), SettingCategory).MainCat = name And CType(List.Item(i), SettingCategory).SubCats = subc Then Return i
Next
Return -1
End Function
End Class
Public Class SettingInfo
Private _Name As String
''' <summary>
''' The display name of the setting.
''' </summary>
''' <value>The display name for the setting</value>
''' <returns>The display name for the setting</returns>
Public Property Name() As String
Get
Return _Name
End Get
Set(ByVal value As String)
_Name = value
End Set
End Property
Private _Category As String
''' <summary>
''' The category to which the setting belongs.
''' </summary>
''' <value>The category to which the setting belongs</value>
''' <returns>The dot seperated category/sub-category to which the setting belongs</returns>
Public ReadOnly Property Category() As String
Get
Return _Category
End Get
End Property
''' <summary>
''' Sets the category based on an array of the categories/sub-categories.
''' </summary>
''' <param name="values">The category/sub-category list. Index 0 is the category and the sub-categories follow.</param>
Public Sub SetCategory(ByVal values() As String)
'The category should contain a dot separated list of values - implode does this nicely
_Category = implode(".", values)
End Sub
''' <summary>
''' Sets the category to the string provided.
''' </summary>
''' <param name="value">The actual dot separated category/sub-category list. (ex Category1.SubCat1.SubCat2)</param>
Public Sub SetCategory(ByVal value As String)
'If the value is already in dot separated format just updated (doesn't check)
_Category = value
End Sub
''' <summary>
''' Takes an array of values and concatenates them separating each item in the array with the value in chr.
''' </summary>
''' <param name="chr">A character or string which separates the values in "values".</param>
''' <param name="values">An array of strings to be concatenated and separated by chr.</param>
''' <returns>A string containing all the values in the array provided concatenated and separated by the value in chr.</returns>
Private Function implode(ByVal chr As String, ByVal values() As String) As String
'Taken from the php function implode
'Setup an empty string for building
Dim tmp As String = Nothing
'Cycle through the array
For Each str As String In values
'Add the current value and the separator to the string
tmp &= str & chr
Next
'The result has an extra delimiter on the end remove it
tmp = tmp.TrimEnd(chr)
'Return the finished result
Return tmp
End Function
''' <summary>
''' Loads a My.Settings setting name into the current SettingInfo object.
''' </summary>
''' <param name="str">A My.Settings name in the following format (Category_SubCategory1_SubCategory2_etc..._Setting__Name_SortIndex).</param>
''' <remarks>A single underscore separates the categories, name, and sort index. A double underscore signifies a space. Not providing a sort index, will give the setting a sort index of -1.</remarks>
Public Sub LoadData(ByVal str As String)
'Replace the double underscore with a space to allow the window
'to show multi-word Settings names
str = str.Replace("__", " ")
'Check if the last value (separated by underscores) is numeric
If MyNumeric(str.Substring(str.LastIndexOf("_") + 1)) Then
'It is numeric - so use it as the SortIndex
_Sort = Integer.Parse(str.Substring(str.LastIndexOf("_") + 1))
'Remove it from the string
str = str.Substring(0, str.LastIndexOf("_"))
Else
'Not numeric assign default SortIndex of -1
_Sort = -1
End If
'Assign the name of the setting - should be that last value in the string
_Name = str.Substring(str.LastIndexOf("_") + 1)
'Get the category string (don't include the name)
Dim cat As String = str.Substring(0, str.LastIndexOf("_"))
'Assign the category (SetCategory takes a string array so use split)
SetCategory(cat.Split("_"))
'Get the value from the My namespace and assign it
_Value = My.Settings.Item(TrueName)
End Sub
''' <summary>
''' Checks if the string is a pure numeric value.
''' </summary>
''' <param name="val">A string containing the value to be checked.</param>
''' <returns>True if the value is numeric, false if the value isn't numeric.</returns>
''' <remarks>Cycles through each character in the value passed and checks if it is one of the following: 1234567890. If it's not the value isn't numeric and it returns false.</remarks>
Private Function MyNumeric(ByVal val As String) As Boolean
'Create a string array for valid characters of a numeric string
Dim chr() As String = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}
'Check each character in the passed value
For i As Integer = 0 To val.Length - 1
If Array.IndexOf(chr, val.Substring(i, 1)) = -1 Then Return False 'Not a number
Next
'Successfully made it through the test - it is a number
Return True
End Function
Private _Sort As Integer
''' <summary>
''' The SortIndex of the setting name. Determines where the setting will be display on the form once it's category is selected.
''' </summary>
''' <value>Integer</value>
''' <returns>Integer</returns>
Public Property SortIndex() As Integer
Get
Return _Sort
End Get
Set(ByVal value As Integer)
_Sort = value
End Set
End Property
Private _Value As Object
''' <summary>
''' The value of the setting. Could be any object type from boolean to font.
''' </summary>
''' <value>Object containing the settings value.</value>
''' <returns>Object containing the settings value.</returns>
Public Property Value() As Object
Get
Return _Value
End Get
Set(ByVal value As Object)
_Value = value
End Set
End Property
''' <summary>
''' The true name of the setting. Computes the original setting name from the category, name, and sort index.
''' </summary>
''' <returns>String containing the true setting name.</returns>
Public ReadOnly Property TrueName() As String
'Rebuilds the original setting name as specified in the My namespace
Get
'Create temporary string and give it the value of the category replacing the periods with underscores
'Also add the name onto the end of it
Dim tmp As String = _Category.Replace(".", "_") & "_" & _Name
'If there is a valid SortIndex - add that to the end as well
If _Sort <> -1 Then tmp &= "_" & SortIndex
'Return a value with no spaces (use double underscores instead)
Return tmp.Replace(" ", "__")
End Get
End Property
''' <summary>
''' Clones the current SettingInfo object to another variable.
''' </summary>
''' <returns>A copy of the current SettingInfo object.</returns>
Public Function Clone() As SettingInfo
'Build a copy of this SettingInfo and return it
Dim tmp As New SettingInfo
tmp.Name = _Name
tmp.SetCategory(_Category)
tmp.SortIndex = _Sort
tmp.Value = _Value
Return tmp
End Function
End Class
Public Class SettingInfoCollection
Inherits CollectionBase
''' <summary>
''' Adds a SettingInfo object to the collection.
''' </summary>
''' <param name="item">A SettingInfo object</param>
Public Sub Add(ByVal item As SettingInfo)
'Add the item to the list
List.Add(item)
End Sub
''' <summary>
''' Returns a specific element of the collection by position. Read-only.
''' </summary>
''' <param name="index">A numeric expression that specifies the position of an element of the collection. Index must be a number from 0 through the value of the Collection's Count Property.</param>
''' <returns>A SettingInfo object from the position specified.</returns>
Default Public ReadOnly Property Item(ByVal index As Integer) As SettingInfo
Get
'Return the selected item (make sure it is the correct type)
Return CType(List.Item(index), SettingInfo)
End Get
End Property
''' <summary>
''' Sets the values of a SettingInfo object already in the collection.
''' </summary>
''' <param name="index">A numeric expression that specifies the position of an element you wish to change in the collection. Index must be a number from 0 through the value of the Collection's Count Property.</param>
''' <param name="value">A SettingInfo object that contains the values you wish to set to the element specified by Index.</param>
Public Sub SetItem(ByVal index As Integer, ByVal value As SettingInfo)
'Update an item already in the collection, must do it one value at a time
'otherwise we just get a reference
CType(List.Item(index), SettingInfo).SetCategory(value.Category)
CType(List.Item(index), SettingInfo).Name = value.Name
CType(List.Item(index), SettingInfo).Value = value.Value
CType(List.Item(index), SettingInfo).SortIndex = value.SortIndex
End Sub
''' <summary>
''' Get's all the SettingInfo objects in the collection that have the same category as the one specified.
''' </summary>
''' <param name="category">A string expression that specifies the category of the elements to retrieve. Category must be in the dot separated format used by the SettingInfo objects (ex. Category.SubCategory.SubCat2)</param>
''' <returns>An array of SettingInfo objects that match the category specified.</returns>
Public Function GetByCategory(ByVal category As String) As SettingInfo()
'Create and empty array used for return the found data
Dim tmp() As SettingInfo = Nothing
'An integer to store the current array index
Dim iT As Integer = 0
'A boolean to tell us if we need to sort it
Dim sortIt As Boolean = False
'Cycle through all the settings
For si As Integer = 0 To Count - 1
'Does the category match?
If CType(List.Item(si), SettingInfo).Category = category Then
'Yes - Initialize current array itemj
ReDim Preserve tmp(iT)
'If the SortIndex isn't default (-1) then we need to sort the entire array when done
If CType(List.Item(si), SettingInfo).SortIndex <> -1 Then sortIt = True
'Copy the item to the array
tmp(iT) = List.Item(si)
'Increment the index
iT += 1
End If
Next
'Do we need to sort it?
If sortIt Then
'Yes - call the quicksort subroutine for this array
Me.SortIt(tmp, 0, UBound(tmp))
End If
'Return the sorted (if necessary) array
Return tmp
End Function
''' <summary>
''' Get's all the SettingInfo objects in the collection that have the same category as the one specified.
''' </summary>
''' <param name="cat">A string expression that specifies the category of the elements to retrieve. Category must be in the dot separated format used by the SettingInfo objects (ex. Category.SubCategory.SubCat2)</param>
''' <returns>An array of SettingInfo objects that match the category specified.</returns>
''' <remarks>Used only for the TabPages as the category could be condensed from the real form.</remarks>
Public Function GetByTabCategory(ByVal cat As String) As SettingInfo()
'Create and empty array used for return the found data
Dim tmp() As SettingInfo = Nothing
'An integer to store the current array index
Dim iT As Integer = 0
'A boolean to tell us if we need to sort it
Dim sortIt As Boolean = False
'Cycle through all the settings
For si As Integer = 0 To Count - 1
'Does the category match?
If CType(List.Item(si), SettingInfo).Category.StartsWith(cat) Then
'Yes - Initialize current array itemj
ReDim Preserve tmp(iT)
'If the SortIndex isn't default (-1) then we need to sort the entire array when done
If CType(List.Item(si), SettingInfo).SortIndex <> -1 Then sortIt = True
'Copy the item to the array
tmp(iT) = List.Item(si)
'Increment the index
iT += 1
End If
Next
'Do we need to sort it?
If sortIt Then
'Yes - call the quicksort subroutine for this array
Me.SortIt(tmp, 0, UBound(tmp))
End If
'Return the sorted (if necessary) array
Return tmp
End Function
Public Function GetCategories() As SettingCategoryCollection
Dim tmp As New SettingCategoryCollection
Dim add As SettingCategory
Dim name As String
Dim subn As String
For Each si As SettingInfo In List
name = Nothing
subn = Nothing
If si.Category.IndexOf(".") = -1 Then name = si.Category Else name = si.Category.Substring(0, si.Category.IndexOf("."))
If si.Category.IndexOf(".") = -1 Then subn = Nothing Else subn = si.Category.Substring(si.Category.IndexOf("."))
If tmp.IndexOf(name, subn) = -1 Then
add = New SettingCategory
add.MainCat = name
add.SubCats = subn
tmp.Add(add)
End If
Next
Return tmp
End Function
''' <summary>
''' Sorts an array of SettingInfo objects by their SortIndex.
''' </summary>
''' <param name="SortArray">A reference to the array you wish to sort.</param>
''' <param name="First">The starting element of the selection you wish to sort (usually 0).</param>
''' <param name="Last">Then final element of the selection you wish to sort (usually the upper bound of the array).</param>
''' <remarks>Called from GetByCategory</remarks>
Private Sub SortIt(ByRef SortArray() As SettingInfo, ByVal First As Long, ByVal Last As Long)
'Copied and modified from the SortIt method in the form
Dim Low As Long, High As Long
Dim Temp As SettingInfo = Nothing
Dim List_Separator As SettingInfo = Nothing
Low = First
High = Last
List_Separator = SortArray((First + Last) / 2).Clone
Do
Do While (SortArray(Low).SortIndex < List_Separator.SortIndex)
Low += 1
Loop
Do While (SortArray(High).SortIndex > List_Separator.SortIndex)
High -= 1
Loop
If (Low <= High) Then
Temp = SortArray(Low).Clone
SortArray(Low) = SortArray(High).Clone
SortArray(High) = Temp.Clone
Low += 1
High -= 1
End If
Loop While (Low <= High)
If (First < High) Then SortIt(SortArray, First, High)
If (Low < Last) Then SortIt(SortArray, Low, Last)
End Sub
''' <summary>
''' Sets the value of an SettingInfo object already in the collection by it's true name.
''' </summary>
''' <param name="name">The name of the setting as specified in the My.Settings object.</param>
''' <param name="value">The value you wish to set to the specified setting.</param>
''' <remarks>Called by the control handlers when the controls lose focus.</remarks>
Public Sub SetValueByTrueName(ByVal name As String, ByVal value As Object)
'Loop through all items in the list
For i As Integer = 0 To Count - 1
'Does the item's TrueName match the name passed?
If CType(List(i), SettingInfo).TrueName = name Then
'Yes - then update the value and exit
CType(List(i), SettingInfo).Value = value
Exit Sub
End If
Next
End Sub
'##### enum types
Public Sub SetEnumByTrueName(ByVal name As String, ByVal EnumText As String)
'Loop through all items in the list
For i As Integer = 0 To Count - 1
'Does the item's TrueName match the name passed?
If CType(List(i), SettingInfo).TrueName = name Then
'Yes - then update the value and exit
CType(List(i), SettingInfo).Value = [Enum].Parse(CType(List(i), SettingInfo).Value.GetType, EnumText)
Exit Sub
End If
Next
End Sub
End Class
|
|
|
|
|
Thanks Peter,
I've updated the source to include enums.
I've also written an update for the article and submitted it along with new source files.
The source download will soon include enum support and FireFox1 & FireFox2 styles (easily add images too!).
|
|
|
|
|
OK let me start off by saying wowsers...nice very nice
i really like that it automaticaly makes the settings on the form by editing the settings under properties. very nice indeed.
you didnt say how to use the information in a form
heres a snip from the help via vb.net
----------------------------------------------
Sub ShowNickname()
MsgBox("Nickname is " & My.Settings.Nickname)
End Sub
For this example to work, your application must have a Nickname setting, of type String
---------------------------------------------------
and heres how to change it
-----------------------------------------------
This example changes the value of the Nickname user setting.
Sub ChangeNickname(ByVal newNickname As String)
My.Settings.Nickname = newNickname
End Sub
For this example to work, your application must have a Nickname user setting, of type String.
------------------------------------------------------
thought youd like to see the vb.net version of how to set it up
------------------------------------------------------------
To add application settings in the Properties window
Select a form or control in the Form Designer; on the View menu, click Properties Window.
In the Properties window, expand the (Application Settings) property (located under the Data node).
Select the (Property Binding) property and click the ellipsis button (...) to open the Application Settings dialog box.
In the Application Settings dialog box, select the property for which you wish to add an application setting.
In the drop-down list for the property, click (New...) to open the New Application Setting dialog box.
In the New Application Setting dialog box, select the Name property and enter a name for the setting. The name cannot contain spaces.
Select the DefaultValue property and enter a default value for the setting.
Select the scope of the setting from the Scope drop-down list. The setting's scope can be Application or User.
To bind the new setting to the property, select it from the drop-down list, then click OK.
The property binding will be added to the Properties window, and the new setting will be added to the Project Designer. Note that once you have created the setting, you must use the Project Designer to change it.
To remove application settings
Select a project in Solution Explorer; on the Project menu, click Properties.
Select the Settings pane.
Click on the row in the Settings grid for the setting you wish to remove.
Press the Delete key, or right-click and select Remove Setting.
The setting will be removed from the Project Designer.
Note
You will need to remove settings manually from app.config because the Project Designer does not remove any references to application settings in your code or its own code.
-----------------------------------------------
again thanks alot for making it..im gonna alow the users to set the way it shows too..and a default of tabs..i like the treeview myself so this is great...
thx
-- modified at 9:06 Friday 25th November, 2005
|
|
|
|
|
Well there are two ways to use the data that is saved.
First you can bind it to an object on the form (or the form itself). You do this by selecting the object you want to bind in the form designer and going (in the properties window) to ApplicationSettings. Expand that and you will find PropertyBindings (though the most common used bindings for that object are already visible), choose the "..." button for PropertyBindings to get a list of all the properties you can bind. Then from a drop-down list, choose the setting you wish to bind it too.
Second you can use direct assignment. You can do this from the My.Settings namespace. For each setting you create using the designer, it adds a setting to the namespace. So say you have a string setting named str_Test_Setting - you would assign that to a textbox like this: TextBox1.Text = My.Settings.str_Test_Setting. It's really that simple.
Yea it probably did hit me in the face... happens quite a bit now that I think about it - probably got numb off the feeling.
Will definately work on updating that in the article as well.
|
|
|
|
|
I was updating my threed when you replied..after waking up this morning i thought better of how i asked about it..and i said hang on i might be able to see this all in the vb.net help..so i copied a few of the things and posted them in my threed...
thank you for such a wonderfull options panel...
Johnny
-- modified at 9:20 Friday 25th November, 2005
|
|
|
|
|
Ain't that always the way.
No problem - I'm was sure that people could use something like this so I did it.
I hope to include a FireFox style for it soon as well.
|
|
|
|
|
i seam to be missing somthing here..
theres a key file in my download of the source
SettingsDialog_TemporaryKey.pfx
is displaying a dialog to enter a password in both loading and in compliling..
how does one get this password
thxs
Pern
aka Johnny
:->
:->
|
|
|
|
|
Woah!
I didn't even know that existed - I will look into that as soon as I can. I don't remember setting any passwords?!
One solution right now is to open up the vbproj file in a text editor (notepad will do) and remove the following XML keys:
<SignManifests>true</SignManifests>
<ManifestCertificateThumbprint>D7F3E3F5E957981AC8F44F52D3DC79D1EE619B31</ManifestCertificateThumbprint>
<ManifestKeyFile>SettingsDialog_TemporaryKey.pfx</ManifestKeyFile>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>SettingsDialog_TemporaryKey.pfx</AssemblyOriginatorKeyFile>
That allowed me to open the solution, on a different computer than the one I created it on without the prompting for the password.
I'll look into updating the zip file available for download here as soon as possible.
Thanks for letting me know about this!
-- modified at 22:00 Thursday 24th November, 2005
|
|
|
|
|
clever bits!
i believe that with a vey minute effort you can get the Firefox style Icon-enabled settings box out of this one,
nice icons on the left side list will also help resolve the "scary dialog box" issue too
+ +
//ch3ckmat3
|
|
|
|
|
Thanks ch3ckmt3,
I will definately look into adding Firefox style groupings on the left, and instead of a boolean an enum type for selecting which style of dialog box people wish to use.
I'm not very good with icons, and as the Categories are dynamic, based on the programmer's need - I'll let the programmer's find their icons.
Thanks again for the suggestion.
Chris Kolkman
|
|
|
|
|
If your users are likely to be highly computer literate, they are likely to have encountered such dialogs before and be familiar with how to use them. If however, ordinary people (a scary concept, I know!) are likely to use your product you would be advised to think very carefully before including this style of UI in your product.
The following excerpt from Chapter 4 of User Interface Design for Programmers by Joel Spolsky is instructive:
"Now you and I are elite programming hackers with a lot of experience of these kinds of dialogs. So when we look at Figure 4-8 [a screenshot of the Preferences dialog from Netscape Navigator] we immediately understand that choosing one of the categories from thje left hand part of the screen is going to magically change which options are available on the right hand part of the screen.
"For some reason this type of indirection was incredibly logical to the programmers who designed it, but many users didn't understand it. The problem? Well most people are not elite programming hackers. Most people would never guess that choosing something from the list on the left is supposed to change the content of the dialog on the right: there's no visual reason to think that. In fact,, what most people think is that the list on the left is nothing more than another setting, and they are afraid to touch it because it seems like a pretty scary setting that they don't understand.
"When you do usability tests with dialogs like that, and you ask people to change one of the settings not actually shown on the main page, you'll find that a remarkable number of people just can't figure out how to do it. When Microsoft did a usability test with a similar dialog from an old version of Microsoft Word, only 30% of the users succeeded at the task. A full 70% simply gave up without accomplishing the task they were given.
"So, the Microsoft team switched to the famous tabbed dialogs..."
Anna
Riverblade Ltd - Software Consultancy Services
Anna's Place | Tears and Laughter
"Be yourself - not what others think you should be"
- Marcia Graesch
"Anna's just a sexy-looking lesbian tart"
- A friend, trying to wind me up. It didn't work.
|
|
|
|
|
Thank you for that excerpt Anna - I have not read that book.
You are indeed correct, which give me the opportunity to include both styles in one form and make it an option for the programmer on which to use.
I'm going to start working on this right away.
Thank you.
-- modified at 7:13 Wednesday 16th November, 2005
|
|
|
|
|
Agreed. The Firefox2 style is even more evil - the Mozilla guys broke ALL design guidelines with it. Fx1.5 is the first application I've seen do this stuff. This doesn't make it correct - if anything, it makes Mozilla look extremely stupid and ridiculous. This is one of the worst things I've ever seen in GUI design...
TabPages are the only correct way of displaying various groups of options, unless there's way too many of them; if you have more than 5-6 tabs, your design is wrong. And *NEVER* have that awful horizontal tab scroller, like in Visual Studio!!!
Don't use the TreeView. Trees aren't very understandable to people and they're hard to navigate. A ListView would be better, and if you need to have subcategories, combine them with tabs - a LV item selects the category, tabs on the right of the dialog select subcategories.
No images should be present in the ListView, as they deter people from focusing on the text due to a heavier visual factor. The FireFox1 theme fails in that aspect, especially with centered images and text.
|
|
|
|
|
Thank you for posting that here. While I'm merely(sp) providing the options for people to choose what this wish - I hope people will read these and make an informed descision about which style to use.
With the "no images should present" comment, I can see your point - especially if the images don't do anything to help the user easily figure out what the group is. However, if the right (very difficult to do) images are chosen, there may be a heavier visual factor - but finding a group at a glance would be easier.
|
|
|
|
|
Of course You did a great job
|
|
|
|
|
|
The FireFox2 style is actually very common on Apple's OSX. I think that's where the Mozilla team got the idea.
But of course, we're developing for Windows here. So we should use a UI that is consistent with most Windows apps.
|
|
|
|
|
Really?
I'm not much of a Mac fan - although a relative of my is a Mac net admin (trained in MS though) - so we do have a Mac in the house, I just try to ignore it.
|
|
|
|
|
|
Wow! That does look a lot like it.
|
|
|
|
|