2009-05-07 7 views
3

목록의 항목에는 상황에 맞는 메뉴가 있습니다. 컨텍스트 메뉴 항목은 라우트 된 명령에 바인드됩니다.WPF - ContextMenu 항목이 ListBox에는 작동하지만 ItemsControl에는 작동하지 않는 이유는 무엇입니까?

목록 컨트롤이 ListBox 일 경우 상황에 맞는 메뉴 항목이 올바르게 작동하지만 나중에 ItemsControl으로 다운 그레이드하면 더 이상 작동하지 않습니다. 특히 메뉴 항목은 항상 회색으로 표시됩니다. 내 CommandBindingCanExecute 콜백이 호출되지 않습니다.

명령이 포함 된 상황에 맞는 메뉴 항목을 올바르게 바인딩 할 수있는 약 ListBox은 무엇입니까? 뷰 모델과 데이터 항목에 대한 C# 코드 여기

<!-- 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}" /> 

것 : 여기

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을 사용할 때 ContextMenu에서 직접 CommandBindings를 정의하면 작동하지만이 명령은 처음부터 명령을 사용하는 목적을 상실합니다. 내 생각 엔이 ListBoxItem 개체의 항목을 래핑하는 ListBox 함께 할 수있는 뭔가가있다 ... –

답변

0

ContextMenu 팝업의 항목이 다른 UserControl과 동일한 시각적 트리가 아니라는 사실과 관련이 있습니다 (기본적으로 팝업은 별도의 창). 이것이 바로 CommandBinding이 작동하지 않는 이유입니다.

그러나 지금은 ContextMenu 내에서 CommandBinding을 지정하지 않고이 문제를 해결하는 방법을 알지 못합니다.

+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의 모양이 좋으므로 선택 영역과 선택되지 않은 영역의 차이를 표시하지 않는 ListBoxItem 스타일을 지정해야합니다.

+0

감사합니다 카메론, 오늘이 기회를주지. 나는 여전히 ItemsBox의 항목이 여전히 논리적 트리의 일부이고 라우트 된 이벤트가 잘 돌아갈 것임을 확신하므로 ListBox가 라우트 된 명령을 사용하여 항목에서 터널링하는 것을 추가하는 것을 알고 싶습니다. –

+0

그래, 리플렉터가 ListBox를 특별하게 만들었던 것을 알아 내려고 노력하면서 최대 2am를 파고 들었다. 오늘 아침 나는 이것이 더 나은 해결책이 될 것임을 깨달았다 : P –

관련 문제