using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace ImageCatalog_2 { public class ViewModelBase : INotifyPropertyChanged { private readonly SynchronizationContext? _synchronizationContext; private Control? _control; protected ViewModelBase() { // Capture the synchronization context (UI thread context) _synchronizationContext = SynchronizationContext.Current; } /// /// Set a Control to use for thread marshalling in WinForms applications. /// This is required for proper cross-thread handling with data binding. /// public void SetControl(Control control) { _control = control; } public event PropertyChangedEventHandler? PropertyChanged; // This method is called by the Set accessor of each property. // The CallerMemberName attribute that is applied to the optional propertyName // parameter causes the property name of the caller to be substituted as an argument. protected void NotifyPropertyChanged([CallerMemberName] String propertyName = "") { if (PropertyChanged == null) return; // If we have a Control reference (WinForms), use Control.Invoke for proper marshalling if (_control != null) { if (_control.InvokeRequired) { _control.Invoke(() => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName))); } else { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } // Fallback to SynchronizationContext if available else if (_synchronizationContext != null && SynchronizationContext.Current != _synchronizationContext) { // We're on a different thread, marshal to the UI thread _synchronizationContext.Send(_ => { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }, null); } else { // We're already on the UI thread or no sync context available PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } }