使用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();
}
}
}
答案 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>
。尝试换一行,看看是否有帮助。