簡體   English   中英

為什么ButtonBase在測試`ICommand.CanExecute`之前不檢查其可見性?

[英]Why ButtonBase doesn't check its Visibility before testing `ICommand.CanExecute`?

我遇到了一個問題,這讓我大吃一驚。
讓我們從ButtonBase看這些方法:

    private void HookCommand(ICommand command)
    { 
        CanExecuteChangedEventManager.AddHandler(command, OnCanExecuteChanged);
        UpdateCanExecute();
    }

    private void OnCanExecuteChanged(object sender, EventArgs e)
    { 
        UpdateCanExecute(); 
    }

    private void UpdateCanExecute()
    {
        if (Command != null)
        { 
            CanExecute = MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(this);
        } 
        else 
        {
            CanExecute = true; 
        }
    }

將新命令分配給按鈕時,將調用HookCommand 它通過弱事件管理器訂閱CommandManager.RequerySuggested並更新按鈕狀態(啟用/禁用)。

OnCanExecuteChanged只是一個事件處理程序,當您使用與RoutedCommand不同的方法時, UpdateCanExecute最終將調用ICommand.CanExecute 當您使用任何MVVM框架時,就是這種情況。

現在,問題了。
我的數據模板之一應用於ContentControl來顯示一些數據:

<ContentControl Grid.Row="0" Content="{Binding}" ContentTemplate="{StaticResource TemplateState}"/>

該模板包含在另一個ContentControl內的相當復雜的可視樹中,該ContentControl托管在ElementHost (這是WinForms MDI應用程序中的WPF組件)。 該模板內有幾個按鈕,其Command屬性綁定到RelayCommand

當我關閉使用此數據模板渲染的包含視覺效果的MDI子項時,按鈕會嘗試更新其狀態並調用OnCanExecuteChanged 這是一個很大的問題,因為CanExecute調用了一些已經被丟棄的一次性對象。

我知道,認為:1)的窗口(WinForms的形式)此時關閉,因為CanExecute后稱為Form.Closed事件被處理; 2)沒有內存泄漏-如果我模擬CanExecute ,內存事件探查器顯示,包含命令的視圖模型是由GC收集的,不再存在。

問題。
如果按鈕不可見,檢查CanExecute的目的是什么? 有什么選擇可以防止這種行為?

PS我看到的唯一解決方法是在視圖模型中的某個位置保留一個標志,該標志將顯示已處理了一次性物品,並從CanExecute返回false 還有更好的主意嗎?

我將給出四個可能的答案,並對如何以這種方式實現它進行猜測:

  1. 在處置所有內容之前,將要拆除的窗口的DataContext設置為null 該按鈕在對象上沒有引用,因此永遠不會引發異常。

  2. 將對一次性對象的調用包裝在try / catch中,該對象過濾ObjectDisposedException並返回false。

  3. IsDisposed屬性添加到一次性對象,並事先檢查。 如果您在非UI或終結器線程上執行任何操作,似乎這里可能存在爭用情況。

  4. 如果正在等待終結器調用Dispose ,則將WeakReference保持為可拋棄對象,或者在調用Dispose()之后將引用設置為null ,並在調用它之前檢查其是否為null

至於為什么即使在不可見的情況下也要查詢命令,請考慮命令的結果可以控制可見性。 想象以下情況:

<!-- This would probably have to be done in some more complicated way, like
     passing IsEnabled to a converter with CanExecute as the parameter, or
     by just binding to IsEnabled. -->
<Button Visibility="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=CanExecute}"
        Command="{Binding TheCommand" Content="Do it" />

如果不查詢按鈕的狀態是隱藏的,則一旦禁用它就永遠不會顯示。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM