如何:處理 ContextMenuOpening 事件

ContextMenuOpening事件可以在應用程式中處理,以在顯示之前調整現有的操作功能表,或藉由在事件資料中將 屬性 true 設定 Handled 為 來隱藏要顯示的功能表。 在事件資料中將 設定 Handledtrue 為 的一般原因是將功能表完全取代為新的 ContextMenu 物件,有時需要取消作業並啟動新的開啟。 如果您撰寫事件的處理常式 ContextMenuOpening ,您應該注意控制項與負責開啟和定位控制項操作功能表的服務之間的 ContextMenu 計時問題。 本主題說明各種操作功能表開啟案例的一些程式碼技術,並說明計時問題開始運作的情況。

有數種處理 ContextMenuOpening 事件的案例:

  • 在顯示之前調整功能表項目。

  • 在顯示之前取代整個功能表。

  • 完全隱藏任何現有的操作功能表,而且不顯示任何操作功能表。

範例

在顯示之前調整功能表項目

調整現有的功能表項目相當簡單,而且可能是最常見的案例。 您可以這麼做,以新增或減去操作功能表選項,以回應應用程式中的目前狀態資訊,或特定狀態資訊,該資訊可作為要求操作功能表之物件上的屬性。

一般技術是取得事件的來源,這是以滑鼠右鍵按一下的特定控制項,並從中取得 ContextMenu 屬性。 您通常會想要檢查 Items 集合,以查看功能表中已有的內容功能表項目,然後新增或移除集合中適當的新 MenuItem 專案。

void AddItemToCM(object sender, ContextMenuEventArgs e)
{
    //check if Item4 is already there, this will probably run more than once
    FrameworkElement fe = e.Source as FrameworkElement;
    ContextMenu cm = fe.ContextMenu;
    foreach (MenuItem mi in cm.Items)
    {
        if ((String)mi.Header == "Item4") return;
    }
    MenuItem mi4 = new MenuItem();
    mi4.Header = "Item4";
    fe.ContextMenu.Items.Add(mi4);
}

在顯示之前取代整個功能表

替代案例是如果您想要取代整個操作功能表。 當然,您也可以使用上述程式碼的變化,移除現有操作功能表的每個專案,並新增從專案零開始的新專案。 但是,取代操作功能表中所有專案的更直覺的方法,是建立新的 ContextMenu 、以專案填入它,然後將 控制項的 屬性設定 FrameworkElement.ContextMenu 為新的 ContextMenu

以下是取代 ContextMenu 的簡單處理常式程式碼。 程式碼會參考自訂 BuildMenu 方法,因為其中一個以上的範例處理常式會呼叫自訂方法。

void HandlerForCMO(object sender, ContextMenuEventArgs e)
{
    FrameworkElement fe = e.Source as FrameworkElement;
    fe.ContextMenu = BuildMenu();
}
ContextMenu BuildMenu()
{
    ContextMenu theMenu = new ContextMenu();
    MenuItem mia = new MenuItem();
    mia.Header = "Item1";
    MenuItem mib = new MenuItem();
    mib.Header = "Item2";
    MenuItem mic = new MenuItem();
    mic.Header = "Item3";
    theMenu.Items.Add(mia);
    theMenu.Items.Add(mib);
    theMenu.Items.Add(mic);
    return theMenu;
}

不過,如果您針對 ContextMenuOpening 使用這個處理常式樣式,則如果設定 ContextMenu 的物件沒有預先存在的操作功能表,可能會公開計時問題。 當使用者以滑鼠右鍵按一下控制項時, ContextMenuOpening 即使現有的 ContextMenu 是空的或 Null,也會引發 。 但在此情況下,您在來源元素上設定的任何新 ContextMenu 專案都遲到無法顯示。 此外,如果使用者第二次按一下滑鼠右鍵,這次您的新 ContextMenu 專案出現時,此值為非 Null,而且您的處理常式會在處理常式第二次執行時正確取代並顯示功能表。 這建議兩種可能的因應措施:

  1. 確保 ContextMenuOpening 處理常式一律針對至少有可用預留位置的控制項執行,而您想要由處理常式程式碼取代該控制項 ContextMenu 。 在此情況下,您仍然可以使用上一個範例所示的處理常式,但通常想要在初始標記中指派預留位置 ContextMenu

    <StackPanel>
      <Rectangle Fill="Yellow" Width="200" Height="100" ContextMenuOpening="HandlerForCMO">
        <Rectangle.ContextMenu>
          <ContextMenu>
            <MenuItem>Initial menu; this will be replaced ...</MenuItem>
          </ContextMenu>
        </Rectangle.ContextMenu>
      </Rectangle>
      <TextBlock>Right-click the rectangle above, context menu gets replaced</TextBlock>
    </StackPanel>
    
  2. 假設初始 ContextMenu 值可能是 Null,根據一些初步邏輯。 您可以檢查 ContextMenu Null,或使用程式碼中的旗標來檢查處理程式是否已至少執行一次。 因為您假設 ContextMenu 即將顯示 ,因此您的處理常式接著會在事件資料中設定 HandledtrueContextMenuService對於負責操作功能表顯示的 , true 事件資料中的 值 Handled 代表取消引發事件之操作功能表/控制群組合的顯示要求。

既然您已隱藏潛在的可疑操作功能表,下一個步驟是提供新的功能表,然後顯示它。 設定新處理常式基本上與上一個處理常式相同:您建置新的 ContextMenu ,並使用它設定控制項來源的 FrameworkElement.ContextMenu 屬性。 另一個步驟是,您現在必須強制顯示操作功能表,因為您隱藏了第一次嘗試。 若要強制顯示,您可以在處理常式內將 屬性設定 Popup.IsOpentrue 。 當您這樣做時請小心,因為開啟處理常式中的操作功能表會再次引發 ContextMenuOpening 事件。 如果您重新輸入處理常式,它會變成無限遞迴。 這就是為什麼當您從事件處理常式內 ContextMenuOpening 開啟操作功能表時,一律需要檢查 null 或使用旗標的原因。

隱藏任何現有的操作功能表,並顯示沒有操作功能表

最後一個案例,撰寫完全隱藏功能表的處理常式並不常見。 如果指定的控制項不應該顯示操作功能表,則可能有比當使用者要求功能表時隱藏功能表更適當的方法來保證這一點。 但是,如果您想要使用 處理常式來隱藏操作功能表,並且不顯示任何內容,則處理常式應該只會在事件資料中設定 HandledtrueContextMenuService負責顯示操作功能表的 會檢查控制項上所引發之事件的事件資料。 如果事件標示在 Handled 路由上的任何位置,則會隱藏起始事件的操作功能表開啟動作。

    void HandlerForCMO2(object sender, ContextMenuEventArgs e)
    {
        if (!FlagForCustomContextMenu)
        {
            e.Handled = true; //need to suppress empty menu
            FrameworkElement fe = e.Source as FrameworkElement;
            fe.ContextMenu = BuildMenu();
            FlagForCustomContextMenu = true;
            fe.ContextMenu.IsOpen = true;
        }
    }
}

另請參閱