1

I'm attempting to access a listview subitem. I have to insert columns into this ListView/GridView dynamically as this program accesses multiple databases with different value types. If need be I can portion out the database searching into multiple tabs, but for ease of use, I'd rather not take this route.

I've been searching for roughly 3-4 days for a solution. This class organizes the subitems data from a OleDB query. Here is a snippet of the code I'm using:

    public class Repair
    {
        public string RP { get; set; }
        public string SN { get; set; }
        public DateTime REC { get; set; }
        public DateTime START { get; set; }
        public string CUST { get; set; }
        public string SP { get; set; }
        public string TECH { get; set; }
        public string STATUS { get; set; }
        public string MODEL { get; set; }
        public string NOTES { get; set; }
        public DateTime ACCUSED { get; set; }
        public string ACCNOTES { get; set; }
        public int ID { get; set; }
    }
    public IList<Repair> OpenRepair { get; set; }

It then inserts the data into the ListView with the following snippet:

var gridView = new GridView();
this.searchListView.View = gridView;
gridView.Columns.Add(new GridViewColumn { Header = "RMA #", DisplayMemberBinding = new System.Windows.Data.Binding("RP") });
gridView.Columns.Add(new GridViewColumn { Header = "Serial", DisplayMemberBinding = new System.Windows.Data.Binding("SN") });
gridView.Columns.Add(new GridViewColumn { Header = "Recieved", DisplayMemberBinding = new System.Windows.Data.Binding("REC") });
gridView.Columns.Add(new GridViewColumn { Header = "Start", DisplayMemberBinding = new System.Windows.Data.Binding("START") });
gridView.Columns.Add(new GridViewColumn { Header = "Customer", DisplayMemberBinding = new System.Windows.Data.Binding("CUST") });
gridView.Columns.Add(new GridViewColumn { Header = "Sales Person", DisplayMemberBinding = new System.Windows.Data.Binding("SP") });
gridView.Columns.Add(new GridViewColumn { Header = "Technician", DisplayMemberBinding = new System.Windows.Data.Binding("TECH") });
gridView.Columns.Add(new GridViewColumn { Header = "Status", DisplayMemberBinding = new System.Windows.Data.Binding("STATUS") });
gridView.Columns.Add(new GridViewColumn { Header = "Repair Notes", DisplayMemberBinding = new System.Windows.Data.Binding("NOTES") });
gridView.Columns.Add(new GridViewColumn { Header = "Accidental Used Date", DisplayMemberBinding = new System.Windows.Data.Binding("ACCUSED") });
gridView.Columns.Add(new GridViewColumn { Header = "Accidental Notes", DisplayMemberBinding = new System.Windows.Data.Binding("ACCNOTES") });
gridView.Columns.Add(new GridViewColumn { Header = "ID", DisplayMemberBinding = new System.Windows.Data.Binding("ID"), Width = 0 });
using (OleDbConnection cn = new OleDbConnection(@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=X:\***"))
{
    cmd.Connection = cn;
    cmd.CommandText = sqlstatement;
    try
    {
         cn.Open();
         dr = cmd.ExecuteReader();
         if (dr.HasRows)
         {
             while (dr.Read())
             {
                  OpenRepair = new List<Repair>();
                  if (dr[10].ToString() == "True")
                  {
                        OpenRepair.Add(
                        new Repair()
                        {
                           RP = dr[0].ToString(),
                           SN = dr[1].ToString(),
                           REC = Convert.ToDateTime(dr[2].ToString()).Date,
                           START = Convert.ToDateTime(dr[3].ToString()).Date,
                           CUST = dr[4].ToString(),
                           SP = dr[5].ToString(),
                           TECH = dr[6].ToString(),
                           STATUS = dr[7].ToString(),
                           MODEL = dr[8].ToString(),
                           NOTES = dr[9].ToString(),
                           ACCUSED = Convert.ToDateTime(dr[11].ToString()),
                           ACCNOTES = dr[12].ToString(),
                           ID = Convert.ToInt32(dr[13].ToString())
                        });
                    }
                    else
                    {
                         OpenRepair.Add(
                         new Repair()
                         {
                             RP = dr[0].ToString(),
                             SN = dr[1].ToString(),
                             REC = Convert.ToDateTime(dr[2].ToString()).Date,
                             START = Convert.ToDateTime(dr[3].ToString()).Date,
                             CUST = dr[4].ToString(),
                             SP = dr[5].ToString(),
                             TECH = dr[6].ToString(),
                             STATUS = dr[7].ToString(),
                             MODEL = dr[8].ToString(),
                             NOTES = dr[9].ToString(),
                             ID = Convert.ToInt32(dr[13].ToString())
                          });
                      }
                  searchListView.Items.Add(OpenRepair);
               }

The XAML behind the ListView:

<ListView ItemsSource="{Binding Repair}" SelectionMode="Single" x:Name="searchListView" Margin="0,63,0,0" Background="DarkGray" MouseDoubleClick="searchListView_MouseDoubleClick">
                    <ListView.View>
                        <GridView />
                    </ListView.View>
                </ListView>

I've attempted to grab the Subitem values within the ListView/GridView with a few approaches. It has not been successful on either of these codes:

                Repair lvi = (Repair)searchListView.SelectedItems[0];
                System.Windows.MessageBox.Show(lvi.RP + " " + lvi.SN + " " + lvi.SP);

&

          Repair lvi = (Repair)this.customersListView.SelectedItem;
          MessageBox.Show(string.Format("Repair: {1}{0}Serial Number:{2}", Environment.NewLine, lvi.RP, lvi.SN));

Even attempted a Windows Form approach, which obviously did not work.

Could someone please at least point me in the correct direction? The error message that I've been obtaining makes sense being it cannot be cast from the object Generic List to class Repair, however, since I am fairly new to WPF, I'm stumped on how to get passed this message!

feinrar
  • 13
  • 3
  • Why are you using an intermediate list? Are you using `ItemsSource` or not? – Pragmateek Sep 10 '14 at 20:09
  • You could do `((List)searchListView.SelectedValue)[0].SN`... – Pragmateek Sep 10 '14 at 20:11
  • @Pragmateek I must have been attempting some suggestion I had found to get the selected value and left that in by mistake. I am not using ItemsSource. My apologize! I also attempted to use your suggestion, I was able to successfully bring the subitem over, and will be what I will be using! Thank you! If you were to put your comment in an answer, I will accept it. – feinrar Sep 10 '14 at 21:10
  • Glad it works! :) As suggested I've added a complete answer. – Pragmateek Sep 10 '14 at 21:28

2 Answers2

1

I tested your code and it works for me, so you probably have got some mistake in the part that you didn't show. But let me try to win you over for using the MVVM pattern, it will make your life so much easier in the long run...

The ViewModel and your Repair class (swapped DateTime for string):

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;

namespace WpfApplication1.Models
{
    public class ListViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public ObservableCollection<Repair> Repairs { get; private set; }
        public ActionCommand DoubleClickCommand { get; private set; }

        private Repair _selectedRepair;
        public Repair SelectedRepair { get { return _selectedRepair; } set { _selectedRepair = value; OnPropertyChanged("SelectedRepair"); } }

        public ListViewModel()
        {
            this.Repairs = new ObservableCollection<Repair>();
            this.Repairs.Add(new Repair { RP = "1000", SN = "A", START = DateTime.Today.ToString("d"), CUST = "C", ID = 0 });
            this.Repairs.Add(new Repair { RP = "2000", SN = "D", REC = DateTime.Today.AddDays(-2).ToString("d"), CUST = "E", ID = 1 });

            this.DoubleClickCommand = new ActionCommand(DoubleClick);
        }

        private void DoubleClick()
        {
            // do whatever (probably not show a MessageBox..)
            MessageBox.Show(string.Format("Repair: {1}{0}Serial Number:{2}", Environment.NewLine, this.SelectedRepair.RP, this.SelectedRepair.SN));
        }
    }

    public class ActionCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;
        private Action _action;
        public ActionCommand(Action action) { _action = action; }
        public bool CanExecute(object parameter) { return true; }
        public void Execute(object parameter) { if (_action != null) _action(); }
    }

    public class Repair
    {
        public string RP { get; set; }
        public string SN { get; set; }
        public string REC { get; set; }
        public string START { get; set; }
        public string CUST { get; set; }
        public string SP { get; set; }
        public string TECH { get; set; }
        public string STATUS { get; set; }
        public string MODEL { get; set; }
        public string NOTES { get; set; }
        public string ACCUSED { get; set; }
        public string ACCNOTES { get; set; }
        public int ID { get; set; }
    }
}

The View:

<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:models="clr-namespace:WpfApplication1.Models"  
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        Title="MainWindow" Height="350" Width="500">
    <Window.DataContext>
        <models:ListViewModel />
    </Window.DataContext>
    <ListView ItemsSource="{Binding Repairs}" SelectedItem="{Binding SelectedRepair}" SelectionMode="Single" Margin="0,63,0,0" Background="DarkGray">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseDoubleClick">
                <i:InvokeCommandAction Command="{Binding DoubleClickCommand}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
        <ListView.View>
            <GridView>
                <GridViewColumn Header="RMA #" DisplayMemberBinding="{Binding RP}" />
                <GridViewColumn Header="Serial" DisplayMemberBinding="{Binding SN}"/>
                <GridViewColumn Header="Recieved" DisplayMemberBinding="{Binding REC}"/>
                <GridViewColumn Header="Start" DisplayMemberBinding="{Binding START}"/>
                <GridViewColumn Header="Customer" DisplayMemberBinding="{Binding CUST}"/>
                <GridViewColumn Header="Sales Person" DisplayMemberBinding="{Binding SP}"/>
                <GridViewColumn Header="Technician" DisplayMemberBinding="{Binding TECH}"/>
                <GridViewColumn Header="Status" DisplayMemberBinding="{Binding STATUS}"/>
                <GridViewColumn Header="Repair Notes" DisplayMemberBinding="{Binding NOTES}"/>
                <GridViewColumn Header="Accidental Used Date" DisplayMemberBinding="{Binding ACCUSED}"/>
                <GridViewColumn Header="Accidental Notes" DisplayMemberBinding="{Binding ACCNOTES}"/>
                <GridViewColumn Header="ID" DisplayMemberBinding="{Binding ID}" Width="0" />
            </GridView>
        </ListView.View>
    </ListView>
</Window>
Mike Fuchs
  • 12,081
  • 6
  • 58
  • 71
  • I was reading into the MVVM pattern, and I definitely agree it would make things much much easier, but the problem I have is it is difficult to make it dynamic. I am dealing with a few people that would rather have only one listview to look at. If it were more tech savy people, sure, but unfortunately it isn't. The missing code was the Catch and Finally within after the Try procedure. Nothing was bringing down the house in that case. – feinrar Sep 10 '14 at 21:10
  • Thank you for putting your time and effort into this! For future programs I make, I definitely will be doing the MVVM model, I just unfortunately cannot in this one. – feinrar Sep 10 '14 at 21:16
  • No worries. As for MVVM, it took me some time to think that way too, but I feel there is no case that cannot be programmed inside that pattern. In very complex scenarios, behaviors are a missing link, but usually not even that is necessary. – Mike Fuchs Sep 11 '14 at 06:50
0

Following my comment, here is a more complete answer, for future reference.

The selected object (the one displayed by the SelectedItem) is available through SelectedValue.

And as you use List then you should do the following:

((List<Repair>)searchListView.SelectedValue)[0].SN
Pragmateek
  • 13,174
  • 9
  • 74
  • 108