0

If I create a simple ListView with an auto-grow column, it only auto grows the first "batch" items are added. Why does this happen, and is there any way to fix it?

I've tried both using an ObservableCollection (example shown), and manually adding items to LstItems.Items, same behaviour. The first time you click BtnAdd it auto-grows as expected, subsequent times it does not.

XAML:

    <ListView Margin="12,12,12,41" Name="LstItems">
        <ListView.View>
            <GridView>
                <GridViewColumn Width="100" Header="Row 1" DisplayMemberBinding="{Binding Path=Col1}" />
                <GridViewColumn Width="Auto" Header="Row 2" DisplayMemberBinding="{Binding Path=Col2}" />
            </GridView>
        </ListView.View>
    </ListView>

Code:

public partial class Window1 : Window
{
    private readonly ObservableCollection<MyData> _data = new ObservableCollection<MyData>();

    private const int NumRepeats = 2;
    private const int EnumerationIncrement = 3;

    private int _enumerationCount = 3;

    public Window1()
    {
        InitializeComponent();
        LstItems.ItemsSource = _data;
    }

    private void BtnAdd_Click(object sender, RoutedEventArgs e)
    {
        for (int i = 1; i <= NumRepeats; i++)
        {
            string data = String.Join(", ", Enumerable.Repeat("Test Data", _enumerationCount));
            _data.Add(new MyData { Col1 = "Test", Col2 = data });
            _enumerationCount += EnumerationIncrement;
        }
    }
}
Lee
  • 1,591
  • 1
  • 14
  • 28
  • Did you assign the `_data.CollectionChanged ` to a method to update the info? It's not included in your example. – Felix Castor Aug 06 '14 at 12:43
  • I didn't, I think ListView listens to that event itself though. The problem is not that rows aren't going in, it's that the column does not stretch horizontally. – Lee Aug 06 '14 at 12:56
  • You datacontext of the listview is to 1 single item (MyData) I'm guessing this is accidental? you should leave the datacontext and bind the itemSource of the Listview to _data instead – JKennedy Aug 06 '14 at 13:00
  • I have seen this behavior also. It only sizes the first screen paint. It would be a lot of overhead to size each paint. And if a user resized a column they probably don't what it changed. Why not just give column 2 the rest of the space? – paparazzo Aug 06 '14 at 13:26
  • Can you post that as an answer Blam? I want to comment on that further and it'll get messy in this comments section. – Lee Aug 06 '14 at 13:36
  • @Lee You should @ so it shows up in my queue. – paparazzo Aug 06 '14 at 22:17

2 Answers2

1

You datacontext of the listview is to 1 single item (MyData) I'm guessing this is accidental? you should leave the datacontext and bind the itemSource of the Listview to _data instead

XAML

 <ListView Margin="12,12,12,41" Name="LstItems" ItemSource="{Binding _data}">
        <ListView.View>
            <GridView>
                <GridViewColumn Width="100" Header="Row 1" DisplayMemberBinding="{Binding Path=Col1}" />
                <GridViewColumn Width="Auto" Header="Row 2" DisplayMemberBinding="{Binding Path=Col2}" />
            </GridView>
        </ListView.View>
    </ListView>

Code:

public partial class Window1 : Window
{
    public ObservableCollection<MyData> _data = new ObservableCollection<MyData>();

    private const int NumRepeats = 2;
    private const int EnumerationIncrement = 3;

    private int _enumerationCount = 3;

    public Window1()
    {
        InitializeComponent();
    }

    private void BtnAdd_Click(object sender, RoutedEventArgs e)
    {
        for (int i = 1; i <= NumRepeats; i++)
        {
            string data = String.Join(", ", Enumerable.Repeat("Test Data", _enumerationCount));
            _data.Add(new MyData { Col1 = "Test", Col2 = data });
            _enumerationCount += EnumerationIncrement;
        }
    }
}

I also just spotted probably the main reason for your code not working as you expected. You are pushing the LstItems.ItemsSource in the Window1 constructor. you are not binding the ItemSource. So when _data is updated the ItemSource doesn't know about it. Maybe a quick solution for you without learning about DataBinding would be to do:

    private void BtnAdd_Click(object sender, RoutedEventArgs e)
    {
        for (int i = 1; i <= NumRepeats; i++)
        {
            string data = String.Join(", ", Enumerable.Repeat("Test Data", _enumerationCount));
            _data.Add(new MyData { Col1 = "Test", Col2 = data });
            _enumerationCount += EnumerationIncrement;
        }
        LstItems.ItemSource = _data;
    }

Although I would highly recommend learning about databinding if you have some spare time.

JKennedy
  • 18,150
  • 17
  • 114
  • 198
  • I didn't set DataContext. I set d:DataContext, which is a design time property. The "d" namespace has been set to be ignored by the compiler. It's simply there so that Resharper knows what the type of object is. So if you just pretend that the d:DataContext assignment isn't there, is there actually anything wrong with my code? – Lee Aug 06 '14 at 13:18
  • I've removed it from the question to avoid confusion – Lee Aug 06 '14 at 13:21
  • So yes, you are updating the `ObservableCollection` in the `BtnAdd_Click` method but then the ItemsSource doesn't get updated. I would add the line `LstItems.ItemSource = _data;` to the end of your `BtnAdd_Click` method and let me know if it works. – JKennedy Aug 06 '14 at 13:32
  • The problem is not that added rows do not appear in the list, they do. You don't need to set ItemSource again in the button click because it has already been set, and the ListView has already subscribed to the necessary events to be aware of changes to the collection. The problem is that after the first click, subsequent clicks, although adding the row correctly, does not then resize the column horizontally. – Lee Aug 06 '14 at 13:39
  • 1
    Oh yes, my bad. In that case I would advise looking at [this link](http://stackoverflow.com/questions/10309249/listview-gridviewcolumn-width) I have used it before and I found it worked – JKennedy Aug 06 '14 at 14:05
0

I have seen this behavior also. It only sizes the first screen paint. It would be a lot of overhead to size each paint. And if a user resized a column they probably don't what it changed. Why not just give column 2 the rest of the space? You would do that with a converter.

paparazzo
  • 44,497
  • 23
  • 105
  • 176
  • I first tried giving the column the remaining space but that wasn't a simple property setting, making the column Auto-grow seemed like a quick win, and appeared to work at first. That's the point where I simplified it down and posted this question. – Lee Aug 07 '14 at 07:43
  • I'm not sure I agree with the argument that it would be a lot of overhead to resize on each paint though. The control is already painted on load. There's no difference between a second and third rearrange (after each button press). Add to that it only resizes based on the items visible on the screen, it's not too an expensive operation either. But if that's the decision, or bug, the WPF team made - that's what we have to live with. Thanks for the confirmation! – Lee Aug 07 '14 at 07:48
  • Size to fill http://stackoverflow.com/questions/9083933/gridviewcolumn-width-adjustment/9088071#9088071 – paparazzo Aug 07 '14 at 12:46