Wpf如何仅在数据网格行上执行上下文菜单 [英] Wpf how to do a contextmenu only on a datagrid row

查看:101
本文介绍了Wpf如何仅在数据网格行上执行上下文菜单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我多年来一直在学习C#,最后决定学习和使用WPF和MVVM,而且没有代码隐藏。这是我的第一个应用程序,在大多数情况下,它的表现相当不错。我无法找到以下问题的答案。



我有一个简单的应用程序,我有一个WPF可执行前端和一个DLL类库结束。前端有一个 DataGrid ,它绑定到ViewModel类中的 DataTable ;当用户请求时,我从查询填充到SQL 2000数据库。我希望能够让用户右键单击 DataGrid 中的一行并显示 ContextMenu 单击时,打开浏览器并显示与单击的行的故障单相关的信息。 ContextMenu 项是ViewModel类中的命令,它启动一个打开URL并使用其中一个文本的进程右键单击的特定项目的 DataRowView 列。除了不确定我是否应该操作 SelectedItem SelectedIndex 直接引用 DataTable (可能有一段时间索引不同?) - 在大多数情况下,这大致使用以下代码:



部分XAML:

I've been learning C# for years, and finally decided to learn and use WPF and MVVM, and no code-behind. This is my first app, and for the most part, it's going rather well. I haven't been able to find an answer to the following problem.

I have a simple application where I have a WPF executable front end and a DLL class library back end. The front end has a DataGrid that is bound to a DataTable in a ViewModel class; which I populate from a query to an SQL 2000 database when the user requests it. I'd like to be able to let the user right-click on a row in a DataGrid and display a ContextMenu that when clicked, opens a browser and displays information related to the ticket for the row that was clicked. That ContextMenu item is a command in the ViewModel class that starts a process that opens a URL and uses text from one of the columns for the DataRowView for the specific item that was right-clicked. Aside from not being sure if I should operate off the SelectedItem or SelectedIndex to reference directly with the DataTable (perhaps there could be a time when the indexes are different?)--for the most part, that's working with roughly the following code:

Some of the XAML:

<DataGrid

    x:Name="TicketsDataGrid"

    ItemsSource="{Binding TicketsViewModel.TicketsDataTable}"

    AutoGenerateColumns="True"

    CanUserAddRows="False"

    CanUserSortColumns="True"

    CanUserResizeRows="False"

    IsReadOnly="True"

    SelectionMode="Single"

    SelectionUnit="FullRow">
    <DataGrid.ContextMenu>
        <ContextMenu>
            <MenuItem

                Header="View Ticket in browser"

                Command="{Binding Path=TicketsViewModel.ViewTicketInBrowser_Command}"

                CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.SelectedItem}"/>
        </ContextMenu>
    </DataGrid.ContextMenu>
</DataGrid>





TicketsViewModel的一部分:



Part of TicketsViewModel:

private RelayCommand viewTicketInBrowser_Command;

public ICommand ViewTicketInBrowser_Command
{
    get
    {
        return this.viewTicketInBrowser_Command ?? (this.viewTicketInBrowser_Command = new RelayCommand(this.ViewTicketInBrowser_Command_Execute, param => ViewTicketInBrowser_Command_CanExecute()));
    }
}

public bool ViewTicketInBrowser_Command_CanExecute()
{
    return true;
}

public void ViewTicketInBrowser_Command_Execute(object ticketObject)
{
    if (ticketObject != null)
    {
        if (ticketObject.GetType() == typeof(System.Data.DataRowView) && ((System.Data.DataRowView)ticketObject).Row.Table.Columns.Contains("TicketNumber"))
        {
            System.Diagnostics.Process.Start("http://mysite/ticket_detail.asp?ticketnum=" + ((System.Data.DataRowView)ticketObject).Row.Field<string>("TicketNumber"));
        }
    }
}





上述方法有效,但允许右键单击并且 ContextMenu DataGrid 中的任何位置。如果您右键单击 DataGrid 中的行,我只希望它可用。理想情况下,我想获取被点击的行,但只知道如何获得 SelectedItem SelectedIndex 。所以我想如果我只限于在行上允许 ContextMenu ,这可能是最好的选择。我可以看到 ContextMenu 对于 DataGrid 本身的许多内容都有好处;但对于这一行来说,让它发生在 DataGrid 的任何地方似乎很奇怪。



我尝试了什么:



我尝试了一些来自搜索的想法,例如定义 ContextMenu DataGrid.Resources 区域,然后设置 DataGrid.RowStyle ,但我似乎没有对。它似乎只将 ContextMenu 限制为该行,但我似乎无法将其正确连接到我的命令



Alternate XAML我试过:



The above works, but will allow right-click and the ContextMenu anywhere in the DataGrid. I only want it to be available if you right-click on a row in the DataGrid. Ideally I'd like to get the row that was clicked, however only know how to get the SelectedItem or SelectedIndex. So I figured if I limited to just allowing the ContextMenu on the row, that could be the best option. I can see where a ContextMenu would be good for many things on the DataGrid itself; but for just the row, it seemed odd to let it happen just anywhere on the DataGrid.

What I have tried:

I tried several ideas from searches, such as defining the ContextMenu in the DataGrid.Resources area, then setting a DataGrid.RowStyle, but I don't seem to have that right. It seems to limit the ContextMenu to just the row, but I cant seem to wire it correctly to my command.

Alternate XAML I tried:

<DataGrid.Resources>
    <ContextMenu

        x:Key="RowMenu"

        DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
        <MenuItem

            Header="View Ticket in browser"

            Command="{Binding Path=TicketsViewModel.ViewTicketInBrowser_Command}"

            CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}, Path=PlacementTarget.SelectedItem}" />
    </ContextMenu>
</DataGrid.Resources>
<DataGrid.RowStyle>
    <Style TargetType="DataGridRow">
        <Setter Property="ContextMenu" Value="{StaticResource RowMenu}" />
    </Style>
</DataGrid.RowStyle>





尝试了多种变体和调整,但似乎得到了接近输出中的两个错误:



错误1:

System.Windows.Data错误: 40:BindingExpression路径错误:'对象'''DataRowView'(HashCode = 7742767)'上找不到'TicketsViewModel'属性。 BindingExpression:路径= TicketsViewModel.ViewTicketInBrowser_Command; DataItem ='DataRowView'(HashCode = 7742767); target元素是'MenuItem'(Name =''); target属性是'Command'(类型'ICommand')



错误2:

System.Windows。数据错误:40:BindingExpression路径错误:'object'''DataGrid'(Name ='TicketsDataGrid')'上找不到'PlacementTarget'属性。 BindingExpression:路径= PlacementTarget.SelectedItem; DataItem ='DataGrid'(Name ='TicketsDataGrid'); target元素是'MenuItem'(Name =''); target属性是'CommandParameter'(类型'Object')



我认为这与祖先绑定不起作用的主要问题有关,如上下文菜单不是它所在元素的孩子;是DataGrid。 Stack Overflow文章如何设置绑定在WPF Toolkit Datagrid的ContextMenu CommandParameter中让我想到了。



也许我喝了太多咖啡,或者一直盯着屏幕长;但需要一些帮助来解决这个问题。



Have tried several variants and tweaks, but seem to get close to the same two errors in Output:

Error 1:
System.Windows.Data Error: 40 : BindingExpression path error: 'TicketsViewModel' property not found on 'object' ''DataRowView' (HashCode=7742767)'. BindingExpression:Path=TicketsViewModel.ViewTicketInBrowser_Command; DataItem='DataRowView' (HashCode=7742767); target element is 'MenuItem' (Name=''); target property is 'Command' (type 'ICommand')

Error 2:
System.Windows.Data Error: 40 : BindingExpression path error: 'PlacementTarget' property not found on 'object' ''DataGrid' (Name='TicketsDataGrid')'. BindingExpression:Path=PlacementTarget.SelectedItem; DataItem='DataGrid' (Name='TicketsDataGrid'); target element is 'MenuItem' (Name=''); target property is 'CommandParameter' (type 'Object')

I think this is related to the main issue where the Ancestor bindings don't work, as the context menu is not a child of the element it is on; being the DataGrid. The Stack Overflow article "How to set a binding in WPF Toolkit Datagrid's ContextMenu CommandParameter" led me to think that.

Perhaps I've had too much coffee, or have been staring at the screen too long; but need some assistance figuring this out.

推荐答案

在资源中定义你的上下文菜单,如下所示:

Define ur context menu in resource as below :
<ContextMenu x:Key="menu">
           <ContextMenu.Items>
               <MenuItem Header="{Binding DataContext.Column1,RelativeSource={RelativeSource Mode=Self}}"  Click="MenuItem_Click" />
               <MenuItem Header="{Binding DataContext.Column2,RelativeSource={RelativeSource Mode=Self}}" />
               <MenuItem Header="{Binding DataContext.Column3,RelativeSource={RelativeSource Mode=Self}}" />
           </ContextMenu.Items>
       </ContextMenu>





为简单起见,我使用click事件而不是命令。我正在做的是将每行的列值绑定到上下文菜单。



使用上面的上下文菜单作为datagrid行样式中的静态资源。每个标头的绑定将从您的DataModel解析为实际内容。





而不是点击事件。使用视图模型中的相应命令。绑定中的相对源属性用于解析视图模型的实例。



我希望这可以解决您的问题



For simplicity i am using click event instead of command. What i am doing is binding the column values of each row to context menu.

Use the above context menu as a static resource in datagrid row style. The binding of each header will be resolved to actual content from your DataModel.


Instead of click event. Use appropriate command from the view model. The relative source property in binding is use to resolve the instance of view model.

I hope this solves your problem


这里是我最终得到的最终解决方案。



有效的最终XAML:

Here's the final solution I ended up with.

Final XAML that works:
<DataGrid.Resources>
    <ContextMenu x:Key="ticketContextMenu">
	    <ContextMenu.Items>
            <MenuItem

                Header="Open ticket in browser"

                Command="{Binding DataContext.TicketsViewModel.ViewTicketInBrowser_Command, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}"

                CommandParameter="{Binding DataContext.TicketNumber, RelativeSource={RelativeSource Mode=Self}}"/>
        </ContextMenu.Items>
    </ContextMenu>
</DataGrid.Resources>
<DataGrid.RowStyle>
    <Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
        <Setter Property="ContextMenu" Value="{StaticResource ticketContextMenu}" />
    </Style>
</DataGrid.RowStyle>





在Kassim的回应和一点点搜索的指导下;我不得不调整我提供的备用XAML代码以使其工作。我认为关键是在绑定时指定DataContext,我认为这是打破我之前尝试的代码的一个部分。我还做了一些其他的改变。无论哪种方式,当我右键单击一行时,我只获得ContextMenu;当我点击相应的命令并提供与DataGrid中指定列相关的CommandParameters时,操作正确。



同样根据Kassim的回复指导,它显示我如何直接引用DataGrid列值。我喜欢将TicketNumber字符串发送到命令而不是SelectedItem或SelectedIndex;虽然我不确定这是否是我应该做的最好的方式(即发送整行,或只发送文本)。但是另一个线程的问题是。



With guidance from Kassim's response, and a little searching; I had to tweak the alternate XAML code I provided to get it to work. I think the key was specifying DataContext when binding, which I think was a piece that was breaking the previous code I tried. I also made a couple other changes. Either way, I only get the ContextMenu when I right click on a row; and operates correctly when I click it for the appropriate Command and providing the CommandParameters related to the specified column in the DataGrid.

Also based on guidance from Kassim's response, it showed me how to reference the DataGrid column value directly. I like sending the TicketNumber string to the command instead of the SelectedItem or SelectedIndex; though am not sure if that's the best way I should be doing it (i.e. sending the entire row, or just the text). A question for a another thread though.


这篇关于Wpf如何仅在数据网格行上执行上下文菜单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆