Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binding to Children.Count

Tags:

c#

binding

wpf

xaml

I'm using a binding of the form

{Binding RelativeSource={RelativeSource Self}, Path=Children.Count, Converter={StaticResource CountToDimensionConverter}, ConverterParameter=Rows}

Despite adding the children in xaml, when I break in the converter, the value is always 0.

What I assume is going on is that the children are not added until after this binding is called.

I also assume that the binding is broken after it's been called once because .Count is a readonly property (I've had a similar problem before where I had to add an empty setter in the property to maintain the binding and fool WPF) hence the binding not updating once children are added.

However, I'm stuck on the bit where you come up with a solution for the problem and make it work... =/

<UniformGrid x:Name="MyUniformGrid"
     Rows="{Binding RelativeSource={RelativeSource Self}, Path=Children.Count, Converter={StaticResource CountToDimensionConverter}, ConverterParameter=R}"
     Columns="{Binding RelativeSource={RelativeSource Self}, Path=Children.Count, Converter={StaticResource CountToDimensionConverter}, ConverterParameter=C}">
    <Button Content="Hello, World!" />
    <Button Content="Hello, World!" />
    <Button Content="Hello, World!" />
    <Button Content="Hello, World!" />
    <Button Content="Hello, World!" />
    <Button Content="Hello, World!" />
   </UniformGrid>

Thanks, Rabit

like image 618
Dead.Rabit Avatar asked Jan 22 '23 14:01

Dead.Rabit


1 Answers

That's because UIElementCollection (the type of the Children property) doesn't raise notifications when a new item is added or removed, so the binding isn't refreshed

You could, however, create your own custom UniformGrid, and override the CreateUIElementCollection property to create an instance of a custom collection that inherits UIElementCollection and implements INotifyCollectionChanged.

Here's a basic implementation :

ObservableUIElementCollection

public class ObservableUIElementCollection : UIElementCollection, INotifyCollectionChanged, INotifyPropertyChanged
{
    public ObservableUIElementCollection(UIElement visualParent, FrameworkElement logicalParent)
        : base(visualParent, logicalParent)
    {
    }

    public override int Add(UIElement element)
    {
        int index = base.Add(element);
        var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, element, index);
        OnCollectionChanged(args);
        OnPropertyChanged("Count");
        OnPropertyChanged("Item[]");
        return index;
    }

    public override void Clear()
    {
        base.Clear();
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        OnPropertyChanged("Count");
        OnPropertyChanged("Item[]");
    }

    public override void Insert(int index, UIElement element)
    {
        base.Insert(index, element);
        var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, element, index);
        OnCollectionChanged(args);
        OnPropertyChanged("Count");
        OnPropertyChanged("Item[]");
    }

    public override void Remove(UIElement element)
    {
        int index = IndexOf(element);
        if (index >= 0)
        {
            RemoveAt(index);
            var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, element, index);
            OnCollectionChanged(args);
            OnPropertyChanged("Count");
            OnPropertyChanged("Item[]");
        }
    }

    public override UIElement this[int index]
    {
        get
        {
            return base[index];
        }
        set
        {
            base[index] = value;
            var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, index);
            OnCollectionChanged(args);
            OnPropertyChanged("Item[]");
        }
    }

    public event NotifyCollectionChangedEventHandler CollectionChanged;
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        var handler = CollectionChanged;
        if (handler != null)
            handler(this, e);
    }

    protected virtual void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

MyUniformGrid

public class MyUniformGrid : UniformGrid
{
    protected override UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent)
    {
        return new ObservableUIElementCollection(this, logicalParent);
    }
}

XAML

<local:MyUniformGrid x:Name="MyUniformGrid"
     Rows="{Binding RelativeSource={RelativeSource Self}, Path=Children.Count, Converter={StaticResource CountToDimensionConverter}, ConverterParameter=R}"
     Columns="{Binding RelativeSource={RelativeSource Self}, Path=Children.Count, Converter={StaticResource CountToDimensionConverter}, ConverterParameter=C}">
    <Button Content="Hello, World!" />
    <Button Content="Hello, World!" />
    <Button Content="Hello, World!" />
    <Button Content="Hello, World!" />
    <Button Content="Hello, World!" />
    <Button Content="Hello, World!" />
</local:MyUniformGrid>
like image 146
Thomas Levesque Avatar answered Jan 30 '23 12:01

Thomas Levesque