2

I have a ListBox with a UniformGrid layout and I'm trying to change the "Columns" property, but I don't know the best way of doing that. I tried binding to a property or to create a new layout programatically, but I can't figure it out.

<ListBox x:Name="ImagesList" ItemsSource="{Binding Path=GridImages}">
        <ListBox.ItemsPanel>

            <ItemsPanelTemplate>
                <UniformGrid IsItemsHost="True" Columns="3" VerticalAlignment="Top" />
            </ItemsPanelTemplate>

        </ListBox.ItemsPanel>
</ListBox>

I'm trying to change between 1 and 3 columns, when the user click on two buttons. I've tried binding with Columns="{Binding Path=MyColumnCount}", but it never changes, and tried to set a x:Name and access from my code, without success. I also tried to instantiate a new UniformGrid, but I've read that I need a factory for that, so I can't set a different Columns value.

Justin XL
  • 38,763
  • 7
  • 88
  • 133
André Gil
  • 624
  • 7
  • 13

1 Answers1

2

I thought maybe the ItemsPanelTemplate didn't inherit the ListBox' DataContext, but it does, so your Binding should work:

<ListBox x:Name="ImagesList" ItemsSource="{Binding Path=GridImages}" >
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid IsItemsHost="True" Columns="{Binding Path=MyColumnCount}"
                         VerticalAlignment="Top" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>

    <ListBox.ItemTemplate>
        <DataTemplate>
            <Image Source="{Binding}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Try it with this simple ViewModel which updates MyColumnCount on a timer:

public class ImagesVM : INotifyPropertyChanged
{
    private System.Threading.Timer _timer;
    private int _colIncrementor = 0;

    public ImagesVM()
    {
        _timer = new System.Threading.Timer(OnTimerTick, null,
                                            TimeSpan.FromSeconds(1),
                                            TimeSpan.FromSeconds(1));
        _gridImages = new string[] {
            "http://www.anbg.gov.au/images/flags/semaphore/a-icon.gif",
            "http://www.anbg.gov.au/images/flags/semaphore/b-icon.gif",
            "http://www.anbg.gov.au/images/flags/semaphore/c-icon.gif",
            "http://www.anbg.gov.au/images/flags/semaphore/d-icon.gif",
            "http://www.anbg.gov.au/images/flags/semaphore/e-icon.gif",
            "http://www.anbg.gov.au/images/flags/semaphore/f-icon.gif",
        };
    }
    private void OnTimerTick(object state)
    {
        this.MyColumnCount = (_colIncrementor++ % 3) + 1;
    }

    private int _myColumnCount = 3;
    public int MyColumnCount
    {
        get { return _myColumnCount; }
        set
        {
            _myColumnCount = value;
            this.PropertyChanged(this, new PropertyChangedEventArgs("MyColumnCount"));
        }
    }

    private string[] _gridImages = null;
    public string[] GridImages
    {
        get { return _gridImages; }
    }

    public event PropertyChangedEventHandler PropertyChanged = (s, e) => { };
}
Sphinxxx
  • 12,484
  • 4
  • 54
  • 84
  • Thanks, the binding is working now! The only problem is that I have to set the `DataContext` everything I want the property to be refreshed. Like `ImagesList.DataContext = null; ImagesList.DataContext = this;`. But that might be because I'm binding directly to the `MyWin : Windows` I guess. It's not a `Something : INotifyPropertyChanged` – André Gil Jun 28 '13 at 23:00
  • 1
    Ok, i see. `INotifyPropertyChanged` and its `PropertyChanged` event is the secret to updating bound values. But if the `MyColumnCount` is a property in your `Window`, you can make it a `DependencyProperty`, which has its own magical update mechanism. This post has some useful links: [What is a dependency property?](http://stackoverflow.com/questions/617312/what-is-a-dependency-property) – Sphinxxx Jun 29 '13 at 16:13