Skip to content

Commit 868195f

Browse files
committed
fix: implement IDisposable for TabInfo
1 parent a44ae0a commit 868195f

File tree

2 files changed

+109
-1
lines changed

2 files changed

+109
-1
lines changed

MainWindow.xaml.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,9 @@ private void CloseTab(TabInfo tabInfo)
672672
// Remove it from our collection
673673
tabs.Remove(tabInfo);
674674

675+
// Properly dispose of the TabInfo to clean up memory and event handlers
676+
tabInfo.Dispose();
677+
675678
// If there are no more tabs, consider adding a default one
676679
if (tabs.Count == 0 && _currentLogFile != null)
677680
{
@@ -725,6 +728,12 @@ private void LoadProfile(string profilePath)
725728
{
726729
tabControl.Items.Remove(item);
727730
}
731+
732+
// Dispose all existing tabs before clearing
733+
foreach (var tab in tabs)
734+
{
735+
tab.Dispose();
736+
}
728737
tabs.Clear();
729738

730739
// Clear and load auto tab configurations
@@ -758,6 +767,12 @@ private void LoadProfile(string profilePath)
758767
{
759768
tabControl.Items.Remove(item);
760769
}
770+
771+
// Dispose all existing tabs before clearing
772+
foreach (var tab in tabs)
773+
{
774+
tab.Dispose();
775+
}
761776
tabs.Clear();
762777
autoTabConfigs.Clear();
763778
MenuAutoTabs_Click(this, new RoutedEventArgs());
@@ -1875,6 +1890,20 @@ private void MainWindow_Closing(object? sender, System.ComponentModel.CancelEven
18751890
// Stop forced refresh timer if it's running
18761891
StopForcedRefresh();
18771892

1893+
// Dispose all tabs to clean up memory and event handlers
1894+
foreach (var tab in tabs)
1895+
{
1896+
try
1897+
{
1898+
tab.Dispose();
1899+
}
1900+
catch (Exception ex)
1901+
{
1902+
Debug.WriteLine($"Error disposing tab: {ex.Message}");
1903+
}
1904+
}
1905+
tabs.Clear();
1906+
18781907
// Save current application configuration
18791908
try
18801909
{

TabInfo.cs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ namespace X4LogWatcher
1313
/// <summary>
1414
/// Class to hold all information related to a tab in the log watcher
1515
/// </summary>
16-
public class TabInfo : INotifyPropertyChanged
16+
public class TabInfo : INotifyPropertyChanged, IDisposable
1717
{
1818
public event PropertyChangedEventHandler? PropertyChanged;
1919

20+
private bool _disposed = false;
21+
2022
private string _regexPattern = string.Empty;
2123
public string RegexPattern
2224
{
@@ -301,6 +303,9 @@ private void WatchingCheckBox_Unchecked(object sender, RoutedEventArgs e)
301303
/// </summary>
302304
public void AppendContent(string text)
303305
{
306+
if (_disposed)
307+
return;
308+
304309
ContentTextBox.AppendText(text);
305310
ContentTextBox.ScrollToEnd();
306311
}
@@ -310,6 +315,9 @@ public void AppendContent(string text)
310315
/// </summary>
311316
public void ClearContent()
312317
{
318+
if (_disposed)
319+
return;
320+
313321
ContentTextBox.Clear();
314322
}
315323

@@ -330,5 +338,76 @@ private void UpdateNewContentIndicator()
330338
// Just update the tab header text which includes the indicator when HasNewContent is true
331339
UpdateTabHeader();
332340
}
341+
342+
#region IDisposable Implementation
343+
344+
/// <summary>
345+
/// Dispose of resources and unsubscribe from event handlers
346+
/// </summary>
347+
public void Dispose()
348+
{
349+
Dispose(true);
350+
GC.SuppressFinalize(this);
351+
}
352+
353+
/// <summary>
354+
/// Protected dispose method
355+
/// </summary>
356+
/// <param name="disposing">True if disposing from Dispose() method, false if from finalizer</param>
357+
protected virtual void Dispose(bool disposing)
358+
{
359+
if (!_disposed && disposing)
360+
{
361+
try
362+
{
363+
// Unsubscribe from all event handlers to prevent memory leaks
364+
if (RegexTextBox != null)
365+
{
366+
RegexTextBox.TextChanged -= RegexTextBox_TextChanged;
367+
}
368+
369+
if (NameTextBox != null)
370+
{
371+
NameTextBox.TextChanged -= NameTextBox_TextChanged;
372+
}
373+
374+
if (AfterLinesNumericUpDown != null)
375+
{
376+
AfterLinesNumericUpDown.ValueChanged -= AfterLinesNumericUpDown_ValueChanged;
377+
}
378+
379+
if (WatchingCheckBox != null)
380+
{
381+
WatchingCheckBox.Unchecked -= WatchingCheckBox_Unchecked;
382+
}
383+
384+
// Clear property change event handlers
385+
PropertyChanged = null;
386+
387+
// Force garbage collection of large content if present
388+
if (ContentTextBox != null && ContentTextBox.Text.Length > 10_000_000) // 10MB threshold
389+
{
390+
ContentTextBox.Clear();
391+
}
392+
}
393+
catch (Exception ex)
394+
{
395+
// Log but don't throw during disposal
396+
System.Diagnostics.Debug.WriteLine($"Error during TabInfo disposal: {ex.Message}");
397+
}
398+
399+
_disposed = true;
400+
}
401+
}
402+
403+
/// <summary>
404+
/// Finalizer to ensure disposal if Dispose() wasn't called
405+
/// </summary>
406+
~TabInfo()
407+
{
408+
Dispose(false);
409+
}
410+
411+
#endregion
333412
}
334413
}

0 commit comments

Comments
 (0)