从items控件的上下文菜单访问Window的数据上下文?

时间:2019-04-13 05:29:23

标签: c# wpf binding

我有一个带有ViewModel的窗口作为DataContext。我的窗口包含一个ItemsControl,它的ItemsSource绑定到一个viewmodel对象集合。

“我的ItemsControl”将Canvas用于ItemsPanelTemplate。 DataTemplate包括一个带有关联的ContextMenu的Ellipse。那个ContextMenu只有一个MenuItem。

“我的窗口”视图模型包含一个ICommand,该ICommand带有一个对象参数(当前的ItemsSource项)。

我试图右键单击ItemsControl中的一个椭圆并打开ContextMenu,然后单击MenuItem以执行ICommand并将当前的ItemsSource项作为参数传递。

由于某种原因,我无法从ContextMenu中访问Window的DataContext。我已经尝试研究此问题,但是所提出的解决方案似乎都不适合我。

我试图通过使用Window的元素名称并找到祖先类型来访问Windows数据上下文,但是没有运气。

public class VM_MainWindow : ViewModelBase
{
    public DelegateCommand<EllipseObject> TestClick { get; }

    // constructor
    public VM_MainWindow()
    {
        // initialization
        EllipseCollection = DynamicData.Ellipses;

        ScreenResolutionWidth = ClientConfig.Info.ScreenResolutionWidth - 8;
        ScreenResolutionHeight = ClientConfig.Info.ScreenResolutionHeight - 120;

        // commands
        TestClick = new DelegateCommand<EllipseObject>(OnTestClickCommand);
    }

    #region "Properties"
    private ObservableCollection<EllipseObject> _ellipseCollection;
    public ObservableCollection<EllipseObject> EllipseCollection
    {
        get => _ellipseCollection;
        set
        {
            _ellipseCollection = value;
            OnPropertyChanged("EllipseCollection");
        }
    }

    private int _screenResolutionWidth;
    public int ScreenResolutionWidth
    {
        get => _screenResolutionWidth;
        set
        {
            _screenResolutionWidth = value;
            OnPropertyChanged("ScreenResolutionWidth");
        }
    }

    private int _screenResolutionHeight;
    public int ScreenResolutionHeight
    {
        get => _screenResolutionHeight;
        set
        {
            _screenResolutionHeight = value;
            OnPropertyChanged("ScreenResolutionHeight");
        }
    }
    #endregion

    private void OnTestClickCommand(EllipseObject eObj)
    {
        MessageBox.Show("Ellipse Name: " + eObj.DisplayName, "Test", MessageBoxButton.OK, MessageBoxImage.Information);
    }
}

public class DelegateCommand<T> : System.Windows.Input.ICommand
{
    private readonly Predicate<T> _canExecute;
    private readonly Action<T> _execute;

    public DelegateCommand(Action<T> execute)
        : this(execute, null)
    {
    }

    public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        if (_canExecute == null)
            return true;

        return _canExecute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
    }

    public void Execute(object parameter)
    {
        _execute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
    }

    public event EventHandler CanExecuteChanged;
    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

这是MainWindow.xaml,请注意,我的窗口名为x:Name =“ mainWindow”:

<Window.DataContext>
    <viewModels:VM_MainWindow/>
</Window.DataContext>

<Grid Background="Black">
    <Grid.RowDefinitions>
        <RowDefinition Height="27px"/>
        <RowDefinition Height="1*"/>
        <RowDefinition Height="30px"/>
    </Grid.RowDefinitions>

    <Grid x:Name="icContainerGrid" Grid.Row="1" Background="{Binding TrackMapBackground}">
        <Grid>
            <!-- ELLIPSES -->
            <ItemsControl ItemsSource="{Binding EllipseCollection}">

                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas IsItemsHost="True" Width="{Binding ScreenResolutionWidth}" Height="{Binding ScreenResolutionHeight}"/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>

                <ItemsControl.ItemContainerStyle>
                    <Style TargetType="ContentPresenter">
                        <Setter Property="Canvas.Left" Value="{Binding X1}"/>
                        <Setter Property="Canvas.Top" Value="{Binding Y1}"/>
                    </Style>
                </ItemsControl.ItemContainerStyle>

                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Ellipse Width="24" Height="24" Fill="Red">
                            <Ellipse.ContextMenu>
                                <ContextMenu>
                                    <MenuItem Header="Menu item 1" Command="{Binding ElementName=mainWindow, Path=DataContext.TestClick}" CommandParameter="{Binding}"/>
                                </ContextMenu>
                            </Ellipse.ContextMenu>
                        </Ellipse>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Grid>
    </Grid>
</Grid>

我希望我的命令在单击MenuItem时触发,但是不会。

运行该应用程序时,我收到以下绑定错误:

System.Windows.Data错误:4:找不到参考'ElementName = mainWindow'的绑定源。 BindingExpression:Path = DataContext.TestClick; DataItem = null;目标元素是'MenuItem'(Name ='');目标属性为“命令”(类型为“ ICommand”)

2 个答案:

答案 0 :(得分:0)

问题已解决:

经过大量阅读,我终于设法找到一种解决方案,该解决方案涉及使用模板对象的标签存储数据上下文,然后可以通过ContextMenu的MenuItem通过ContextMenu的PlacementTarget属性对其进行访问:

                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Ellipse Width="{Binding Width}" Height="{Binding Height}" Fill="{Binding DisplayColor}" Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=DataContext}">
                                <Ellipse.ContextMenu>
                                    <ContextMenu>
                                        <MenuItem Header="Menu item 2" Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.Tag.TestClick}" CommandParameter="{Binding}"/>
                                    </ContextMenu>
                                </Ellipse.ContextMenu>
                            </Ellipse>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>

由于某种原因,我完全无法从ContextMenu内访问Window的数据上下文。此修复程序可能并不理想,但对我有用。

希望这可以帮助遇到类似问题的其他人。

答案 1 :(得分:-1)

如果要访问DataTemplate中的DataContext,请尝试以下操作:

{Binding DataContext.YourProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}}