.NET Framework Developer Center > .NET Development Forums > Windows Presentation Foundation (WPF) > Click Events for ListView item (aka ListView cell)
Ask a questionAsk a question
 

AnswerClick Events for ListView item (aka ListView cell)

  • Friday, May 25, 2007 4:10 PMJack Cole Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Suppose the user clicks a cell in a ListView.  Is there an event that I can handle that will give me the row and column indexes of the cell that was clicked?

     

    It sounds like such a simple thing, but a couple of us have looked at the problem and came up empty handed.

Answers

  • Friday, May 25, 2007 7:09 PMJosh SmithMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I came up with this approach, hopefully it will work in your situation.

     

    Code Snippet

    <ListView x:Name="listView" ItemsSource="{Binding}">

     <ListView.View>

      <GridView AllowsColumnReorder="False">

       <GridViewColumn Header="Name">

        <GridViewColumn.CellTemplate>

         <DataTemplate>

          <Border Name="bdrIdx" Grid.Column="0">

           <TextBlock Text="{Binding Name}" />

          </Border>

         </DataTemplate>

        </GridViewColumn.CellTemplate>

       </GridViewColumn>

       <GridViewColumn Header="Age">

        <GridViewColumn.CellTemplate>

         <DataTemplate>

          <Border Name="bdrIdx" Grid.Column="1">

           <TextBlock Text="{Binding Age}" />

          </Border>

         </DataTemplate>

        </GridViewColumn.CellTemplate>

       </GridViewColumn>

      </GridView>

     </ListView.View>

     

     <ListView.ItemContainerStyle>

      <Style TargetType="ListViewItem">

       <!-- Stretch the item content so that hit testing works for the

            entire viewable surface of the ListViewItem. -->

       <Setter Property="HorizontalContentAlignment" Value="Stretch" />

      </Style>

     </ListView.ItemContainerStyle>

    </ListView>

     

     

    void listView_PreviewMouseDown(object sender, MouseButtonEventArgs e)

    {

     Border border;

     ListViewItem lvItem;

      

     if (FindBorderAndListViewItem(

      e.OriginalSource as DependencyObject,

      this.listView,

      out border,

      out lvItem))

     {

      ItemContainerGenerator generator =

        this.listView.ItemContainerGenerator;

      int rowIndex = generator.IndexFromContainer(lvItem);

     

      int columnIndex = Grid.GetColumn(border);

     

      Debug.WriteLine("Row #: " + rowIndex);

      Debug.WriteLine("Col #: " + columnIndex);

     }

    }

     

    static bool FindBorderAndListViewItem(

      DependencyObject elementUnderCursor,

      ListView listVw,

      out Border border,

      out ListViewItem lvItem)

    {

     border = null;

     lvItem = null;

     

     DependencyObject depObj = elementUnderCursor;

     while (depObj != listVw)

     {

      if (border == null && depObj is Border)

      {

       border = depObj as Border;

       // Only reference the named Border.

       if (border.Name != "bdrIdx")

        border = null;

      }

      else if (depObj is ListViewItem)

      {

       lvItem = depObj as ListViewItem;

      }

     

      depObj = VisualTreeHelper.GetParent(depObj);

     }

     

     return border != null && lvItem != null;

    }

     

     HTH!

All Replies

  • Friday, May 25, 2007 7:09 PMJosh SmithMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    I came up with this approach, hopefully it will work in your situation.

     

    Code Snippet

    <ListView x:Name="listView" ItemsSource="{Binding}">

     <ListView.View>

      <GridView AllowsColumnReorder="False">

       <GridViewColumn Header="Name">

        <GridViewColumn.CellTemplate>

         <DataTemplate>

          <Border Name="bdrIdx" Grid.Column="0">

           <TextBlock Text="{Binding Name}" />

          </Border>

         </DataTemplate>

        </GridViewColumn.CellTemplate>

       </GridViewColumn>

       <GridViewColumn Header="Age">

        <GridViewColumn.CellTemplate>

         <DataTemplate>

          <Border Name="bdrIdx" Grid.Column="1">

           <TextBlock Text="{Binding Age}" />

          </Border>

         </DataTemplate>

        </GridViewColumn.CellTemplate>

       </GridViewColumn>

      </GridView>

     </ListView.View>

     

     <ListView.ItemContainerStyle>

      <Style TargetType="ListViewItem">

       <!-- Stretch the item content so that hit testing works for the

            entire viewable surface of the ListViewItem. -->

       <Setter Property="HorizontalContentAlignment" Value="Stretch" />

      </Style>

     </ListView.ItemContainerStyle>

    </ListView>

     

     

    void listView_PreviewMouseDown(object sender, MouseButtonEventArgs e)

    {

     Border border;

     ListViewItem lvItem;

      

     if (FindBorderAndListViewItem(

      e.OriginalSource as DependencyObject,

      this.listView,

      out border,

      out lvItem))

     {

      ItemContainerGenerator generator =

        this.listView.ItemContainerGenerator;

      int rowIndex = generator.IndexFromContainer(lvItem);

     

      int columnIndex = Grid.GetColumn(border);

     

      Debug.WriteLine("Row #: " + rowIndex);

      Debug.WriteLine("Col #: " + columnIndex);

     }

    }

     

    static bool FindBorderAndListViewItem(

      DependencyObject elementUnderCursor,

      ListView listVw,

      out Border border,

      out ListViewItem lvItem)

    {

     border = null;

     lvItem = null;

     

     DependencyObject depObj = elementUnderCursor;

     while (depObj != listVw)

     {

      if (border == null && depObj is Border)

      {

       border = depObj as Border;

       // Only reference the named Border.

       if (border.Name != "bdrIdx")

        border = null;

      }

      else if (depObj is ListViewItem)

      {

       lvItem = depObj as ListViewItem;

      }

     

      depObj = VisualTreeHelper.GetParent(depObj);

     }

     

     return border != null && lvItem != null;

    }

     

     HTH!

  • Monday, May 28, 2007 9:52 PMJack Cole Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thanks Josh.  This works great an I think I can use the same technique to do some other things I want to do.
  • Monday, May 28, 2007 10:08 PMJosh SmithMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Jack,

     

    Just out of curiosity, why do you need to gain access to the row and column indexes for the "cell" in which the user clicked?  What functionality are you creating which requires that information?  Thanks.

  • Tuesday, May 29, 2007 12:55 PMJack Cole Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Josh,

     

    We want the user to be able to multiply select cells.  But not all combinations are valid.  The rows represent batches.  Some of the columns represent counts of errors in various categories for the batch.  Other columns represent the name of the batch, the date, type of batch, etc.  The user should be able to multiply select error count cells, but not cells with other kinds of information.  Also they shouldn’t be able to multiply select counts from batches of different types.

     

    When the user selects error count cells we show the individual errors that make up the count in another grid.

  • Tuesday, May 29, 2007 1:11 PMJosh SmithMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Jack,

     

    Very interesting.  Thanks for the explanation.  Good luck with the project.

  • Monday, July 23, 2007 10:47 PMAybe81 Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Hello,

     

    I've tried to convert it to VB but it doesn't work

     

    I couldn't find a substitute for the "out" keyword in C#,

    and returning both values if not null ...

     

    The two tests in the While loop simply never happens.

     

    Basically what i'm trying to do is,

     

    I implemented a Drag & Drop feature however it does Drag everytime,

    even when i'm not clicking a cell and for example, clicking a scrollbar.

     

    Do you have any idea about what could be wrong with it ?

     

     

    Thanks for your help !

     

     

    Private Sub ListViewMain_PreviewMouseDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles ListViewMain.PreviewMouseDown

    Dim border As Border

    Dim lvitem As ListViewItem

    If (FindBorderAndListViewItem(e.OriginalSource, ListViewMain, border, lvitem)) Then

    Dim generator As ItemContainerGenerator = ListViewMain.ItemContainerGenerator

    Dim rowindex As Integer = generator.IndexFromContainer(lvitem)

    Dim columnindex As Integer = Grid.GetColumn(border)

    Console.WriteLine("Row #: " + rowindex)

    Console.WriteLine("Col #: " + columnindex)

    End If

     

    End Sub

     

    Private Function FindBorderAndListViewItem(ByVal elementUnderCursor As DependencyObject, ByVal listVw As ListView, ByVal border As Border, ByVal lvitem As ListViewItem) As Boolean

    border = Nothing

    lvitem = Nothing

    Dim depobj As DependencyObject = elementUnderCursor

     

    ' Find parent

    While depobj IsNot listVw

     

    If border Is Nothing AndAlso depobj Is border Then

    border = depobj

    If border.Name <> "bdrIdx" Then border = Nothing

    ElseIf depobj Is lvitem Then

    lvitem = depobj

    End If

    ' Find next parent

    depobj = VisualTreeHelper.GetParent(depobj)

    End While

    'Return True

    End Function

     

  • Thursday, December 27, 2007 8:38 AMManjunathV Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi,

    Can we access the each cell from ListView without clicking on the particular Item or cell?

    I want change the cellTemplate for individual cells. Please tell me how to access the individual cell and apply the style for that particular cell without affecting other cells.



    Warm Regards,
    Manjunath V
  • Wednesday, July 02, 2008 2:00 PMPdeveloper Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Hi, I read your conservation. I am having a question, thought anybody of you solve my problem,
    the sceneario is I have a tree contol and a Listview control. In list view control i have three columns.
    out of three one will be empty. what i need to do is to drag the item from tree and drop it in the third blank column in the listview control? is this possible ? i need to devlope in C# 2005. help me :(

    thanks in adv.
    S/W Devep
  • Sunday, November 08, 2009 1:57 AMYuezhong Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    I have the below solution.
    I have a ListView, has 5 columns, Name, OfficeNumber, MobileNumber, HomeNumber,Email.   I want to determine which number(office,mobile,home) that a user has clicked. 
    The ContactEntry is an object holds the above properties.
    GetContactEntryList returns a ObservableCollection of ContactEntry). 

    <ListView Name="contactsListView" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Width="320"  Height="320" Visibility="Collapsed"  ItemsSource="{Binding ElementName=This, Path=GetContactEntryList}"  VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" IsSynchronizedWithCurrentItem="True"  MouseDoubleClick="contactsListView_MouseDoubleClick">
                    <ListView.ItemContainerStyle>
                        <Style TargetType="ListViewItem">
                            <Setter Property="Height" Value="20" />
                        </Style>
                    </ListView.ItemContainerStyle>
                    <ListView.View>
                        <GridView  AllowsColumnReorder="False">
                            <GridViewColumn Width="95" DisplayMemberBinding="{Binding FullName}">
                                <GridViewColumnHeader  Tag="FullName" Content="{StaticResource TEXT_NAME}"/>
                            </GridViewColumn>
                            <GridViewColumn Width="85" DisplayMemberBinding="{Binding OfficePhone}">
                                <GridViewColumnHeader  Tag="OfficePhone" Content="{StaticResource TEXT_OFFICE}"/>
                            </GridViewColumn>
                            <GridViewColumn Width="80" DisplayMemberBinding="{Binding MobilePhone}">
                                <GridViewColumnHeader  Tag="MobilePhone" Content="{StaticResource TEXT_MOBILE}"/>
                            </GridViewColumn>
                            <GridViewColumn Width="Auto"  DisplayMemberBinding="{Binding HomePhone}">
                                <GridViewColumnHeader  Tag="HomePhone" Content="{StaticResource TEXT_HOME}"/>
                            </GridViewColumn>
                            <GridViewColumn Width="Auto"  DisplayMemberBinding="{Binding Email}">
                                <GridViewColumnHeader  Tag="Email" Content="{StaticResource TEXT_EMAIL}"/>
                            </GridViewColumn>
                        </GridView>
                    </ListView.View>
                </ListView>
    

     private void contactsListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
            {
                object obj = this.contactsListView.SelectedItem;
                if (obj is ContactEntry)
                {
                    NumberType type = GetCallNumberType(this.contactsListView, e.GetPosition(this.contactsListView));
                    if (type != NumberType.UNKNOWN)
                    {
                       //we know which number user clicked
                    }
                }
            }

     public NumberType GetCallNumberType(ListView listView, Point point)
            {
                Decorator border = VisualTreeHelper.GetChild(listView, 0) as Decorator;//Border is the first child of a Listview
                ScrollViewer scroll = border.Child as ScrollViewer;
                point.X = point.X + scroll.ContentHorizontalOffset;
                scroll = null;
                border = null;
                double pStart = 0;
                double pEnd = 0;
                GridView gv = listView.View as GridView;
                for (int i = 0; i < gv.Columns.Count; i++)
                {
                    pEnd = pStart + gv.Columns[i].ActualWidth;
                    if (point.X > pStart && point.X < pEnd)
                    {
                        try
                        {
                            GridViewColumnHeader header = gv.Columns[i].Header as GridViewColumnHeader;
                            if (header != null && header.Tag is string)
                            {
                                string tag = header.Tag as string;
                                header = null;
                                gv = null;
                                if (tag.Equals("OfficePhone"))
                                {
                                    return NumberType.OFFICE_PHONE;
                                }
                                else if (tag.Equals("MobilePhone"))
                                {
                                   return NumberType.MOBILE_PHONE;
                                }
                                else if (tag.Equals("HomePhone"))
                                {
                                    return NumberType.HOME_PHONE;
                                }
                                else
                                {
                                    return NumberType.UNKNOWN;
                                }
                            }
                          
                        }
                        catch (Exception)
                        {
                            return NumberType.UNKNOWN;
                        }
                    }
                    pStart = pEnd;
                }
                return NumberType.UNKNOWN;
            }
    


    The important section is 
                Decorator border = VisualTreeHelper.GetChild(listView, 0) as Decorator;//Border is the first child of a Listview
                ScrollViewer scroll = border.Child as ScrollViewer;
                point.X = point.X + scroll.ContentHorizontalOffset;
                scroll = null;
                border = null;
                double pStart = 0;
                double pEnd = 0;
                GridView gv = listView.View as GridView;
                for (int i = 0; i < gv.Columns.Count; i++)
                {
                    pEnd = pStart + gv.Columns[i]. ActualWidth;<br/>                if (point.X > pStart && point.X < pEnd)
                    {
                        //this was clicked
                    }
                }
    
    

    If you don't use the code below, when the column resize too wide, you will not able to detect the correct column.
            Decorator border = VisualTreeHelper.GetChild(listView, 0) as Decorator;//Border is the first child of a Listview
                ScrollViewer scroll = border.Child as ScrollViewer;
                point.X = point.X + scroll.ContentHorizontalOffset;


    Hope this can help anyone out there need a solution.