TheBritishAreComing
TheBritishAreComing

Reputation: 1717

Can't get ListViewItem when using custom object

I've searched and searched and searched but can't find an answer.

C# and WPF, I have a single ListView with 5 columns and each column has a TextBox in it.

My custom class

public class SomeThing
{
    public String field1 { get; set; }
    public String field2 { get; set; }
    public String field3 { get; set; }
    public String field4 { get; set; }
    public String field5 { get; set; }
}

My add code

SomeThing item = new SomeThing();
lstItems.Items.Add(item);

My keydown code

private void TextBox_KeyDown(object sender, KeyEventArgs e)
    {
        if(e.Key == Key.Return || e.Key == Key.Tab)
        {
            TextBox tb = (TextBox)sender;
            Grid grid = (Grid)tb.Parent;                

            if (tb.Tag.Equals("Price"))
            {
                if(lstItems.Items.Count <= lstItems.SelectedIndex + 1) {                     
                    SomeThing item = new SomeThing();
                    lstItems.Items.Add(item);                        
                }

                lstItems.SelectedIndex = lstItems.SelectedIndex + 1;                   

                ListViewItem selectedItem = (ListViewItem)lstItems.ItemContainerGenerator.ContainerFromItem(this.lstItems.SelectedItem);

                    e.Handled = true;
            }                               

        }
    }

But

ListViewItem selectedItem = (ListViewItem)lstItems.ItemContainerGenerator.ContainerFromItem(this.lstItems.SelectedItem);

Is always null,

this.lstItems.SelectedItem 

is just an object of instance "SomeThing".

How do I get the ListView Container?

How do I focus the TextBox on the new selected row?

Please help

Upvotes: 0

Views: 246

Answers (1)

Xavier
Xavier

Reputation: 3404

It is likely that you are trying to get something from the ItemContainerGenerator that has not been generated yet at the time you ask for it. Adding an item to an ItemsControl (which ListView is a subclass of) does not immediately create a container for that item. There is a delay involved.

This is not really the ideal way to be working with ItemsControl instances. They are really designed around being used with the MVVM design pattern. However, if you need to work with it this way for some reason, then you will need to pay attention to the Status property on the ItemContainerGenerator and the associated StatusChanged event.

So, something along these lines:

    if (myItemsControlInstance.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
    {
        // You should be able to get the container using ContainerFromItem
    }
    else
    {
        // You will have to wait
        myItemsControlInstance.ItemContainerGenerator.StatusChanged += myItemsControlInstance_StatusChanged;
    }

...

void myItemsControlInstance_StatusChanged(object sender, EventArgs e)
{
    if (myItemsControlInstance.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
    {
        myItemsControlInstance.ItemContainerGenerator.StatusChanged -= myEventHandler;
        // You should be able to get the container now using ContainerFromItem.
        // However, layout hasn't been performed on it yet at this point, so there is
        // no guarantee that the item is in good condition to be messed with yet.
        LayoutUpdated += app_LayoutUpdated;
    }
}

void app_LayoutUpdated(object sender, EventArgs e)
{
    LayoutUpdated -= app_LayoutUpdated;
    if (myItemsControlInstance.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
    {
        // Now, you can finally get the container using ContainerFromItem and do something with it.
    }
    else
    {
        // It looks like more items needed to be generated...
        myItemsControlInstance.ItemContainerGenerator.StatusChanged += myItemsControlInstance_StatusChanged;
    }
}

There are a few more things you have to watch out for though when working with ItemContainerGenerator directly like this:

  • The Status on the generator can be Error. You might want to check for that, though I have never seen it happen.
  • If the ItemsControl is using a virtualizing panel to contain the items, it is possible that you are trying to access an item that is not in view and therefore doesn't exist. You can either override the type of panel used to not virtualize, such as StackPanel instead of VirtualizingStackPanel by setting the ItemsPanel property, or you can make sure the item is scrolled into view somehow before starting the process (which is a whole separate topic).

My recommendation would be to switch to an MVVM model and read up on how to work with ItemsControl in a more natural way, because doing it this way is complicated and error-prone.

Upvotes: 1

Related Questions