1

I Have a Windows Forms Application where I'm trying to use Dependency Injectio for my Forms and some Services. This is the initial configuration in my StartUpModule:

Module StartUpModule
    Sub Main(args As String())
        Dim Host = CreateHostBuilder(args).Build()

        Using ServiceScope = Host.Services.CreateScope()
            Dim Services = ServiceScope.ServiceProvider

            Try
            Dim Form1 = Services.GetRequiredService(GetType(BaseMenu))
            Application.EnableVisualStyles()
                Application.Run(Form1)
            Catch ex As Exception
                Console.WriteLine(ex.Message)
            End Try
        End Using
    End Sub

    Public Function CreateHostBuilder(args As String()) As IHostBuilder
        Return Host.CreateDefaultBuilder(args).ConfigureServices(Function(HostContext, Services) ConfigureServices(Services))
    End Function

    Public Function ConfigureServices(Services As IServiceCollection) As IHostBuilder
   
    'Forms
    Services.AddSingleton(GetType(BaseMenu))
    Services.AddTransient(GetType(Form1))
    Services.AddTransient(GetType(Form2))
    '...
    
    Services.AddTransient(GetType(IUserRepository), GetType(UserRepository))
          
    'Servicos
    Services.AddTransient(GetType(IUserService), GetType(UserService))

    'Controllers
    Services.AddTransient(GetType(UserController))       
End Function
End Module

I need to create an SPA(Single Page Application), so I nest the Child Forms onto the BaseMenu Form. To do this I need to instantiate the Child Forms and I want to use DI for that. For that I pass ServiceProvider to My BaseMenu Form As Follows:

Public Class BaseMenu


Private ReadOnly ServiceProvider As IServiceProvider

Public Sub New(_ServiceProvider As IServiceProvider)

    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.
    ServiceProvider = _ServiceProvider
End Sub

 Private ActiveForm As Form
Public Sub OpenChildForm(ChildForm As Form)
    If ActiveForm IsNot Nothing Then
        If ChildForm.GetType <> ActiveForm.GetType Then
            ActiveForm.Close()
        Else
            Exit Sub
        End If
    End If

    Panel1.SuspendLayout()
    ActiveForm = ChildForm
    AddHandler ActiveForm.FormClosed, AddressOf ChildForm_Closed
    ChildForm.TopLevel = False
    ChildForm.FormBorderStyle = FormBorderStyle.None
    ChildForm.Dock = DockStyle.Fill
    Panel1.Controls.Add(ChildForm)
    Panel1.Tag = ChildForm
    ChildForm.BringToFront()
    ChildForm.Show()
    Panel1.ResumeLayout()
End Sub

Private Sub ChildForm_Closed()
    ActiveForm = Nothing
    GC.Collect()
End Sub

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    OpenChildForm(ServiceProider.GetService(GetType(Form1)))        
End Sub

So far so Good. It opens the forms as intended. The problem I'm facing is when I Close the child Form or open another form in its place. The references to said form aren't being deleted, creating a memory leak. Everytime I reopen the childform a new one gets created but the previous one does not get deleted.

I'm guessing it has something to do with the scope of the App, but I'm not sure how to tackle it.

  • 1
    Troubleshoot by removing DI for the forms. That probably is unrelated. Look for references to the old forms that would keep them from being GC'd. EG this `Panel1.Controls.Add(ChildForm)` looks like a possible cause. – David Browne - Microsoft Mar 14 '22 at 14:02
  • As usual, dispose of all the disposable objects you create. Do not use `[Control].Controls.Clear()`. – Jimi Mar 14 '22 at 14:10
  • I've already tried this code without the DI, it works just fine. It releases the memory correctly, but when using DI it does not. – Tiago Pires Mar 14 '22 at 14:13
  • @Jimi I thought you were not suposed to dispose of Forms as the DI container will handle that itself. – Tiago Pires Mar 14 '22 at 14:24
  • In general, yes, when the Service is configured correctly and owns the resource. You have this: `Services.AddTransient(GetType(Form1))` and also this: `ActiveForm.Close()` (this one calls `Dispose()`). However, I meant whatever your Forms create internally in their life-time. -- Might be useful: [Dependency Injection and IDisposable](https://stackoverflow.com/q/30283564/7444103) – Jimi Mar 14 '22 at 14:55
  • The problem is that objects created via DI will only be garbage collected when the scope is disposed. Since you likely are only using the root scope, nothing is GCed. I'm still trying to figure out the correct way to do this, but right now I am wrapping the child forms in a child scope. – Dan Friedman May 18 '22 at 02:12

0 Answers0