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;
Лично я предпочел бы это созданию нового класса с наследованием, но все, что работает для вас. Это все еще не отвечает "Почему это происходит?" но я думаю, что принятый ответ хорошо с этим справляется.