ContextMenu.PlacementTarget не устанавливается, понятия не имею, почему - PullRequest
5 голосов
/ 19 января 2011
<DataTemplate x:Key="_ItemTemplateA">
  <Grid Tag="{Binding Path=DataContext.Command, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <ContentControl Content="{Binding}" ContentTemplate="{StaticResource ContentTemplateB}" Grid.Row="0" />
    <ContentControl Name="uiContentPresenter" Content="{Binding ContentView}" Grid.Row="1" Height="0" />
    <ContentControl DataContext="{Binding IsContentDisplayed}" DataContextChanged="IsDisplayed_Changed" Visibility="Collapsed" />
    <Grid.ContextMenu>
      <ContextMenu>
        <MenuItem Header="Text" 
              Command="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}"
              CommandParameter="{Binding}" />
      </ContextMenu>
    </Grid.ContextMenu>
  </Grid>
</DataTemplate>

Приведенный выше шаблон данных применяется к ItemsControl.Проблема заключается в том, что для ContextMenu, которое указано для Grid, свойство PlacementTarget фактически никогда не устанавливается на что-либо, поэтому я не могу получить свойство Tag Grid, которое необходимо для передачи Команды, которая должна выполняться в родительском UserControl, внизв контекстное меню.Я основал этот подход на аналогичных примерах, таких как: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/0244fbb0-fd5f-4a03-bd7b-978d7cbe1be3/

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

Ответы [ 3 ]

3 голосов
/ 20 января 2011

У нас та же проблема, но она работает случайным образом. Контекстное меню внутри шаблона элемента управления в стиле списка. Мы попытались переместить контекстное меню на разные уровни внутри шаблона, но произошла та же ошибка.

Мы думаем, что это может быть связано с обновлением нашего ICollectionView, являющегося источником элементов ListBox.

Похоже, что когда представление обновляется, относительная исходная привязка внутри контекстного меню оценивается до установки PlacementTarget.

Это похоже на ошибку либо в collectionviewsource, либо в ContextMenu WPF ...

3 голосов
/ 11 марта 2013

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

Во-первых, назовите ваше мнение UserControl ... Я обычно называю все мое This для простоты.Затем, помня, что наша модель представления привязана к DataContext из UserControl, мы можем привязаться к модели представления, используя {Binding DataContext, ElementName=This}.

Так что теперь мы можем привязаться к модели представления, мы должнысоедините это с ContextMenu.DataContext.Я использую свойство Tag объекта с ContextMenu (PlacementTarget) в качестве этого соединения, в этом примере, Grid:

<DataTemplate x:Key="YourTemplate" DataType="{x:Type DataTypes:YourDataType}">
    <Grid ContextMenu="{StaticResource Menu}" Tag="{Binding DataContext, 
        ElementName=This}">
        ...
    </Grid>
</DataTemplate>

Затем мы можем получить доступ к модели представлениясвойства и команды в ContextMenu путем привязки свойства ContextMenu.DataContext к свойству PlacementTarget.Tag (в нашем примере Grid):

<ContextMenu x:Key="Menu" DataContext="{Binding PlacementTarget.Tag, RelativeSource=
    {RelativeSource Self}}">
    <MenuItem Header="Delete" Command="{Binding DeleteFile}" CommandParameter=
        "{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource 
        AncestorType=ContextMenu}}" CommandTarget="{Binding PlacementTarget, 
        RelativeSource={RelativeSource Self}}" />
</ContextMenu>

Обратите внимание на привязку к свойству MenuItem.CommandTarget,Установка этого параметра гарантирует, что целевым элементом, для которого вызвана указанная команда, является PlacementTarget или Grid в этом случае.

Также обратите внимание на привязку CommandParameter.Это связывается с DataContext из PlacementTarget или Grid в этом случае.DataContext Grid будет унаследовано от DataTemplate, поэтому ваш элемент данных теперь привязан к параметру object в вашем Command, если вы используете некоторую реализацию интерфейса ICommand:

public bool CanExecuteDeleteFileCommand(object parameter)
{
    return ((YourDataType)parameter).IsInvalid;
}

public void ExecuteDeleteFileCommand(object parameter)
{
    Delete((YourDataType)parameter);
}

Или, если вы используете какие-то RelayCommand делегаты непосредственно в вашей модели представления:

public ICommand Remove
{
    get 
    {
        return new ActionCommand(execute => Delete((YourDataType)execute), 
            canExecute => return ((YourDataType)canExecute).IsInvalid); 
    }
}
0 голосов
/ 20 января 2011

Вот рабочий автономный пример только для XAML, основанный на вашем тестовом примере: ContextMenu, который извлекает Command из DataContext его PlacementTarget, используя Tag.Вы можете повторно вводить части своего кода, пока он не перестанет работать, чтобы попытаться найти причину проблемы:

<Grid>
    <Grid.Resources>
        <PointCollection x:Key="sampleData">
            <Point X="10" Y="20"/>
            <Point X="30" Y="40"/>
        </PointCollection>
        <DataTemplate x:Key="_ItemTemplateA">
            <Grid Tag="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DockPanel}}}">
                <TextBlock Text="{Binding X}"/>
                <Grid.ContextMenu>
                    <ContextMenu>
                        <MenuItem Header="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}}" CommandParameter="{Binding}"/>
                    </ContextMenu>
                </Grid.ContextMenu>
            </Grid>
        </DataTemplate>
    </Grid.Resources>
    <DockPanel DataContext="{x:Static ApplicationCommands.Open}">
        <ListBox ItemTemplate="{StaticResource _ItemTemplateA}" ItemsSource="{StaticResource sampleData}"/>
    </DockPanel>
</Grid>
...