locked
TabControl does not refresh when binding source changes? RRS feed

  • Question

  •  Hi,

    First time everything is ok, but when I change datacontext (I add or remove elements from a collection, so more or less tabs should show), nothing changes.

    I also use iconverter for tabs.

    I know I am doing something wrong, but I could NOT find a clear explanation (for simple people with no very deep knowledge) how to fix it.


    I set the datacontext of tab control to:

    tab_control.DataContext= presented_data.profili_tipa;

    public PresentedData presented_data = new PresentedData();

    public class PresentedData
    {
         public ObservableCollection<TypeProfiles> profili_tipa;
    }

    public class TypeProfiles
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string ime;
        public string Ime
        {
            get { return ime; }
            set
            {
                if (value != ime)
               {
                   ime = value;
                   NotifyPropertyChanged("Ime");
               }
            }
        }
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

     

    ICONVERTER CODE:
        public class TabProfilesTest : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                ObservableCollection<TypeProfiles> tipi_profilov = value as ObservableCollection<TypeProfiles>;

                if (tipi_profilov != null)
                {
                    List<TabItem> result = new List<TabItem>();
                    foreach (TypeProfiles tab in tipi_profilov)
                    {
                        TabItem ti = new TabItem();
                        ti.Header = tab.Ime;

                        result.Add(ti);
                    }
                    return result;
                }
                return null;
            }
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }

     

     

    Saturday, April 24, 2010 5:09 AM

Answers

  • Yes  I see now the problem, you convert the observable collection in a list which doesn't implement the INotifyCollectionChanged interface, more over you should create a synchronizing mechanisms between the two collection:

    var source = obect as ObservableCollection;
    var converted = new ObesrvableCollection();
    
    source.CollectionChanged += (s,e) => 
    {
      if (e.Action == NotifyCollectionChangedAction.Reset) converted.Clear();
      if (e.OldItems != null) foreach(var oldItem in e.OldItems) converted.Remove(oldItem);
      if (e.NewItems != null) foreach(var newItem in e.NewItems) converted.Insert(e.NewStartingIndex, newItem);
    }
    The other possibility is to raise a property changed event for the property with the items source.
    Wednesday, April 28, 2010 2:26 AM

All replies

  • We have noticed that sometime the controls lose the binding, although is a one way binding they behave as it would be a one time binding, this means after the first "binding" the binding is lost. Check if there is still a binding with the get binding expression method, if not try to change the binding mode to two way.
    Monday, April 26, 2010 7:46 AM
  •  Hi,

    I checked the bindig expression as you suggested and it is ok.

    mytab.GetBindingExpression ( TabControl.ItemsSourceProperty );

     

    If I try to change binding to two way, I get some strange collapse and visual studio asks me, if I want to debug explorer, so I think it does not work.

     I tried:

    ItemsSource="{Binding Converter={StaticResource TabProfilesTest}, Mode=OneWay}" - works the same

    ItemsSource="{Binding Converter={StaticResource TabProfilesTest}, Mode=TwoWay}" - explorer chrashes

     

    Can I in any way notify that I have changed binding and that TabControl should refresh. Maybe because TabControl needs iconverter I should notify it in some other way?

    Monday, April 26, 2010 10:44 AM
  • Theoretically it should work, I haven't tested it but I take a look to the code (at least for SL4). What are you as items source?

    If you change the collection, I means you add or remove items, and the collection is not implementing the INotifyCollectionChanged clearly the tab control won't take any action, in this case you can use an ObservableCollection.

    If you are changing the collection object, you assign a new collection to the property, the object must be implement the INotifyPropertyChanged interface and you have to rise the property changed event for the collection property.

    Tuesday, April 27, 2010 4:00 AM
  •  Hi,

    I am binding TabControl ItemsSource to ObservableCollection. I am binding it to profili_tipa:

    public class PresentedData
    {
         public ObservableCollection<TypeProfiles> profili_tipa;
    }

    but maybe I should do something more, because I build tabitems in iconverter?

     

    Can you be more specific with what you meant with your last sentence, because I don't understand it completely?

     

     

    Tuesday, April 27, 2010 1:21 PM
  • Yes  I see now the problem, you convert the observable collection in a list which doesn't implement the INotifyCollectionChanged interface, more over you should create a synchronizing mechanisms between the two collection:

    var source = obect as ObservableCollection;
    var converted = new ObesrvableCollection();
    
    source.CollectionChanged += (s,e) => 
    {
      if (e.Action == NotifyCollectionChangedAction.Reset) converted.Clear();
      if (e.OldItems != null) foreach(var oldItem in e.OldItems) converted.Remove(oldItem);
      if (e.NewItems != null) foreach(var newItem in e.NewItems) converted.Insert(e.NewStartingIndex, newItem);
    }
    The other possibility is to raise a property changed event for the property with the items source.
    Wednesday, April 28, 2010 2:26 AM
  •  Hi,

    thank you for your answer. I believe that this is the right way, but I am very new to programing and I dont know how to implement your suggestions.

     

    As I understand, I should implement  INotifyCollectionChanged to my iconverter? Where should I put your code and what is this:

    source.CollectionChanged += (s,e) => ?

    With my knowladge I can only do this:

    source.CollectionChanged += new NotifyCollectionChangedEventHandler(source_CollectionChanged);

     

    I will be glad if you could explain it in more detail, what I should do and what should I learn to understand how to implement it. Maybe if you could post some real example it would be perfect.

    I also dont know how to raise a property changed event?

     

     

     

     

    Wednesday, April 28, 2010 3:45 AM
  • I'm used to use lambda expression to add event handler, it's a shorted notation and very cool when you get used to it, in few words I have just set the arguments of the method (s,e) and the code for the method. Moreover doing so I take advantage of anonymous method and the closure effect, without it I couldn't use the referenced objects outside the anonymous method inside it.

     

    Wednesday, April 28, 2010 6:31 AM
  •  Hi,

    I figured it out what you meant with your code and now it works. Thank you very much for your help.

    I will study the links that you have posted (lambda expression and closure effect), and try to understand.

     

    For others, who are also like me, a bit new to the silverlight and C# programing, I made the following modifications to the iconverter class:

     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        source = value as ObservableCollection<Profiles.TypeProfiles>;
        source.CollectionChanged += new NotifyCollectionChangedEventHandler(source_CollectionChanged);

        converted = new ObservableCollection<TabItem>();
        if (source != null)
        {
            converted = new ObservableCollection<TabItem>();
            foreach (Profiles.TypeProfiles tab in source)
            {
                TabItem ti = new TabItem();
                ti.Header = tab.Ime_profila;

                converted.Add(ti);
            }
            return converted;
        }
        return null;
    }

    void source_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Reset) converted.Clear();
        if (e.OldItems != null) foreach (TabItem oldItem in e.OldItems) converted.Remove(oldItem);
        if (e.NewItems != null)
        {
            foreach (Profiles.TypeProfiles newItem in e.NewItems)
            {
                TabItem ti = new TabItem();
                ti.Header = newItem.Ime_profila;

                converted.Insert(e.NewStartingIndex, ti);
            }
        }
    }

       public object ConvertBack(object value, Type targetType, object parameter,    System.Globalization.CultureInfo culture)
      {
          return null;
       }
    }

    Wednesday, April 28, 2010 7:03 AM
  • Isn't this Converter adding a listener for CollectionChanged EVERY TIME the convert is called?  If that is called to convert 3 items, aren't there 3 notifications done when the Collection changes?
    Friday, April 5, 2013 7:34 PM