You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET WPF中如何使用MVVM模式实现特定文件变更跟踪?

解决方案:将文件变更事件传递至MVVM层

针对你的场景,有几种简洁的方式可以把文件变更事件传递到MainViewModel,同时保持MVVM的分离原则,以下是具体实现方案:


方案1:直接通过DataContext调用ViewModel方法

这是最直接的实现方式,利用View的DataContext获取ViewModel实例,调用其方法传递状态数据。

步骤1:在MainViewModel中添加状态更新方法

public class MainViewModel : INotifyPropertyChanged
{
    private int _currentAppState;
    public int CurrentAppState
    {
        get => _currentAppState;
        set
        {
            _currentAppState = value;
            OnPropertyChanged();
            // 这里可以添加状态变更后的业务逻辑
        }
    }

    public void UpdateAppState(int newState)
    {
        // FileSystemWatcher事件在非UI线程触发,需切换到UI线程更新属性
        Application.Current.Dispatcher.Invoke(() =>
        {
            CurrentAppState = newState;
        });
    }

    // 实现INotifyPropertyChanged接口
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

步骤2:在MainWindow.xaml.cs的OnChanged方法中调用ViewModel方法

修改现有OnChanged方法,添加ViewModel调用逻辑:

private void OnChanged(object source, FileSystemEventArgs e)
{
    Thread.Sleep(1000);
    var appState = CheckAppState();
    AppState = appState;

    // 获取ViewModel实例并传递状态
    var viewModel = DataContext as MainViewModel;
    viewModel?.UpdateAppState(appState);

    // 原有的托盘图标更新逻辑保持不变
    string workingDirectory = Environment.CurrentDirectory;
    string projectDirectory = Directory.GetParent(workingDirectory).Parent.FullName;
    var path = Path.Combine(projectDirectory, "Assets");

    switch (appState)
    {
        case 1:
            m_notifyIcon.Icon = new System.Drawing.Icon(Path.Combine(path, "48x48_active.ico"));
            break;
        case 2:
            m_notifyIcon.Icon = new System.Drawing.Icon(Path.Combine(path, "48x48_inactive.ico"));
            break;
        case 4:
            m_notifyIcon.Icon = new System.Drawing.Icon(Path.Combine(path, "48x48_running.ico"));
            break;
        default:
            break;
    }
}

方案2:使用事件委托实现解耦

如果想让View和ViewModel进一步解耦,可以通过自定义事件传递状态,避免直接依赖ViewModel类型。

步骤1:在MainWindow中定义事件

public partial class MainWindow : Window
{
    // 定义状态变更事件
    public event Action<int> AppStateUpdated;

    // 原有的CreateFileWatcher、CheckAppState等方法保持不变,仅修改OnChanged中的触发逻辑
    private void OnChanged(object source, FileSystemEventArgs e)
    {
        Thread.Sleep(1000);
        var appState = CheckAppState();
        AppState = appState;

        // 触发事件传递状态
        AppStateUpdated?.Invoke(appState);

        // 原有的托盘图标更新逻辑...
    }
}

步骤2:在MainViewModel中订阅事件

在ViewModel初始化时订阅View的事件:

public class MainViewModel : INotifyPropertyChanged
{
    private int _currentAppState;
    public int CurrentAppState
    {
        get => _currentAppState;
        set
        {
            _currentAppState = value;
            OnPropertyChanged();
        }
    }

    public MainViewModel()
    {
        // 获取主窗口实例并订阅事件
        var mainWindow = Application.Current.MainWindow as MainWindow;
        mainWindow?.AppStateUpdated += OnAppStateUpdated;
    }

    private void OnAppStateUpdated(int newState)
    {
        Application.Current.Dispatcher.Invoke(() =>
        {
            CurrentAppState = newState;
            // 处理状态变更的业务逻辑
        });
    }

    // INotifyPropertyChanged实现...
}

方案3:将文件监听逻辑移至ViewModel(更符合MVVM原则)

如果希望完全遵循MVVM的职责分离,可以把FileSystemWatcher的逻辑移到ViewModel中,再通过事件通知View更新托盘图标。

步骤1:ViewModel中实现文件监听

public class MainViewModel : INotifyPropertyChanged
{
    private FileSystemWatcher _watcher;
    private int _currentAppState;
    public int CurrentAppState
    {
        get => _currentAppState;
        set
        {
            _currentAppState = value;
            OnPropertyChanged();
            // 触发托盘图标更新事件
            AppStateChangedForUI?.Invoke(value);
        }
    }

    // 通知View更新UI的事件
    public event Action<int> AppStateChangedForUI;

    public void CreateFileWatcher(string path)
    {
        _watcher = new FileSystemWatcher();
        _watcher.Path = path;
        _watcher.NotifyFilter = NotifyFilters.LastWrite;
        _watcher.Filter = nameof(AppStateModel);
        _watcher.Changed += OnFileChanged;
        _watcher.EnableRaisingEvents = true;
    }

    private void OnFileChanged(object source, FileSystemEventArgs e)
    {
        Thread.Sleep(1000);
        var appState = CheckAppState();
        Application.Current.Dispatcher.Invoke(() =>
        {
            CurrentAppState = appState;
        });
    }

    private int CheckAppState()
    {
        // 复制原有的CheckAppState逻辑到这里
        try
        {
            var tempFile = Path.Combine(AppWizard.Default.FolderPath, nameof(AppStateModel));
            if (!File.Exists(tempFile))
                return -1;

            using (var stream = new FileStream(tempFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                var appStateModel = JsonConvert.DeserializeObject<AppStateModel>(File.ReadAllText(tempFile));
                if (appStateModel != null)
                {
                    if (appStateModel.CurrentState == AppStates.Ready)
                        return 1;
                    else if (appStateModel.CurrentState == AppStates.Dead || appStateModel.CurrentState == AppStates.Expired)
                        return 2;
                    else if (appStateModel.CurrentState == AppStates.Busy)
                        return 4;
                }
            }
        }
        catch (Exception ex)
        {
            FileLogger.Default.WriteExceptionLog(ex);
        }
        return -1;
    }

    // INotifyPropertyChanged实现...
}

步骤2:View中订阅ViewModel的事件更新托盘

在MainWindow.xaml.cs中订阅ViewModel的事件:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        var viewModel = new MainViewModel();
        DataContext = viewModel;
        // 订阅ViewModel的UI更新事件
        viewModel.AppStateChangedForUI += UpdateTrayIcon;
        // 初始化文件监听
        viewModel.CreateFileWatcher("你的监听路径");
    }

    private void UpdateTrayIcon(int appState)
    {
        string workingDirectory = Environment.CurrentDirectory;
        string projectDirectory = Directory.GetParent(workingDirectory).Parent.FullName;
        var path = Path.Combine(projectDirectory, "Assets");

        switch (appState)
        {
            case 1:
                m_notifyIcon.Icon = new System.Drawing.Icon(Path.Combine(path, "48x48_active.ico"));
                break;
            case 2:
                m_notifyIcon.Icon = new System.Drawing.Icon(Path.Combine(path, "48x48_inactive.ico"));
                break;
            case 4:
                m_notifyIcon.Icon = new System.Drawing.Icon(Path.Combine(path, "48x48_running.ico"));
                break;
            default:
                break;
        }
    }
}

注意事项

  • 线程安全:FileSystemWatcher的事件在后台线程触发,更新UI或ViewModel属性时必须切换到UI线程(使用Dispatcher.Invoke)。
  • 资源释放:窗口关闭或ViewModel销毁时,记得停止FileSystemWatcher并释放资源,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Kaan Çağhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 22:50:23