Results 1 to 7 of 7

Thread: Change User Control associated within a panel from another User Control

  1. #1

    Thread Starter
    Junior Member
    Join Date
    Jul 2012
    Posts
    17

    Change User Control associated within a panel from another User Control

    In my app, I have three panels that make up the main screen. A header panel, a Navigation panel and a Main display panel.

    When user clicks on the navigation button in nav panel, I use the following code to display the proper user control in main panel

    Dim tempobject as new [usercontrol]
    pnlMain.Controls.Clear()
    pnlMain.Controls.Add(tempobject)

    If I were to have a user control that contained sub menu items from a button in the nav panel,how would I call a new UC to the pnlMain if I am already in a UC in pnlMain?

    Also, how would I create a generic function to accept a usercontrol (one that I want to display in pnlMain) as a parameter and display in pnlMain?

    Something like this?

    Public Sub GetUserControl(uc as System.Windows.Forms.Control)
    Dim tempobject as new uc
    pnlMain.Controls.Clear()
    pnlMain.Controls.Add(tempobject)

    That code isnt working, Getting error "value of type 'uc' cannot be converted to 'System.Windows.Forms.Control'

  2. #2
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: Change User Control associated within a panel from another User Control

    The New keyword means to create a new object of the given type by calling a constructor. But "uc" is a parameter to your Sub. So when you write this line:
    Code:
    Dim tempobject As New uc()
    I'm surprised you aren't being told something like "Unknown type: 'uc'". It implies you probably have some class named "uc" in your program.

    So what your sub is doing, as written, is creating a new instance of some class called 'uc', and trying to add it to pnlMain's Controls collection. But whatever 'uc' is, it isn't a Control, so that attempt fails. What I'd expect this method to look like is:
    Code:
    Public Sub GetUserControl(uc as System.Windows.Forms.Control)
        pnlMain.Controls.Clear()
        pnlMain.Controls.Add(uc)
    End Sub
    With respect to your first question, that's a question of application architecture. You could have your UserControls know that their parent is pnlMain, and that IT's parent is some form, and via casts and Parent properties make the appropriate calls. I don't like that.

    In this style of application, since you have a concept of navigation, why not make a Navigation class that knows about pnlMain and is referenced by your UserControls. That way, when they want to update the control in pnlMain, they talk to the Navigation class. Here's about as quick and dirty as that gets:
    Code:
    Public Class Form1
    
        Private _pnlMain As New Panel()
        Private _navigation As Navigation
    
        Public Sub New()
            InitializeComponent()
    
            _pnlMain = New Panel()
            _pnlMain.Dock = DockStyle.Fill
            Controls.Add(_pnlMain)
    
            _navigation = New Navigation(_pnlMain)
    
            Dim startControl = New FirstUserControl(_navigation)
            _navigation.SetNewControl(startControl)
        End Sub
    
    End Class
    
    Public Class FirstUserControl
        Inherits UserControl
    
        Private _navigation As Navigation
    
        Public Sub New(ByVal navigation As Navigation)
            _navigation = navigation
    
            BackColor = Color.Crimson
            Dock = DockStyle.Fill
    
            Dim btn As New Button() With {.Text = "1"}
            AddHandler btn.Click, AddressOf HandleClick
            Me.Controls.Add(btn)
        End Sub
    
        Private Sub HandleClick(sender As Object, e As EventArgs)
            _navigation.SetNewControl(New SecondUserControl(_navigation))
        End Sub
    End Class
    
    Public Class SecondUserControl
        Inherits UserControl
    
        Private _navigation As Navigation
    
        Public Sub New(ByVal navigation As Navigation)
            _navigation = navigation
    
            BackColor = Color.Azure
            Dock = DockStyle.Fill
    
            Dim btn As New Button() With {.Text = "2"}
            AddHandler btn.Click, AddressOf HandleClick
            Me.Controls.Add(btn)
        End Sub
    
        Private Sub HandleClick(sender As Object, e As EventArgs)
            _navigation.SetNewControl(New FirstUserControl(_navigation))
        End Sub
    End Class
    
    Public Class Navigation
    
        Private _container As Panel
    
        Public Sub New(ByVal container As Panel)
            _container = container
        End Sub
    
        Public Sub SetNewControl(ByVal uc As UserControl)
            _container.Controls.Clear()
            _container.Controls.Add(uc)
        End Sub
    End Class
    There's a lot going on, but walk through it. The form creates a Navigation instance after it creates its Panel, and that class is responsible for updating the Panel's children. Each UserControl expects to get that Navigation instance, and will use it to create the next UI when its button is clicked. And it doesn't matter if it's a sub-menu, or even a child control: so long as what you're working with has a reference to that one Navigation object, it can update the UI.

    There's a lot of features you could add, but this should be a nudge in the right direction.

    (You also probably want to Dispose() of controls instead of just calling Clear() on the panel, but the timing on that's tricky.)
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  3. #3

    Thread Starter
    Junior Member
    Join Date
    Jul 2012
    Posts
    17

    Re: Change User Control associated within a panel from another User Control

    Quote Originally Posted by Sitten Spynne View Post
    The New keyword means to create a new object of the given type by calling a constructor. But "uc" is a parameter to your Sub. So when you write this line:
    Code:
    Dim tempobject As New uc()
    I'm surprised you aren't being told something like "Unknown type: 'uc'". It implies you probably have some class named "uc" in your program.

    So what your sub is doing, as written, is creating a new instance of some class called 'uc', and trying to add it to pnlMain's Controls collection. But whatever 'uc' is, it isn't a Control, so that attempt fails. What I'd expect this method to look like is:
    Code:
    Public Sub GetUserControl(uc as System.Windows.Forms.Control)
        pnlMain.Controls.Clear()
        pnlMain.Controls.Add(uc)
    End Sub
    With respect to your first question, that's a question of application architecture. You could have your UserControls know that their parent is pnlMain, and that IT's parent is some form, and via casts and Parent properties make the appropriate calls. I don't like that.

    In this style of application, since you have a concept of navigation, why not make a Navigation class that knows about pnlMain and is referenced by your UserControls. That way, when they want to update the control in pnlMain, they talk to the Navigation class. Here's about as quick and dirty as that gets:
    Code:
    Public Class Form1
    
        Private _pnlMain As New Panel()
        Private _navigation As Navigation
    
        Public Sub New()
            InitializeComponent()
    
            _pnlMain = New Panel()
            _pnlMain.Dock = DockStyle.Fill
            Controls.Add(_pnlMain)
    
            _navigation = New Navigation(_pnlMain)
    
            Dim startControl = New FirstUserControl(_navigation)
            _navigation.SetNewControl(startControl)
        End Sub
    
    End Class
    
    Public Class FirstUserControl
        Inherits UserControl
    
        Private _navigation As Navigation
    
        Public Sub New(ByVal navigation As Navigation)
            _navigation = navigation
    
            BackColor = Color.Crimson
            Dock = DockStyle.Fill
    
            Dim btn As New Button() With {.Text = "1"}
            AddHandler btn.Click, AddressOf HandleClick
            Me.Controls.Add(btn)
        End Sub
    
        Private Sub HandleClick(sender As Object, e As EventArgs)
            _navigation.SetNewControl(New SecondUserControl(_navigation))
        End Sub
    End Class
    
    Public Class SecondUserControl
        Inherits UserControl
    
        Private _navigation As Navigation
    
        Public Sub New(ByVal navigation As Navigation)
            _navigation = navigation
    
            BackColor = Color.Azure
            Dock = DockStyle.Fill
    
            Dim btn As New Button() With {.Text = "2"}
            AddHandler btn.Click, AddressOf HandleClick
            Me.Controls.Add(btn)
        End Sub
    
        Private Sub HandleClick(sender As Object, e As EventArgs)
            _navigation.SetNewControl(New FirstUserControl(_navigation))
        End Sub
    End Class
    
    Public Class Navigation
    
        Private _container As Panel
    
        Public Sub New(ByVal container As Panel)
            _container = container
        End Sub
    
        Public Sub SetNewControl(ByVal uc As UserControl)
            _container.Controls.Clear()
            _container.Controls.Add(uc)
        End Sub
    End Class
    There's a lot going on, but walk through it. The form creates a Navigation instance after it creates its Panel, and that class is responsible for updating the Panel's children. Each UserControl expects to get that Navigation instance, and will use it to create the next UI when its button is clicked. And it doesn't matter if it's a sub-menu, or even a child control: so long as what you're working with has a reference to that one Navigation object, it can update the UI.

    There's a lot of features you could add, but this should be a nudge in the right direction.

    (You also probably want to Dispose() of controls instead of just calling Clear() on the panel, but the timing on that's tricky.)
    So in your example, the "Form" would be my main form that has the panels on it, and the FirstUserControl, SecondUserControl, etc are the user controls that would be shown in pnlMain?

  4. #4
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: Change User Control associated within a panel from another User Control

    Yep. It's just organized a little weird because no one would toss them all in one file like this, and I'm not using the designer at all. That wasn't for a technical reason, it's just easier to post that kind of project on the forums.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  5. #5

    Thread Starter
    Junior Member
    Join Date
    Jul 2012
    Posts
    17

    Re: Change User Control associated within a panel from another User Control

    Quote Originally Posted by Sitten Spynne View Post
    Yep. It's just organized a little weird because no one would toss them all in one file like this, and I'm not using the designer at all. That wasn't for a technical reason, it's just easier to post that kind of project on the forums.
    I'll take a stab at implementation and let you know how it goes

  6. #6

    Thread Starter
    Junior Member
    Join Date
    Jul 2012
    Posts
    17

    Re: Change User Control associated within a panel from another User Control

    I put the code in a new project and was able to see how the code works. Pretty sweet. My question is, if I want to have my panels outlines as follows:



    Would I just create the panel on frmMain with dimensions on New Panel()? Same with navigation when adding buttons? Could the panels and navigation buttons be done at design time and i reference when showing in pnlMain? My second level of navigation buttons would be in a UC in pnlMain

  7. #7
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: Change User Control associated within a panel from another User Control

    You don't have to make the Panel yourself like I did, you can use the designer if you wish, and that's usually an easier way to get it sized right. But I do like Sub New() for setting up other things. There's just one trick to using it with the designer.

    If you type out Sub New(), Visual Studio is likely to replace it with this:
    Code:
    Public Sub New()
      ' Some really long comment
      InitializeComponent()
    
      ' Another really long comment
    End Sub
    InitializeComponent() is where all your designer controls get made. So if you put your code before that, all your controls will be Nothing at the time and you'll be sad to get NullReferenceExceptions. The solution's easy: if you want to do stuff in Sub New(), make sure it goes after InitializeComponent() so all your designer controls are set up.

    You could also use the Load or Shown events, but I prefer to put things in the constructor.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width