WPF ContextMenu = {x:Null}, но все равно показывает меню внутри ContentControl

Мне нужно отключить стандартное ContextMenu TextBox. Я создал новый проект WPF и добавил следующее:

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
       <ContentControl>
           <ContentControl.ContentTemplate>
               <DataTemplate>
                    <TextBox ContextMenu="{x:Null}" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
                </DataTemplate>
           </ContentControl.ContentTemplate>
       </ContentControl>
    </Grid>
</Window>

Но вот что я получаю:

Следующий код работает нормально:

<Grid>
     <TextBox ContextMenu="{x:Null}" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
</Grid>

Почему это происходит?

Обновить.

В соответствии с принятым ответом я создал класс, производный от TextBox, чтобы иметь возможность показывать родителям ContextMenu.

    public class TextBoxNoMenu: TextBox
    {
        public TextBoxNoMenu()
        {
            ContextMenu = null;
        }
    }

4 ответа

Решение

Почему это происходит?

Это интересный случай изменения поведения элемента управления в зависимости от того, где / как установлено свойство.

TextBox предоставляет собственное контекстное меню по умолчанию. Единственный раз, когда это не будет сделано, это когда вы явно устанавливаете локальное значение ContextMenu в null, Это то, что происходит в вашем простом примере, где TextBox находится прямо в Grid,

Однако, когда вы устанавливаете свойство внутри шаблона, вы фактически не устанавливаете локальное значение; Вы устанавливаете значение "родительского шаблона". Если вы проверяете значение с DependencyPropertyHelper.GetValueSource()вы увидите базовое значение источника ParentTemplate вместо Local, Таким образом, меню по-прежнему переопределяется.

См. Приоритет значения свойства зависимости для получения дополнительной информации о различных видах источников значения свойства зависимости.

Предложение @OmegaMan о назначении "скрытого" контекстного меню, кажется, работает очень хорошо.

Похоже, это проблема, из-за которой X:Null не отключает контекстное меню по умолчанию. Лучшим способом было бы изменить его видимость:

<TextBox.ContextMenu>
    <ContextMenu Visibility="Collapsed"/>
</TextBox.ContextMenu>

Обратите внимание, что, хотя вы можете отключить ContextMenu в TextBox, если он находится в другом элементе управления, возможно, вы на самом деле видите ContextMenu такой оболочки. Попробуйте Snooping, чтобы увидеть более конкретно такого рода поведение.

Также обратите внимание, что многие шаблоны управления по умолчанию в WPF могут вызывать такие проблемы, добавляя свои собственные дочерние объекты. Просмотр шаблона по умолчанию для TextBox использует Border а потом <ScrollViewer Margin="0" x:Name="PART_ContentHost" />вы, вероятно, видите ContextMenu дочернего объекта, если TextBox.

У меня была похожая проблема, но я генерировал свои элементы управления программно, и мой родительский элемент управления - это док-панель. Основываясь на принятом ответе, я решил установить нулевое значение в коде.

        <Grid>
            <DockPanel>
               <TextBox Name="txtBox" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
            </DockPanel>
        </Grid>

а потом

            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                txtBox.ContextMenu = null;
            }

РЕДАКТИРОВАТЬ: я чувствовал, что это был своего рода случайный ответ, так как он не полностью или прямо не решить этот вопрос. Я немного покопался, и если вы реализуете метод, найденный в ответе на этот вопрос, вы можете найти текстовое поле в коде.

Итак, если у вас есть это

        <Grid>
           <ContentControl>
              <ContentControl.ContentTemplate>
                 <DataTemplate>
                    <TextBox Name="txtBox" VerticalAlignment="Top" HorizontalAlignment="Left" Width="50"></TextBox>
                 </DataTemplate>
              </ContentControl.ContentTemplate>
           </ContentControl>
        </Grid>

Затем вы сможете найти текстовое поле по имени (в данном случае txtBox) и установить в контекстном меню значение null.

                TextBox myTextBox = FindChild<TextBox>(Application.Current.MainWindow, "txtBox");

                myTextBox.ContextMenu = null;

Лично я предпочел бы это созданию нового класса с наследованием, но все, что работает для вас. Это все еще не отвечает "Почему это происходит?" но я думаю, что принятый ответ хорошо с этим справляется.

Другие вопросы по тегам