Search code examples
c#wpfxamlxaml-binding

Observable Stack<T> is not working on push


I have copied ObservableStack from Stackoverflow answer.

public class ObservableStack<T> : Stack<T>, INotifyCollectionChanged, INotifyPropertyChanged
    {
        public ObservableStack()
        {
        }

        public ObservableStack(IEnumerable<T> collection)
        {
            foreach (var item in collection)
                base.Push(item);
        }

        public ObservableStack(List<T> list)
        {
            foreach (var item in list)
                base.Push(item);
        }


        public new virtual void Clear()
        {
            base.Clear();
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        public new virtual T Pop()
        {
            var item = base.Pop();
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item));
            return item;
        }

        public new virtual void Push(T item)
        {
            base.Push(item);
            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
        }


        public virtual event NotifyCollectionChangedEventHandler CollectionChanged;


        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            this.RaiseCollectionChanged(e);
        }

        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            this.RaisePropertyChanged(e);
        }


        protected virtual event PropertyChangedEventHandler PropertyChanged;


        private void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (this.CollectionChanged != null)
                this.CollectionChanged(this, e);
        }

        private void RaisePropertyChanged(PropertyChangedEventArgs e)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, e);
        }


        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
        {
            add { this.PropertyChanged += value; }
            remove { this.PropertyChanged -= value; }
        }
    }

And now I am trying to Bind the visibility of button to Stack.Count by using following Converter class:

public class StackToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            var collection = (Stack<int>)value;
            return collection.Count > 0  ? Visibility.Visible : Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }

I defined stack in code behind as a public property

public ObservableStack<int> myStack;

public MainPage()
{
            myStack = new ObservableStack<int>();
            this.InitializeComponent(); 
            myButton.DataContext = myStack;
}

and XAML is as follow:

 <Page.Resources>
        <Converters:StackToVisibilityConverter x:Key="StackToVisibilityConverter"/>
    </Page.Resources>


<Button x:Name="myButton" Content="Test Button" Visibility="{Binding Converter={StaticResource StackToVisibilityConverter}}"/>

Now when I push an int using another button click event, the button is still invisible, what am I doing wrong?


Solution

  • The stack itself is not changed to another stack, so the binding is not reevaluated, when you pop or push.

    You should bind to Count property instead, and change the converter to convert the count to visibility.

    <Button x:Name="myButton" Content="Test Button" Visibility="{Binding Count, Converter={StaticResource GreaterThanEqualIntToVisibilityConverter}}"/>
    

    Converter:

    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var count = (int)value;
        return count > 0 ? Visibility.Visible : Visibility.Collapsed;
    }
    

    And you want the stack to notify that Count changed in the ObservableStack implementation.

    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        this.RaiseCollectionChanged(e);
        RaisePropertyChanged(new PropertyChangedEventArgs("Count"));
    }