Question Passing parameters to usercontrol in design time

Stein Wuestenberghs

New member
Joined
Jan 10, 2011
Messages
3
Programming Experience
3-5
Dear

I'm developing a form with usercontrols which have to be edited during disign.

The project has a connectionstring in de my.settings.
The usercontrol has a property which is also called connectionstring.

Now, I want to pass the project connectionstring to the usercontrol connectionstring, when the usercontrol is dragged to a form. (All during design)

Is this possible?
 
To select a My.Settings connection string for the property in Properies window you need a UITypeEditor. Here is a sample code, which was written with VB 2010, but looks compatible with VB 2005 to me. Ask if you wonder about any of this (and do consult the help library first :))

A sample usercontrol with a ConnectionString property:
VB.NET:
Public Class TestConn
    Inherits UserControl

    Private _connstring As String = ""
    <System.ComponentModel.Editor(GetType(ConnectionsUITypeEditor), GetType(Drawing.Design.UITypeEditor)), _
    System.ComponentModel.DefaultValue("")> _
    Public Property ConnectionString() As String
        Get
            Return _connstring
        End Get
        Set(ByVal value As String)
            _connstring = value
        End Set
    End Property

End Class
The UITypeEditor:
VB.NET:
Public Class ConnectionsUITypeEditor
    Inherits Drawing.Design.UITypeEditor

    Public Overrides Function GetEditStyle(ByVal context As System.ComponentModel.ITypeDescriptorContext) As System.Drawing.Design.UITypeEditorEditStyle
        Return Drawing.Design.UITypeEditorEditStyle.DropDown
    End Function

    Private editorService As Windows.Forms.Design.IWindowsFormsEditorService = Nothing

    Public Overrides Function EditValue(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal provider As System.IServiceProvider, ByVal value As Object) As Object
        If (provider IsNot Nothing) Then
            editorService = CType(provider.GetService(GetType(Windows.Forms.Design.IWindowsFormsEditorService)), Windows.Forms.Design.IWindowsFormsEditorService)
        End If

        If (editorService IsNot Nothing) Then
            Dim selectionControl As New ListBox
            Dim connections = My.Settings.GetConnectionStrings
            For Each connection In connections
                selectionControl.Items.Add(connection.Key & " (My.Settings)")
            Next

            AddHandler selectionControl.Click, AddressOf OnClick
            editorService.DropDownControl(selectionControl)
            RemoveHandler selectionControl.Click, AddressOf OnClick
            Dim selected As String = CStr(selectionControl.SelectedItem)
            If selected IsNot Nothing Then
                selected = selected.Remove(selected.Length - 14)
                value = connections(selected)
            End If
            selectionControl.Dispose()
        End If

        Return value
    End Function

    Private Sub OnClick(ByVal sender As Object, ByVal e As EventArgs)
        editorService.CloseDropDown()
    End Sub
End Class
Getting the ConnectionStrings, I extended My.Setting with a GetConnectionStrings function that uses reflection to get the relevant settings:
VB.NET:
Imports System.Configuration

Namespace My
    Partial Class MySettings

        Public Function GetConnectionStrings() As Dictionary(Of String, String)
            Dim connections As New Dictionary(Of String, String)
            For Each prop In Me.GetType.GetProperties
                Dim attr As SpecialSettingAttribute = CType(Attribute.GetCustomAttribute(prop, GetType(SpecialSettingAttribute)), SpecialSettingAttribute)
                If attr IsNot Nothing AndAlso attr.SpecialSetting = SpecialSetting.ConnectionString Then
                    connections(prop.Name) = CStr(prop.GetValue(Me, Nothing))
                End If
            Next
            Return connections
        End Function
    End Class
End Namespace
 
First of all, sorry for the late response. I was working on an other project and overlooked my previous question.

I reviewed your solution, but it was not suitable for me because i'm developing a project where the usercontrol is in a control library and therefore the My.Settings of the main project are not accesible from the usercontrol.

At this moment I have a workable solution witch goes as followed.
1. I created a component connection (with holds the connectionstring, so it isn't in the my.Setttings).
2. In my usercontrol I used the control designer to enumerate through all controls at design time and check for the connection component. If present I copy it to my usercontrol.

This solution works but my preference still goes out to the my.settings solution, because this is the standaard way of working.

Regards
 
That is a quite different case, though it is still possible to use the above approach. What is needed is to get the relevant type information from the designer. The first example was looping "Me.GetType.GetProperties" where the type was the MySettings
referred to by the extension method. From what I can see the root component is the first component added to a designer, so when you add the class library user control to a form the root component should be that form. The trick here is that this form in designer is really just a base System.Windows.Form with a name and the designer generated initialization added to it, so getting the RootComponent from IDesignerHost is not helpful, but getting RootComponentClassName is. Then ITypeResolutionService can be used to resolve that type. Now there is access to the containing type being designed and more importantly the Assembly it belongs to. From here is it easy to get the MySettings type:
VB.NET:
Dim host As Design.IDesignerHost = CType(provider.GetService(GetType(Design.IDesignerHost)), Design.IDesignerHost)
Dim typeres As Design.ITypeResolutionService = CType(provider.GetService(GetType(Design.ITypeResolutionService)), Design.ITypeResolutionService)
Dim asm = typeres.GetType(host.RootComponentClassName).Assembly           
Dim settings As Type = asm.GetType(asm.GetName.Name & ".My.MySettings")
Dim connections = Me.GetConnectionStrings(settings)
GetConnectionStrings method is now made a private function in the UITypeEditor also adding a 'settings' Type parameter used to loop through, ie "For Each prop In settings.GetProperties". Then getting the value from DefaultSettingValueAttribute:
VB.NET:
connections(prop.Name) = CType(Attribute.GetCustomAttribute(prop, GetType(DefaultSettingValueAttribute)), DefaultSettingValueAttribute).Value

It would also be possible to get the settings instance, for example from the shared defaultInstance property:
VB.NET:
Dim instance = settings.GetField("defaultInstance", Reflection.BindingFlags.Static Or Reflection.BindingFlags.NonPublic).GetValue(Nothing)
Though I think it is better to avoid creating the instance when it is not needed.

Now what if the root component is not part of the project being designed, but also is referenced from some library? :eek:
 
John

I tried your solution and it works great. Thanks for that.
But i don't understand your last sentence, do you main that I will not be able to do same as above when my class library is not present in the solution?

Because my goal is to make een userfriendly designer in VB2005. Where users can use my components or usercontrols from the toolbox.
And where these controls are able to get global data (like the connectionstring) from the rootcomponent.
This because they all use the same database for storing their data.
 
No, it was a joke really, bad perhaps. You can't design a form type from a referenced library, because that type is already compiled.
You would in that case have to inherit it, which means the root component again would be a type defined by the containing project.

There is however another issue that is real. The My.Settings object is built by root namespace, then the My namespace, then the MySettings class. The current code get the Assembly from the root component, then gets the name of the assembly and uses this to qualify the Namespace to the MySettings class (the asm.GetName.Name part). By default this is fine, because assembly name and root namespace is the same, but if you look in project properties you can see these names can be set independently. Root namespace may even be cleared. In case the root namespace or assembly name was changed to different names the code would break not finding the generated "rootNamespace.My.MySettings" type. The only way around that I can think of is to loop all types from the Assembly and check if any of them has name "MySettings" and is derived from ApplicationSettingsBase and verify the My part of namespace, in other words make a qualified guess. For example like this:
VB.NET:
Private Function GuessSettings(ByVal asm As Reflection.Assembly) As Type
    For Each t In asm.GetTypes
        If t.Name = "MySettings" _
        AndAlso GetType(ApplicationSettingsBase).IsAssignableFrom(t) _
        AndAlso (t.Namespace = "My" OrElse t.Namespace.EndsWith(".My")) Then
            Return t
        End If
    Next
    Return Nothing
End Function
One thing I found out during debugging was that changing the project namespace required closing the designer for the form containing the component and rebuild, if not the type resolution seemed to get out of sync.
 
Back
Top