Практическое руководство. Обработка события ContextMenuOpening
ContextMenuOpening Событие может быть обработано в приложении, либо для корректировки существующего контекстного меню перед для отображения или подавления меню, которое будет отображаться в противном случае, задав Handled свойства true
в данных события. Типичной причиной для параметра Handled для true
событий данных — замена меню полностью новый ContextMenu объекта, который иногда требует отмены операции и запуска нового открытия. Если создать обработчики для ContextMenuOpening событий, следует иметь в виду проблемы синхронизации между ContextMenu control и служба, которая отвечает за открытие и позиционирование контекстных меню для элементов управления в целом. В этом разделе показаны некоторые методы кода для различных скриптов открытия контекстного меню и показан случай, где ошибки синхронизации вступает в действие.
Существует несколько сценариев для обработки 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, и ваш обработчик правильно заменит и в открывшемся меню при выполнении обработчика во второй раз. Это предполагает два возможных решения:
Убедитесь, что 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>
Предполагается, что начальный ContextMenu значение может иметь значение null, на основе определенной логики предварительной. Можно либо проверить ContextMenu на null, либо использовать флаг в коде, чтобы проверить, была ли ваш обработчик запустить по крайней мере один раз. Так как предполагается, что ContextMenu о будет отображаться, ваш обработчик в затем задает Handled для
true
в данных события. Чтобы ContextMenuService , отвечает за отображение контекстного меню,true
значение Handled событий данных представляет собой запрос элемента управления сочетание, которое вызвало событие / отмена отображения для контекстного меню.
Теперь, когда вы отменили потенциально подозрительные контекстного меню, следующим шагом является предоставление нового, затем отобразить ее. Настройка нового одним по сути является таким же, как предыдущий обработчик: выполняется построение новой ContextMenu и в качестве источника указывается управления FrameworkElement.ContextMenu свойство с ним. Дополнительный шаг обусловлено тем, что принудительное отображение контекстного меню, первая попытка подавляется. Чтобы принудительно отображение, необходимо задать Popup.IsOpen свойства true
обработчика. Следите за при этом, так как Открытие контекстного меню в обработчике вызывает ContextMenuOpening событий снова. Если вы вводите Ваш обработчик, он бесконечной рекурсии. Именно по этой причине необходимо всегда проверять для null
или использовать флаг, если открыть контекстное меню из среды ContextMenuOpening обработчик событий.
Подавление любых существующих контекстных меню и отсутствие отображения контекстного меню
Последний сценарий — написание обработчика, который полностью, подавляет меню используется редко. Если данный элемент управления не предназначен для вызова контекстного меню, чтобы обеспечить это, чем подавление меню только в том случае, когда пользователь запрашивает его, вероятно, более подходящим способами. Но если вы хотите использовать обработчик для подавления контекстного меню без отображения, то обработчик просто должен задавать Handled для true
в данных события. ContextMenuService , Отвечает за отображение контекстного меню будет проверять данные события, возникшего в элементе управления. Если событие было помечено как 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;
}
}
}