开发者

Dispose of WPF Window

开发者 https://www.devze.com 2023-01-28 18:25 出处:网络
I have a WPF Window that I show with Window.Show(). When I click X the form closes. But it is still in memory and the GC never comes and cleans it up.

I have a WPF Window that I show with Window.Show(). When I click X the form closes. But it is still in memory and the GC never comes and cleans it up. Also I do not have any refrences/handles to it.

does any one know what could be causing it to stay in memory?

Thanks!

Update: I am using the following code to create the window:

   Public Sub OpenPageWindow(ByVal nick As String)
        Dim found As Boolean

        For Each pw As PageWindow In Application.Current.Windows.OfType(Of PageWindow)()
            If Not pw.Tag Is Nothing Then
                If pw.Tag.ToString() = nick Then
                    If pw.IsVisible = False Then
                        pw.Show()
                    End If
                    Exit Sub
                End If
            End If
        Next

        If found = False Then
            Dim p As New PageWindow With {.Name = "pw" & nick, .Tag = nick, _
                                          .Title = "Chatting with " & nick, .ShowActivated = False}
            p.Show()
        End If
    End Sub

and the following code-behind for the window itself:

Public Class PageWindow
    Implements System.IDisposable

    Public UserPressedExit As Boolean
    Dim MainWin As MainWindow

    Private _IsBuddy As Boolean
    Private _IsBlocked As Boolean
    Private _IsOnline As Boolean

    Public Property FirstOpen As Boolean

    Public Property IsOnline As Boolean
        Get
            Return _IsOnline
        End Get
        Set(ByVal value As Boolean)
            If _IsOnline <> value Then
                _IsOnline = value
            End If
        End Set
    End Property

    Public Property IsBuddy As Boolean
        Get
            Return _IsBuddy
        End Get
        Set(ByVal value As Boolean)
            If _IsBuddy <> value Then
                _IsBuddy = value
                'If value Then
                '    btnAddBuddy.IsEnabled = False
                '    iDeleteBuddy.IsEnabled = True
                'Else
                '    btnAddBuddy.IsEnabled = True
                '    iDeleteBuddy.IsEnabled = False
                'End If
            End If
        End Set
    End Property

    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        Try
            MainWin = CType(Application.Current.Windows(0), MainWindow)
        Catch ex As Exception
            txtChat.AppendText("ERROR: " & ex.Message.ToString() & Environment.NewLine)
        End Try
    End Sub

    Private Sub Window_Closed(ByVal sender As System.Object, ByVal e As System.EventArgs)
        Me.Dispose()
    End Sub

    Private Sub txtTitle_PreviewMouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs)
        Me.DragMove()
    End Sub

    Private Sub pbMin_PreviewMouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs)
        WindowState = Windows.WindowState.Minimized
    End Sub

    Private Sub pbClose_PreviewMouseUp(ByVal sender As System.Object, ByVal e As System.Windows.Input.MouseButtonEventArgs)
        UserPressedExit = True
        txtChat.Clear()
        Me.Close()
    End Sub

    Private Sub txtSend_KeyUp(ByVal sender As System.Object, ByVal e As System.Windows.Input.KeyEventArgs)
        If e.Key = Key.Enter Then
            btnSend_Click(Nothing, Nothing)
        End If
    End Sub

    Private Sub btnSend_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
        If String.IsNullOrWhiteSpace(txtSend.Text) Then
            MessageBox.Show("Empty Message.")
            txtSend.Text = ""
        Else
            Send(txtSend.Text)

            txtSend.Text = ""
        End If
    End Sub

    Public 开发者_StackOverflowSub Page(ByVal message As String)
        If String.IsNullOrEmpty(message) = False Then
            If MainWin.Settings.ShowPageTimestamp Then
                txtChat.AppendText(Me.Tag.ToString() & " (" & Format(Date.Now, "HH:mm:ss") & "): " & message & Environment.NewLine)
            Else
                txtChat.AppendText(Me.Tag.ToString() & ": " & message & Environment.NewLine)
            End If

            If Me.IsFocused = False Then

            End If
        End If
    End Sub

    Private Sub Send(ByVal text As String)
        'work on 389 event
        MainWin.Send("PAGE " + Me.Tag.ToString + " " + text)

        If MainWin.Settings.ShowPageTimestamp Then
            txtChat.AppendText(MainWin.Settings.Nick & ": " & text & Environment.NewLine)
        Else
            txtChat.AppendText(MainWin.Settings.Nick & " (" & Format(Date.Now, "HH:mm:ss") & "): " & text & Environment.NewLine)
        End If
    End Sub

#Region "IDisposable Support"
    Private disposed As Boolean ' To detect redundant calls

    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposed Then
            If disposing Then
                'dispose managed state (managed objects).
                RemoveHandler Me.Loaded, AddressOf Window_Loaded
                RemoveHandler txtTitle.PreviewMouseDown, AddressOf txtTitle_PreviewMouseDown
                RemoveHandler pbMin.PreviewMouseUp, AddressOf pbMin_PreviewMouseUp
                RemoveHandler pbClose.PreviewMouseUp, AddressOf pbClose_PreviewMouseUp
                RemoveHandler txtSend.KeyUp, AddressOf txtSend_KeyUp
                RemoveHandler btnSend.Click, AddressOf btnSend_Click
                RemoveHandler Me.Closed, AddressOf Window_Closed

                BindingOperations.ClearBinding(txtTitle, TextBlock.TextProperty)

                Me.Icon = Nothing
                ibBackground = Nothing
                'iDeleteBuddy = Nothing
                'btnAddBuddy = Nothing
                'iBlockUser = Nothing
                pbMin = Nothing
                pbClose = Nothing
                MainWin = Nothing

                Me.Resources.Clear()

                GC.Collect()
            End If
        End If
        Me.disposed = True
    End Sub

    Protected Overrides Sub Finalize()
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(False)
        MyBase.Finalize()
    End Sub
#End Region
End Class

Then the following code for the XAML:

<Window x:Class="PageWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="PageWindow" Height="300" Width="400"
        Icon="Images\R2.ico" WindowStyle="None" ResizeMode="NoResize"
        WindowStartupLocation="Manual" Loaded="Window_Loaded"
        BorderThickness="2" BorderBrush="#FFAE28"
        Closed="Window_Closed">
    <Window.Resources>
        <Style x:Key="sRenegadeButton" TargetType="Button">
            <Setter Property="FontFamily" Value="Franklin Gothic Medium" />
            <Setter Property="FontSize" Value="12px" />
            <Setter Property="FontWeight" Value="Bold" />
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Foreground" Value="#FFAE28" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Border Name="Border" BorderBrush="#FFAE28" BorderThickness="1" CornerRadius="3" 
                                Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
                            <Border Name="innerborder" BorderBrush="#FFAE28" BorderThickness="1" CornerRadius="3" 
                                    Background="{TemplateBinding Background}" SnapsToDevicePixels="True" Margin="1">
                                <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Name="content" />
                            </Border>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter TargetName="Border" Property="BorderBrush" Value="#FFD528" />
                                <Setter TargetName="innerborder" Property="BorderBrush" Value="#FFD528" />
                                <Setter Property="Background" Value="#28FFAE28" />
                                <Setter Property="Foreground" Value="#FFD528" />
                            </Trigger>
                            <Trigger Property="IsPressed" Value="True">
                                <Setter Property="Background" Value="#28FFAE28" />
                                <Setter TargetName="content" Property="RenderTransform" >
                                    <Setter.Value>
                                        <TranslateTransform Y="1.0" />
                                    </Setter.Value>
                                </Setter>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter TargetName="Border" Property="BorderBrush" Value="#80E6A023" />
                                <Setter TargetName="innerborder" Property="BorderBrush" Value="#80E6A023" />
                                <Setter Property="Foreground" Value="#8CFFD528" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        <Style x:Key="sRenegadeTextBox" TargetType="TextBox">
            <Setter Property="FontFamily" Value="Franklin Gothic Medium" />
            <Setter Property="FontSize" Value="12px" />
            <Setter Property="Foreground" Value="#FFD528" />
            <Setter Property="Background" Value="#28FFAE28" />
            <Setter Property="BorderBrush" Value="#FFAE28" />
            <Setter Property="CaretBrush" Value="#FFAE28" />
            <Setter Property="SelectionBrush" Value="#FFD528" />
            <Setter Property="BorderThickness" Value="1" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TextBox">
                        <Border Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" 
                                BorderBrush="{TemplateBinding BorderBrush}" 
                                Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
                            <ScrollViewer Name="PART_ContentHost" Background="{TemplateBinding Background}" 
                                          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Property="Foreground" Value="#8CFFD528" />
                                <Setter Property="Background" Value="#1EFFAE28" />
                                <Setter Property="BorderBrush" Value="#80E6A023" />
                                <Setter TargetName="PART_ContentHost" Property="Background" Value="#1EFFAE28"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="21" />
            <RowDefinition Height="32" />
            <RowDefinition Height="*" />
            <RowDefinition Height="27" />
        </Grid.RowDefinitions>

        <Grid.Background>
            <ImageBrush x:Name="ibBackground"  ImageSource="Images\page_back.gif" />
        </Grid.Background>

        <Border Grid.Row="0" BorderBrush="#FFAE28" BorderThickness="0,0,0,2" 
                SnapsToDevicePixels="True" />

        <Grid Name="gTitleBar" Grid.Row="0" Background="#32FFAE28">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="30" />
                <ColumnDefinition Width="32" />
            </Grid.ColumnDefinitions>

            <TextBlock Name="txtTitle" Grid.Column="0" Text="{Binding Title,RelativeSource={RelativeSource FindAncestor,AncestorType=Window}}"
                       HorizontalAlignment="Stretch" VerticalAlignment="Center"
                       TextAlignment="Left" Margin="4,0,1,0" 
                       PreviewMouseDown="txtTitle_PreviewMouseDown"
                       Foreground="#FED528" FontFamily="Franklin Gothic Medium" />

            <Image Name="pbMin" Grid.Column="1" Width="28" Height="15" 
                    HorizontalAlignment="Center" VerticalAlignment="Center"
                    PreviewMouseUp="pbMin_PreviewMouseUp" Stretch="None">
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Setter Property="Source" Value="Images\min.png" />

                        <Style.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Source" Value="Images\min_o.png" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
            <Image Name="pbClose" Grid.Column="2" Width="28" Height="15" 
                    HorizontalAlignment="Left" VerticalAlignment="Center"
                    PreviewMouseUp="pbClose_PreviewMouseUp" Stretch="None">
                <Image.Style>
                    <Style TargetType="{x:Type Image}">
                        <Setter Property="Source" Value="Images\close2.png" />

                        <Style.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Source" Value="Images\close2_o.png" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Image.Style>
            </Image>
        </Grid>

        <Grid Grid.Row="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="25" />
                <ColumnDefinition Width="25" />
                <ColumnDefinition Width="25" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>

            <!--<Button Name="btnAddBuddy" Grid.Column="1" Margin="0,2,0,0">
                <Button.Style>
                    <Style TargetType="{x:Type Button}">
                        <Setter Property="Background" Value="Transparent" />
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type Button}">
                                    <Grid>
                                        <Border Name="Border" BorderBrush="Transparent" BorderThickness="1" CornerRadius="3" 
                                            Background="{TemplateBinding Background}" SnapsToDevicePixels="True">

                                        </Border>
                                        <Image Name="iAddBuddy" Margin="1" Source="Images\Add Buddy 32x36.png" />
                                        <Image Name="iAddBuddyDissabled" Margin="1" Source="Images\Add Buddy 32x36.png" Visibility="Hidden" />
                                    </Grid>

                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter TargetName="Border" Property="BorderBrush" Value="#FFD528" />
                                        </Trigger>
                                        <Trigger Property="IsPressed" Value="True">
                                            <Setter Property="Background" Value="#FFD528" />
                                        </Trigger>
                                        <Trigger Property="IsEnabled" Value="False">
                                            <Setter TargetName="Border" Property="BorderBrush" Value="Transparent" />
                                            <Setter TargetName="iAddBuddy" Property="Visibility" Value="Hidden" />
                                            <Setter TargetName="iAddBuddyDissabled" Property="Visibility" Value="Visible" />
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </Button.Style>
            </Button>

            <Border Name="bDeleteBuddyBorder" Grid.Column="2" BorderThickness="1"
                    SnapsToDevicePixels="True" Margin="0,2,0,0">
                <Image Name="iDeleteBuddy" Margin="1" IsEnabled="True"
                   Source="Images\Delete 32x32.png">
                    <Image.Style>
                        <Style TargetType="{x:Type Image}">
                            <Style.Triggers>
                                <Trigger Property="IsEnabled" Value="False">
                                    <Setter Property="Source" Value="Images\Delete Disabled 32x32.png" />
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Image.Style>
                </Image>
                <Border.Style>
                    <Style TargetType="Border">
                        <Setter Property="BorderBrush" Value="Transparent" />
                        <Setter Property="Background" Value="Transparent" />
                        <Style.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="BorderBrush" Value="#FFD528" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>

            <Border Name="bBlockUserBorder" Grid.Column="3" BorderThickness="1"
                    SnapsToDevicePixels="True" Margin="0,2,0,0">
                <Image Name="iBlockUser" Margin="1" IsEnabled="True"
                       Source="Images\Block Buddy 32x36.png">
                    <Image.Style>
                        <Style TargetType="{x:Type Image}">
                            <Style.Triggers>
                                <Trigger Property="IsEnabled" Value="False">
                                    <Setter Property="Source" Value="Images\Block Buddy Dissabled 32x36.png" />
                                </Trigger>
                            </Style.Triggers>
                        </Style>
                    </Image.Style>
                </Image>
                <Border.Style>
                    <Style TargetType="Border">
                        <Setter Property="BorderBrush" Value="Transparent" />
                        <Setter Property="Background" Value="Transparent" />
                        <Style.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="BorderBrush" Value="#FFD528" />
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>-->
        </Grid>

        <TextBox Name="txtChat" Grid.Row="2" Margin="4,2,4,2"
                 Style="{StaticResource sRenegadeTextBox}" UndoLimit="0" />

        <Grid Grid.Row="3">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="45" />
            </Grid.ColumnDefinitions>

            <TextBox Name="txtSend" Grid.Column="0" MaxLength="495" 
                     Height="22" Margin="4,1,2,4" VerticalContentAlignment="Center" 
                     Style="{StaticResource sRenegadeTextBox}" UndoLimit="0"
                     KeyUp="txtSend_KeyUp" />
            <Button Name="btnSend" Content="Send" Grid.Column="1" 
                    Style="{StaticResource sRenegadeButton}" 
                    Height="22" Width="40" Margin="0,1,4,4" Click="btnSend_Click" />
        </Grid>
    </Grid>
</Window>

Basically even if I create an empty WPF Window with a background image for the main grid and 2 eventhandlers Opening and Closed it still stays in memory, but when all the 'empty wpf' windows are closed the memory only decreases a little. just a little. Why arent simple windows disposed of entirely? I have even tried implementing the disposed functionality coded above.


First of all, ensure that your memory issue is what you think it is (i.e. not getting cleaned up) by using a real memory tool such as redgate's memory profiler, which has a free trial (http://www.red-gate.com/products/ants_memory_profiler/).

Then if you are still getting the issue, the profiler will highlight what is telling the GC not to clean it up (references by other objects and such).

From a quick guess, I would say that it could be the event handlers that you have wired up, try un-subscribing them once they are finished.

Also, as stated elsewhere, call GC.Collect as a further check


What is as important as the memory is if you are leaking handles - in processes (in taskmanager) add a column for GDI objects and Handles.

When you open and close your window it will increase and decrease - if not you have a leak - with the leak comes some memory. I would'n worry too much about some kb's not dissapearing right away they will eventually be cleaned up or reused, but handles have to dissapear so they can be a good indication.

Usually leaks arise when using static eventhandlers or other static stuctures that holds a reference to a control or window - i.e. static dictionaries or hashtables. If the reference is needed, remember to clean it up on close or use WeakReference.

Dispose methods are only needed when you have resources as member variables on your window/class and it have to be implemented right (yours does look right - but unnesesary :) - you also have to make sure that Dispose() is actually called...


does any one know what could be causing it to stay in memory?

Does your window listen to any external events? If so, using a weak event manager might solve your problem: http://msdn.microsoft.com/en-us/library/aa970850.aspx.


Are you absolutely sure that the runtime is guaranteed to perform garbage collection in such a scenario?

I think it's quite possible that the GC doesn't see the need to perform garbage collection in this case. Make sure by calling GC.Collect in your code and see if this changes things.

0

精彩评论

暂无评论...
验证码 换一张
取 消