绑定到ObservableCollection <string>的Silverlight TabControl在集合更改时不更新</string>

时间:2009-08-18 14:35:54

标签: silverlight data-binding tabcontrol observablecollection

使用IValueConverter将TabControl绑定到ObservableCollection的Silverlight 3应用程序。应用程序启动时初始绑定工作(转换器调用)。对绑定集合的更改,Clear()或Add()不会反映在未调用的TabControl ...转换器中。

注意:绑定的ListBox反映了对绑定集合的更改,而TabControl则没有。

想法?

/ JHD


XAML绑定......

<UserControl.Resources>
    <local:ViewModel x:Key="TheViewModel"/>
    <local:TabConverter x:Key="TabConverter" />
</UserControl.Resources>
<StackPanel DataContext="{StaticResource TheViewModel}">
    <ListBox ItemsSource="{Binding Classnames}" />
    <controls:TabControl x:Name="TheTabControl" 
        ItemsSource="{Binding Classnames, Converter={StaticResource TabConverter}, ConverterParameter=SomeParameter}"/>
    <Button Click="Button_Click" Content="Change ObservableCollection" />
</StackPanel>

ViewModel ...

namespace DatabindingSpike
{
    public class ViewModel
    {
        private ObservableCollection<string> _classnames = new ObservableCollection<string>();

        public ViewModel()
        {
            _classnames.Add("default 1 of 2");
            _classnames.Add("default 2 of 2");
        }

        public ObservableCollection<string> Classnames
        {
            get { return _classnames; }
            set { _classnames = value; }
        }
    }
}

转换器(完整性)......

namespace DatabindingSpike
{
    public class TabConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var source = value as ObservableCollection<string>;
            if (source == null)
                return null;

            var param = parameter as string;
            if (string.IsNullOrEmpty(param) || param != "SomeParameter")
                throw new NotImplementedException("Null or unknow parameter pasased to the tab converter");

            var tabItems = new List<TabItem>();
            foreach (string classname in source)
            {
                var tabItem = new TabItem
                                  {
                                      Header = classname,
                                      Content = new Button {Content = classname}
                                  };
                tabItems.Add(tabItem);
            }

            return tabItems;
        }

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

4 个答案:

答案 0 :(得分:3)

更新8/19

简明扼要的答案是您必须在视图模型上实现INotifyPropertyChanged,并在更改Property / Collection时通知侦听器。

在ViewModel上实施INotifyPropertyChanged

* implement the interface INotifyPropertyChanged
* define the event (public event PropertyChangedEventHandler PropertyChanged)
* subscribe to the CollectionChanged event (Classnames.CollectionChanged += ...)
* fire the event for listeners

最佳,

/ JHD


以上ViewModel更新... ValueConverter现在调用属性/集合的所有更改

public class ViewModel : INotifyPropertyChanged
{
    private readonly ObservableCollection<string> _classnames = new ObservableCollection<string>();

    public ViewModel()
    {
        Classnames.CollectionChanged += Classnames_CollectionChanged;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void Classnames_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        NotifyPropertyChanged("Classnames");
    }

    private void NotifyPropertyChanged(string info)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            foreach (PropertyChangedEventHandler d in handler.GetInvocationList())
            {
                    d(this, new PropertyChangedEventArgs(info));
            }
        }
    }

    public ObservableCollection<string> Classnames
    {
        get { return _classnames; }
    }
}

XAML绑定......

<UserControl.Resources>
    <local:ViewModel x:Key="TheViewModel"/>
    <local:TabConverter x:Key="TabConverter" />
</UserControl.Resources>

<StackPanel DataContext="{StaticResource TheViewModel}">
    <ListBox ItemsSource="{Binding Classnames}" />
    <controls:TabControl x:Name="TheTabControl" 
        ItemsSource="{Binding Classnames, Converter={StaticResource TabConverter}, ConverterParameter={StaticResource TheViewModel}}"/>
    <Button Click="Button_Click" Content="Change Classnames" />
</StackPanel>

ValueConverter(基本不变

    public class TabConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var source = value as ObservableCollection<string>;
            if (source == null)
                return null;

            //also sorted out the binding syntax to pass the ViewModel as a parameter
            var viewModel = parameter as ViewModel;
            if (viewModel == null)
                throw new ArgumentException("ConverterParameter must be ViewModel (e.g. ConverterParameter={StaticResource TheViewModel}");

            var tabItems = new List<TabItem>();
            foreach (string classname in source)
            {
                // real code dynamically loads controls by name
                var tabItem = new TabItem
                                  {
                                      Header = "Tab " + classname,
                                      Content = new Button {Content = "Content " + classname}
                                  };
                tabItems.Add(tabItem);
            }

            return tabItems;
        }

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

答案 1 :(得分:3)

我意识到这是一个稍微陈旧的问题,但我不知道有人解释了为什么你需要在视图模型的绑定属性上执行INotifyPropertyChanged。

ItemsControl本身需要绑定到集合更改事件的ObservableCollection,以使ItemsControl重新评估。每次调用时,转换器都会返回一个不同的List(或Observable)集合,而不是保留单个ObservableCollection并向其添加项目。因此,这些集合永远不会在它们上面引发任何集合更改事件......每次重新绑定时,它们都是新的。

提升PropertyChanged强制重新评估绑定并重新运行转换器,返回不同的集合并反映您的更改。

我觉得更好的方法可能是在ViewModel而不是转换器中进行转换。公开直接绑定到的TabItem的ObservableCollection,并在适当的位置进行修改。然后,TabControl应该看到直接对您的集合进行的更改,而无需引发PropertyChanged并重新评估整个绑定。

[编辑 - 添加了我的方法] 视图模型:     公共类TabSampleViewModel     {         private ObservableCollection _tabItems = new ObservableCollection();

    public TabSampleViewModel()
    {
        AddTabItem("Alpba");
        AddTabItem("Beta");
    }

    public ObservableCollection<TabItem> TabItems
    {
        get
        {
            return _tabItems;
        }
    }

    public void AddTabItem( string newTabItemName )
    {
        TabItem newTabItem = new TabItem();

        newTabItem.Header = newTabItemName;
        newTabItem.Content = newTabItemName;

        TabItems.Add( newTabItem );
    }
}

查看:         &lt; controls:TabControl ItemsSource =“{Binding TabItems}”/&gt;

答案 2 :(得分:0)

曝光

public ObservableCollection<TabItem> Classnames
{
    get { return _classnames; }
    set { _classnames = value; }
}

如果你调试了valueconverter,你会发现它并没有像你想象的那样经常被调用。

答案 3 :(得分:0)

问题可能是您的ValueConverter返回List<TabItem>而不是ObservableCollection<TabItem>。尝试换一行,看看是否有帮助。