如何在WPF中使用一次性视图模型?

发布于 2024-11-28 14:44:15 字数 123 浏览 1 评论 0原文

如果视图模型引用非托管资源或具有事件处理程序(例如调度程序计时器上的处理已过期),如何确保视图模型得到正确处理。在第一种情况下,终结器是一种选择,虽然并不理想,但在后者中,它永远不会被调用。我们如何判断何时不再有视图附加到视图模型。

How do I ensure view models are properly disposed of if they reference unmanaged resources or have event handlers such as handling elapsed on a dispatcher timer. In the first case, a finaliser is an option, although not ideal, but in the latter, it will never be called. How can we tell when there is no longer a view attached to the view model.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

木緿 2024-12-05 14:44:15

我通过执行以下操作来完成此操作:

  1. 从 App.xaml 中删除 StartupUri 属性。
  2. 定义我的应用程序类如下:

    公共部分类App:应用程序
    {
        公共应用程序()
        {
            IDisposable 一次性ViewModel = null;
    
            //创建并显示窗口,同时存储数据上下文
            this.Startup += (sender, args) =>;
            {
                主窗口=新的主窗口();
                disposableViewModel = MainWindow.DataContext 作为 IDisposable;
    
                MainWindow.Show();
            };
    
            //处理未处理的异常
            this.DispatcherUnhandledException += (sender, args) =>; 
            { 
                如果(disposableViewModel!= null)disposableViewModel.Dispose(); 
            };
    
            //退出时处理
            this.Exit += (sender, args) =>
            { 
                如果(disposableViewModel!= null)disposableViewModel.Dispose(); 
            };
        }
    }
    

I accomplished this by doing the following:

  1. Removing the StartupUri property from App.xaml.
  2. Defining my App class as follows:

    public partial class App : Application
    {
        public App()
        {
            IDisposable disposableViewModel = null;
    
            //Create and show window while storing datacontext
            this.Startup += (sender, args) =>
            {
                MainWindow = new MainWindow();
                disposableViewModel = MainWindow.DataContext as IDisposable;
    
                MainWindow.Show();
            };
    
            //Dispose on unhandled exception
            this.DispatcherUnhandledException += (sender, args) => 
            { 
                if (disposableViewModel != null) disposableViewModel.Dispose(); 
            };
    
            //Dispose on exit
            this.Exit += (sender, args) =>
            { 
                if (disposableViewModel != null) disposableViewModel.Dispose(); 
            };
        }
    }
    
静谧幽蓝 2024-12-05 14:44:15

一种可能但不是完美的解决方案:

在视图模型上实现 IDisposable,然后在视图的构造函数中使用此扩展方法。

    public static void HandleDisposableViewModel(this FrameworkElement Element)
    {
        Action Dispose = () =>
            {
                var DataContext = Element.DataContext as IDisposable;
                if (DataContext != null)
                {
                    DataContext.Dispose();
                }
            };
        Element.Unloaded += (s, ea) => Dispose();
        Element.Dispatcher.ShutdownStarted += (s, ea) => Dispose();
    }

One possible, although not perfect solution:

Implement IDisposable on the View Model, then use this extension method in the constructor of the view.

    public static void HandleDisposableViewModel(this FrameworkElement Element)
    {
        Action Dispose = () =>
            {
                var DataContext = Element.DataContext as IDisposable;
                if (DataContext != null)
                {
                    DataContext.Dispose();
                }
            };
        Element.Unloaded += (s, ea) => Dispose();
        Element.Dispatcher.ShutdownStarted += (s, ea) => Dispose();
    }
泼猴你往哪里跑 2024-12-05 14:44:15

您应该在视图卸载或应用程序关闭时处置视图模型。 现有答案采用这种方法,但不处理卸载后再次加载视图的情况。如果这是您需要处理的场景,那么您还需要在重新加载视图时重新创建数据上下文。

public partial class CustomControl : UserControl
{
    public CustomControl()
    {
        InitializeComponent();
        
        // Initialize the view model
        SetViewModel();

        // Create/Dispose the view model as this view is loaded/unloaded
        Loaded += (_, _) => SetViewModel();
        Unloaded += (_, _) => DisposeViewModel();

        // Dispose the view model when the application is shutting down
        Dispatcher.ShutdownStarted += (_, _) => DisposeViewModel();
    }

    // Use whatever logic here that you need to create the view model
    private void SetViewModel() =>
        DataContext ??= new CustomControlViewModel();

    public void DisposeViewModel()
    {
        // Nothing to do if there is no view model to dispose
        if (DataContext is not IDisposable viewModel) return;

        // Remove and dispose the view model
        // NOTE: Make sure to set DataContext to null before disposing so 
        // that the view doesn't have a chance to access a disposed object
        DataContext = null;
        viewModel.Dispose();
    }
}

如果您经常使用此模式,它也可以轻松地移动到您可以从视图构造函数调用的扩展方法中:

public static void UseDisposableViewModel(this FrameworkElement view, Func<IDisposable> viewModelFactory)
{
    SetViewModel();
    view.Loaded += (_, _) => SetViewModel();
    view.Unloaded += (_, _) => DisposeViewModel();
    view.Dispatcher.ShutdownStarted += (_, _) => DisposeViewModel();

    void SetViewModel() =>
        view.DataContext ??= viewModelFactory();

    void DisposeViewModel()
    {
        if (view.DataContext is not IDisposable viewModel) return;
        view.DataContext = null;
        viewModel.Dispose();
    }
}

You should dispose the view model when the view is Unloaded or when the application is shutting down. The existing answer takes this approach, but doesn't handle the case where the view is loaded again after being unloaded. If that is a scenario you need to handle, you will also need to re-create the data context when the view is re-loaded.

public partial class CustomControl : UserControl
{
    public CustomControl()
    {
        InitializeComponent();
        
        // Initialize the view model
        SetViewModel();

        // Create/Dispose the view model as this view is loaded/unloaded
        Loaded += (_, _) => SetViewModel();
        Unloaded += (_, _) => DisposeViewModel();

        // Dispose the view model when the application is shutting down
        Dispatcher.ShutdownStarted += (_, _) => DisposeViewModel();
    }

    // Use whatever logic here that you need to create the view model
    private void SetViewModel() =>
        DataContext ??= new CustomControlViewModel();

    public void DisposeViewModel()
    {
        // Nothing to do if there is no view model to dispose
        if (DataContext is not IDisposable viewModel) return;

        // Remove and dispose the view model
        // NOTE: Make sure to set DataContext to null before disposing so 
        // that the view doesn't have a chance to access a disposed object
        DataContext = null;
        viewModel.Dispose();
    }
}

If you use this pattern a lot, it could also be easily moved into an extension method that you can call from the view constructor:

public static void UseDisposableViewModel(this FrameworkElement view, Func<IDisposable> viewModelFactory)
{
    SetViewModel();
    view.Loaded += (_, _) => SetViewModel();
    view.Unloaded += (_, _) => DisposeViewModel();
    view.Dispatcher.ShutdownStarted += (_, _) => DisposeViewModel();

    void SetViewModel() =>
        view.DataContext ??= viewModelFactory();

    void DisposeViewModel()
    {
        if (view.DataContext is not IDisposable viewModel) return;
        view.DataContext = null;
        viewModel.Dispose();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文