@@ -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