5

I am new in WPF programming with MVVM pattern. Now I have added the context menu in the datagrid. But, when I click the right mouse button, I don't know how to get the select row value. this is my xmal

 <DataGrid  AutoGenerateColumns="False" Grid.Row="1" CanUserAddRows="False" CanUserDeleteRows="False"
              ItemsSource="{Binding StoryList}">
        <DataGrid.Columns>
        <DataGridTextColumn Header="ID" Binding="{Binding ID}" Width="40" IsReadOnly="True" />
        <DataGridTextColumn Header="Title" Binding="{Binding Title}" Width="60" IsReadOnly="True"/>
        <DataGridTextColumn Header="StoryPoints" Binding="{Binding StoryPoints}" Width="90" IsReadOnly="True">
                <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding StoryPoints}" Value="0">
                                <Setter Property="Background" Value="Red"/>
                                <Setter Property="ToolTip" Value="Story Point cannot be 0."/>
                            </DataTrigger>                                
                        </Style.Triggers>
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>
            <DataGridTextColumn Header="Stack Rank" Binding="{Binding StackRank}" Width="80"/>
            <DataGridTextColumn Header="Estimate" Binding="{Binding Estimate}" Width="60"/>
            <DataGridTextColumn Header="CompletedWork" Binding="{Binding CompletedWork}" Width="120" />
            <DataGridTextColumn Header="RemainWork" Binding="{Binding RemainWork}" Width="110" />
            <DataGridTextColumn Header="CompleteProcess" Binding="{Binding CompletedProcess}" Width="110" />
            <DataGridTextColumn Header="YesterdayComments" Binding="{Binding YesterdayComments}" Width="120" />
            <DataGridTextColumn Header="TodayComments" Binding="{Binding TodayComments}" Width="120" />
        </DataGrid.Columns>
        <DataGrid.ContextMenu>
            <ContextMenu Name="StoryMenu" StaysOpen="True">
                <MenuItem Header="Add an Issue" Command="{Binding AddIssueCommand}" />
                <MenuItem Header="Burn Down Chart" Command="{Binding BurnDownChartCommand}"/>
            </ContextMenu>
        </DataGrid.ContextMenu>
    </DataGrid>

And here is my viewModel

 class MainViewModel:NotificationObject
{
    private ObservableCollection<Story> storyList;

    public ObservableCollection<Story> StoryList
    {
        get { return storyList; }
        set
        {
            storyList = value;
            this.RaisePropertyChanged("StoryList");
        }
    }
    public DelegateCommand AddIssueCommand { get; set; }
    public DelegateCommand BurnDownChartCommand { get; set; }
    private Story selectStory;

    public Story SelectStory
    {
        get { return selectStory; }
        set
        {
            selectStory = value;
            this.RaisePropertyChanged("SelectStory");
        }
    }

    public void LoadStory()
    {
        this.storyList = new ObservableCollection<Story>();
        Story story1 = new Story(){ID=1, Title="win App", StoryPoints=0, StackRank=1, Estimate=40, CompletedWork=10, RemainWork=30, CompletedProcess=25, TodayComments="Coding", YesterdayComments="N/a"};
        Story story2 = new Story() { ID = 2, Title = "win App", StoryPoints = 10, Estimate = 40, CompletedWork = 10, RemainWork = 30, CompletedProcess = 25, TodayComments = "Coding 20%", YesterdayComments = "N/a" };
        Story story3 = new Story() { ID = 3, Title = "win App", StoryPoints = 10, Estimate = 50, CompletedWork = 20, RemainWork = 30, CompletedProcess = 20, TodayComments = "Coding  30%", YesterdayComments = "N/a" };
        Story story4 = new Story() { ID = 4, Title = "win App", StoryPoints = 10, Estimate = 60, CompletedWork = 30, RemainWork = 30, CompletedProcess = 50, TodayComments = "Coding  40%", YesterdayComments = "N/a" };
        Story story5 = new Story() { ID = 5, Title = "win App", StoryPoints = 10, Estimate = 40, CompletedWork = 10, RemainWork = 30, CompletedProcess = 25, TodayComments = "Coding  50%", YesterdayComments = "N/a" };
        Story story6 = new Story() { ID = 6, Title = "win App", StoryPoints = 10, Estimate = 30, CompletedWork = 30, RemainWork = 0, CompletedProcess = 100, TodayComments = "Coding  60%", YesterdayComments = "N/a" };

        storyList.Add(story1);
        storyList.Add(story3);
        storyList.Add(story2);
        storyList.Add(story4);
        storyList.Add(story5);
        storyList.Add(story6);
    }
    public MainViewModel()
    {
        this.SelectStory = new Story();
        this.LoadStory();
        this.AddIssueCommand = new DelegateCommand(new Action(this.AddIssueCommandExecute));
    }

    public void AddIssueCommandExecute()
    {

        if (SelectStory != null)
        {
            System.Windows.MessageBox.Show("Add an Issue" + SelectStory.Title + "!");
        }
        else
        {
            System.Windows.MessageBox.Show("choose an story first!");
        }
        //System.Windows.MessageBox.Show("record" + RecordIndex);
    }
}![What I need][1]

Thank you very much

Sheridan
  • 68,826
  • 24
  • 143
  • 183
Echo
  • 53
  • 1
  • 1
  • 3
  • why do you need the selected item value for context menu? do addissuecommand and burndowncommand work on selected item? – Nitin Sep 04 '13 at 10:20
  • 2
    Binding `SelectedItem` to a property in the `viewmodel` and use that. – Bolu Sep 04 '13 at 10:27
  • Yes, the addIssueCommand and burndownCommand will work on it. Thank you @nit – Echo Sep 05 '13 at 02:31

4 Answers4

10

Expanding on Bolu's comment you could use SelectedItem to get the current item. Below is a quick example:

<DataGrid ItemsSource="{Binding Source}" SelectedItem="{Binding SelectedItemProperty, Mode=TwoWay}">
    <DataGrid.ContextMenu>
        <ContextMenu>
            <MenuItem Command="{Binding MyCommand}" Header="MyCommand"/>
        </ContextMenu>
    </DataGrid.ContextMenu>
    <DataGrid.Columns>
        <DataGridTextColumn Header="Name" Binding="{Binding Key, Mode=TwoWay}" Width="1*"/>
        <DataGridTextColumn Header="Value" Binding="{Binding Value, Mode=TwoWay}" Width="3*"/>
    </DataGrid.Columns>
</DataGrid>

SelectedItem is now bound to SelectedItemProperty in the ViewModel.

Community
  • 1
  • 1
MartinThé
  • 706
  • 7
  • 19
  • 1
    This seems to be the cleanest solution, and works for me. Not sure why it doesn't have the up votes. – gap Jun 16 '16 at 15:46
6

This is a common problem in WPF. The solution is to utilise a Tag property in the item DataTemplate to hold the data item. First, let's add this part:

<DataTemplate DataType="{x:Type YourDataTypeXmlNamespace:YourDataType}">
    <Border Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType={
x:Type YourViewsXmlNamespace:YourViewWhereThisIsDeclared}}}">
        ...
    </Border>
</DataTemplate>

Now that we have access to the DataContext of the UserControl which can be found in the Tag property of each data object, let's bind to this from the ContextMenu... we do it using a handy property called PlacementTarget:

<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={
RelativeSource Self}}">
    <MenuItem Header="Do Something" Command="{Binding YourCommandInYourViewModel}" 
CommandParameter="{Binding YourCollection.CurrentItem}">
        ...
    </MenuItem>
</ContextMenu>

One thing to note is the YourCollection.CurrentItem property shown in the CommandParameter above. The CurrentItem property is a property that I added into my collection classes to bind to the SelectedItem properties of collection controls in the UI. If you don't have one of these, it's ok, but you will need a property that is bound to the SelectedItem property of your collection control for this to work. For my example, I have this:

<ListBox ItemsSource="{Binding YourCollection}" SelectedItem="{Binding 
YourCollection.CurrentItem}" />
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Thank you very much for your help,@Sheridan. I have solved this problem. Accurately, I use another method to solve it. I create a MouseRightButton class – Echo Sep 05 '13 at 10:34
  • I add the dependency property to the UIElement right click. And then I add the " – Echo Sep 06 '13 at 02:15
  • Because this comment does not allow to paste coding. May be I will write a blog and paste the link here later – Echo Sep 06 '13 at 02:19
  • @Echo, it is common for question authors to add a section to the end of their *question* clearly labelled 'UPDATE' or 'EDIT' or 'SOLUTION' and to include a description of their found solution. – Sheridan Sep 06 '13 at 08:01
  • @Sheridan I prefer the model whereby if someone answers their own question they just post it as a separate answer and mark that as the accepted one. The who is not important on this site, just the what! – Stephen Holt Mar 07 '14 at 11:58
  • Thanks for sharing your preference with us @StephenHolt. I would say that it very much depends on the situation... I've seen question authors adding an answer and accepting it even though their answer was made entirely from content taken from the answers that they received from other users. In this situation, I'd say that they should add an update to the end of their question. I believe that you're talking about a situation where a question author has some new found solution to their own problem, that was *not* taken from their received answers. – Sheridan Mar 07 '14 at 12:10
  • I personally think that is also about the time invested by answer authors. I think that it is only fair for a question author to acknowledge the time spent by the users that are helping them, but I'd certainly agree that there *are* times when a question author *should* answer their own question. – Sheridan Mar 07 '14 at 12:12
  • @Sheridan - I agree with you. Distilling other answers into your own is just denying credit where its due. But if nobody gets to the correct answer and you later work out what to do by yourself, it's most useful to list it as an answer. – Stephen Holt Mar 12 '14 at 10:21
1

bigworld12 has close to the right answer here, but it breaks if your context menu is templated. Try:

DataGridRow row =
            ((sender as MenuItem)?.GetAncestors()
               ?.FirstOrDefault(dpo => dpo.GetType() == typeof(ContextMenu)) as ContextMenu)
               ?.PlacementTarget as DataGridRow;

for the code-behind. I used nullable operators in case you somehow get here without the expected parent tree and target (maybe by triggering the context menu while off the grid.)

Byrel Mitchell
  • 129
  • 1
  • 7
0

I know this is an old question, but i wanted to share this very easy solution.

XAML:

<ContextMenu x:Key="MyRowMenu1" DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}">
     <MenuItem Click="MenuItem_Click" Header="Add to Favourites"/>
     <MenuItem Header="Copy" >
     <MenuItem Header="Copy Name"/>
     <MenuItem Header="Copy ID" />
     <MenuItem Header="Copy IP Adddress"/>
    </MenuItem>
</ContextMenu>

Code behind:

Private Sub MenuItem_Click(sender As Object, e As RoutedEventArgs)
    Dim row As DataGridRow = DirectCast(DirectCast(DirectCast(sender, MenuItem).GetParentObject, ContextMenu).PlacementTarget, DataGridRow)
    'replace with what ever item you want
    Dim srvr As Server = DirectCast(row.Item, Server)

End Sub
Tobias Tengler
  • 6,848
  • 4
  • 20
  • 34
Ahmed Fwela
  • 955
  • 1
  • 13
  • 32