2009-05-07 6 views
3

Элементы в списке имеют контекстные меню. Элементы контекстного меню привязаны к маршрутизируемым командам.WPF - Почему элементы ContextMenu работают для ListBox, но не ItemsControl?

Элементы контекстного меню работают правильно, если элемент управления списком является ListBox, но как только я понижаю его до ItemsControl, он больше не работает. В частности, пункты меню всегда выделены серым цветом. Обратный вызов CanExecute в моем CommandBinding также не вызывается.

Что это такое о ListBox, которое позволяет элементам контекстного меню с командами правильно связываться?

Вот некоторые выдержки из образца приложение, которое я поставил вместе, чтобы выделить эту проблему:

<!-- Data template for items --> 
<DataTemplate DataType="{x:Type local:Widget}"> 
    <StackPanel Orientation="Horizontal"> 
    <StackPanel.ContextMenu> 
     <ContextMenu> 
     <MenuItem Header="UseWidget" 
        Command="{x:Static l:WidgetListControl.UseWidgetCommand}" 
        CommandParameter="{Binding}" /> 
     </ContextMenu> 
    </StackPanel.ContextMenu> 
    <TextBlock Text="{Binding Path=Name}" /> 
    <TextBlock Text="{Binding Path=Price}" /> 
    </StackPanel> 
</DataTemplate> 

<!-- Binding --> 
<UserControl.CommandBindings> 
    <CommandBinding Command="{x:Static l:WidgetListControl.UseWidgetCommand}" 
        Executed="OnUseWidgetExecuted" 
        CanExecute="CanUseWidgetExecute" /> 
</UserControl.CommandBindings> 

<!-- ItemsControl doesn't work... --> 
<ItemsControl ItemsSource="{Binding Path=Widgets}" /> 

<!-- But change it to ListBox, and it works! --> 
<ListBox ItemsSource="{Binding Path=Widgets}" /> 

Вот C# код для представления модели и данных элемента:

public sealed class WidgetListViewModel 
{ 
    public ObservableCollection<Widget> Widgets { get; private set; } 

    public WidgetViewModel() 
    { 
     Widgets = new ObservableCollection<Widget> 
      { 
       new Widget { Name = "Flopple", Price = 1.234 }, 
       new Widget { Name = "Fudge", Price = 4.321 } 
      }; 
    } 
} 

public sealed class Widget 
{ 
    public string Name { get; set; } 
    public double Price { get; set; } 
} 

Вот код C# - для управления:

public partial class WidgetListControl 
{ 
    public static readonly ICommand UseWidgetCommand 
     = new RoutedCommand("UseWidget", typeof(WidgetListWindow)); 

    public WidgetListControl() 
    { 
     InitializeComponent(); 
    } 

    private void OnUseWidgetExecuted(object s, ExecutedRoutedEventArgs e) 
    { 
     var widget = (Widget)e.Parameter; 
     MessageBox.Show("Widget used: " + widget.Name); 
    } 

    private void CanUseWidgetExecute(object s, CanExecuteRoutedEventArgs e) 
    { 
     e.CanExecute = true; 
     e.Handled = true; 
    } 
} 

Просто повторить вопрос - что это такое ListBox обеспечивает, что позволяет командам элемента контекстного меню правильно связываться, и есть ли способ, которым я могу заставить это работать для ItemsControl?

+0

Обратите внимание, что при использовании ItemsControl, если я определяю CommandBindings непосредственно в ContextMenu, то это работает, но это поражение цели с помощью команд в первую очередь. Я предполагаю, что это связано с тем, что ListBox обертывает свои объекты в объектах ListBoxItem ... –

ответ

0

Возможно, это связано с тем, что элементы в всплывающем меню ContextMenu не являются тем же визуальным деревом, что и остальная часть вашего UserControl (в основном всплывающее окно является отдельным окном). Вот почему CommandBindings не работают.

Но пока у меня нет идеи, как исправить это, не указав CommandBindings в ContextMenu.

+0

Я читал, что на http://stackoverflow.com/questions/662164/wpf-context-menu-doesnt-bind-to -right-databound-item, но он не объясняет, почему ListBox работает, но ItemsControl этого не делает. Что делает ListBox, что делает его элементы меню контекстного меню связуемыми? Я посмотрел в .NET Reflector, но ничего не сделал. –

5

Хорошо, основная проблема, которую я вижу, заключается в том, что ItemsControl не имеет концепции выбранного элемента, поэтому вы не можете выбрать элемент для привязки DataTemplate.

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

Так что, думая об этом, вы хотите поведение ListBox, но внешний вид ItemsControl, так почему бы вам не стилизовать ListBoxItems, чтобы не показывать разницу между выбранными и не выбранными.

<Style TargetType="{x:Type ListBoxItem}"> 
    <Setter Property="Background" Value="Transparent"/> 
    <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> 
    <Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> 
    <Setter Property="Padding" Value="2,0,0,0"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type ListBoxItem}"> 
       <Border SnapsToDevicePixels="true" x:Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"> 
        <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 
       </Border> 
       <ControlTemplate.Triggers> 
        <Trigger Property="IsEnabled" Value="false"> 
         <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> 
        </Trigger> 
       </ControlTemplate.Triggers> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 
+0

Спасибо, Кэмерон, сегодня я сделаю этот снимок. Я все равно хотел бы знать, что именно добавляет ListBox, чтобы включить маршрутизированную команду для туннелирования из своих элементов, поскольку я уверен, что элементы ItemsControl все еще являются частью логического дерева и что перенаправленные события должны проходить нормально. –

+0

Да, я был до 2-х часов, пробираясь через Reflector, пытаясь выяснить, что сделало ListBox таким особенным. Сегодня утром я понял, что это будет лучшее решение: P –

Смежные вопросы