2
votes

I have a tab control on a page; its items are bound back to my ViewModel, which also exposes an ActiveTabItemIndex which is bound (two way) to the SelectedIndex property in my xaml, and which implements INotifyPropertyChanged so that my TabControl knows when to update.

This is (I understand) the MVVM-correct way to do things, and works 99% properly.

class MainWindowViewModel : BaseViewModel, INotifyPropertyChanged
{
    ObservableCollection<TabItemViewModel> _TabItems;
    int _ActiveTabItemIndex;

    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(name));
    }

    void _TabItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            _ActiveTabItemIndex = _TabItems.IndexOf((TabItemViewModel)e.NewItems[0]);
        RaisePropertyChanged("ActiveTabItemIndex");
    }

    public ObservableCollection<TabItemViewModel> TabItems
    {
        get
        {
            if (_TabItems == null)
            {
                _TabItems = new ObservableCollection<TabItemViewModel>();
                _TabItems.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_TabItems_CollectionChanged);
            }
            return _TabItems;
        }
    }

    public int ActiveTabItemIndex
    {
        get
        {
            return _ActiveTabItemIndex;
        }
        set
        {
            _ActiveTabItemIndex = value;
        }
    }
}

This way any manipulations I make to my TabItems collection are reflected on the TabControl, and when I add a new item, it is automatically selected. This works a treat; however, when adding the very first item to an empty tab control, it looks like this:

Tab contents displayed but tab not selected

The tab contents are displayed, but the tab is not selected. I need to manually click the tab to get it to look right:

Tab contents displayed and tab selected

It is as though there is some kind of disconnect between the drawing of the tabs and the drawing of their contents. I know the binding is working because subsequent tabs are handled correctly and if I remove the binding altogether then the first page does not show its contents until the tab is manually selected. If anyone has seen this or can shed some light it would be very much appreciated! Thank you all :)

1
Maybe the same fix as this issue (in winforms) applies.Gert Arnold

1 Answers

2
votes

Only raise your property change events in the setter; you can think of it as allowing the property itself to dictate what "changed" means, and by extension, giving it control over when the event is fired (and makes it do what you expect):

public int ActiveTabItemIndex
{
    get{ return _ActiveTabItemIndex; }
    set
    {
        if(_ActiveTabItemIndex != value)
        {
            _ActiveTabItemIndex = value;
            RaisePropertyChanged("ActiveTabItemIndex");
        }
    }
}

Just change

_ActiveTabItemIndex = _TabItems.IndexOf(...);

to

ActiveTabItemIndex = _TabItems.IndexOf(...);

and remove the RaisePropertyChanged call from _TabItems_CollectionChanged

There will be times when you'll need raise property changed notifications outside a property's setter but that is for a far more complicated day :)

As an aside, INotifyPropertyChanged should be implemented on your BaseViewModel. Check out the absolutely fantastic MVVM Light Toolkit - it has all the code you'd otherwise have to duplicate in every project that you use MVVM in.