【问题标题】:Access the Window's datacontext from contextmenu within an itemscontrol? [duplicate]从项目控件中的上下文菜单访问窗口的数据上下文? [复制]
【发布时间】:2019-04-13 05:29:22
【问题描述】:

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

我的 ItemsControl 使用画布作为 ItemsPanelTemplate。 DataTemplate 包含一个 Ellipse 和一个与之关联的 ContextMenu; ContextMenu 有一个 MenuItem。

我的窗口视图模型包含一个 ICommand,它接受一个对象参数(当前 ItemsSource 项)。

我正在尝试右键单击我的 ItemsControl 中的一个省略号并调出 ContextMenu,然后单击 MenuItem 以执行 ICommand 并将当前 ItemsSource 项作为参数传递。

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

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

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;数据项=空;目标元素是'MenuItem'(名称='');目标属性是“命令”(输入“ICommand”)

【问题讨论】:

  • 尝试使用相对源绑定而不是元素名称来查找窗口,例如 RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}} 您的错误也说明了 mainGrid 而不是 mainWindow
  • Pavel,我所做的错误是正确的,我只是忘记更新我的帖子以反映以前的更改。我尝试了您使用相对来源的建议,但仍然没有运气

标签: c# wpf binding


【解决方案1】:

问题已解决:

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

                    <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 的数据上下文。这个修复可能并不理想,但对我有用。

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

【讨论】:

    【解决方案2】:

    如果你想在 DataTemplate 中访问 DataContext,试试这个:

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

    【讨论】:

    • 欢迎来到 Stack Overflow!请为您的答案添加一些解释,因为解释是 Stack Overflow 上的一个重要元素。您可以通过单击其下方的edit 链接来编辑您的答案。您可以找到我们的指南How to write a good answer。谢谢!
    猜你喜欢
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    • 1970-01-01
    相关资源
    最近更新 更多