I have the following XAML that uses a classes CurrencyImg property to look up a static resource at runtime, as there are a lot of these in a treeview, I don't want to have to load the same image 1000 times, I keep it in a resourcedictionary so it is only loaded once.
<Image Name="imgCurrency" Grid.Column="5" Margin="0,0,0,0" Source="{w:ImageStaticResource {Binding CurrencyImg}}" Height="22" VerticalAlignment="Top"/>
With a sample resource dictionary entry as
<ImageSource x:Key="..\Resources\Images\USD.ico">../Resources/Images/USD.ico</ImageSource>
The property is as follows
public string CurrencyImg
{
get
{
if (DisplayUSDRate)
{
return AppString.General.ImagePath + AppString.CurrencyId.USD + ".ico";
}
else
{
return AppString.General.ImagePath + curr + ".ico";
}
}
}
And the property action which does the work
public override bool DisplayUSDRate
{
get { return _customer.DisplayUSDRate; }
set
{
_customer.DisplayUSDRate = value;
OnPropertyChanged("CurrencyImg");
}
}
Now when this runs, the OnPropertyChanged("CurrencyImg") does nothing, and it's clear why. In the XAML I'm not directly binding to the CurrencyImg property, I'm using it as a parameter to my StaticResourceExtension class, so when I call OnPropertyChanged, it thinks there are no bound properties to update, so the XAML image does not update. I can understand this, but obviously this does not help me as what I need to happen is the following.
1) For the class to determine which currency image gets displayed for that row
2) For the images to come from a resource dictionary so they don't get loaded more than once or performance hugely suffers
3) For changes to the DisplayUSDRate property to then reflect by updating the flag to the appropriate image. This is the bit I can't figure out how to do with my StaticResourceExtension (code below)
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace Citi.Rbcs.UI.Windows
{
public class ImageStaticResource : StaticResourceExtension
{
public Binding Binding { get; set; }
private static readonly DependencyProperty DummyProperty;
public ImageStaticResource()
{
}
public ImageStaticResource(Binding binding)
{
Binding = binding;
}
static ImageStaticResource()
{
DummyProperty = DependencyProperty.RegisterAttached(
"Dummy",
typeof (Object),
typeof (DependencyObject),
new UIPropertyMetadata(null));
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
var targetObject = (FrameworkElement)target.TargetObject;
Binding.Source = targetObject.DataContext;
var DummyDO = new DependencyObject();
BindingOperations.SetBinding(DummyDO, DummyProperty, Binding);
ResourceKey = DummyDO.GetValue(DummyProperty);
var resourceDictionary = new ResourceDictionary
{
Source = new Uri("pack://application:,,,/Windows/Images.xaml")
};
var key = (string) ResourceKey;
if (!resourceDictionary.Contains(key)) ResourceKey = "Default";
return base.ProvideValue(serviceProvider);
}
}
}
You could write a very simple binding converter that converts image URI strings to cached ImageSource objects:
public class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var imageUrl = value as string;
var image = MemoryCache.Default.Get(imageUrl) as ImageSource;
if (image == null)
{
image = new BitmapImage(new Uri(imageUrl));
MemoryCache.Default.Set(imageUrl, image, new CacheItemPolicy());
}
return image;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Related
So I have this view model:
public class WiresharkFiles : INotifyPropertyChanged
{
public ObservableCollection<WiresharkFile> List { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private bool _inUse;
private int _packets;
private bool _hasItems;
public WiresharkFiles()
{
List = new ObservableCollection<WiresharkFile>();
HasItems = false;
List.CollectionChanged += List_CollectionChanged;
}
private void List_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
HasItems = List.Count > 0;
}
public bool InUse
{
get { return _inUse; }
set
{
_inUse = value;
NotifyPropertyChanged("InUse");
}
}
public int Packets
{
get { return _packets; }
set
{
_packets = value;
NotifyPropertyChanged("Packets");
}
}
public bool HasItems
{
get { return _hasItems; }
set
{
_hasItems = value;
NotifyPropertyChanged("HasItems");
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow.xaml
private WiresharkFiles caps;
public MainWindow()
{
InitializeComponent();
caps = new WiresharkFiles();
}
Window.Resources
<Window.Resources>
<Convertors:CollectionHasItemsConverter x:Key="CollectionHasItemsConverter"/>
</Window.Resources>
Converter
public class CollectionHasItemsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And base of my collection item (empty or not) i want to enable/disable my Button:
<Button Name="btnDeleteAll"
Click="btnDeleteAll_Click"
IsEnabled="{Binding Path=(caps.HasItems),Converter={StaticResource CollectionHasItemsConverter}}">
And i got this error:
XamlParseException: Type reference cannot find type named
'{http://schemas.microsoft.com/winfx/2006/xaml/presentation}caps'.
I don't see where you're associating your DataContext with the caps property.
Make sure you have a public property because the WPF engine isn't running from within your class and won't be able to access the private WiresharkFiles caps; variable. Try the following:
private WiresharkFiles caps;
public WiresharkFiles Files { get { return caps; } }
with a corresponding
public MainWindow()
{
InitializeComponent();
caps = new WiresharkFiles();
DataContext = Files;
}
Your XAML will then bind to Files as follows
IsEnabled="{Binding Path=HasItems}"
Update You'll need to have a look at implementing and binding to commands for the button which will make it a lot better. Look at this article for info on implementing and dealing with commands.
caps is a private variable:
private WiresharkFiles caps;
In order to bind, it would have to be a public property:
public WiresharkFiles caps {get;set;}
You would also have to set the datacontext of the window to itself. Something like:
this.DataContext = this;
or
In your window tag put:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
I don't see how this relates to your initial question but you can use dot notation in binding.
You can bind:
{Binding AnObservableCollection.Count}
And you can compare that to 0 in a datatrigger. With a button and a bound command if you want to disable it then I'd use the canexecute of icommand and return false if you have no entries or whatever your logic is.
im rookie with wpf + mvvm, have a simple mui:ModernTab control with items harcoded.
<mui:ModernTab Layout="List" SelectedSource="/Pages/Settings/Appearance.xaml">
<mui:ModernTab.Links>
<mui:Link DisplayName="appearance" Source="/Pages/Settings/Appearance.xaml" />
<mui:Link DisplayName="about" Source="/Pages/Settings/About.xaml" />
</mui:ModernTab.Links>
</mui:ModernTab>
I want populate it tab with the dbdata on the constructor of viewModel something like this on xaml code:
<ScrollViewer>
<mui:ModernTab Layout="List" Links="{Binding AllowedViews}" />
</ScrollViewer>
on viewModel c# constructor as:
public class ApplicationViewModel:ViewModelBase
{
private LinkCollection allowedViews;
public LinkCollection AllowedViews
{
get { return allowedViews; }
set {
allowedViews = value;
NotifyPropertyChanged("tabitem");
}
}
public ApplicationViewModel()
{
allowedViews.Add(new Link() { DisplayName = "item1"});
allowedViews.Add(new Link() { DisplayName = "item2" });
allowedViews.Add(new Link() { DisplayName = "item3" });
}
//allowedViews.Add(new Link() { DisplayName = "Otra Ventana", Source = new Uri("/Views/ModernWindow1.xaml", UriKind.RelativeOrAbsolute) });
}
Questions:
1-is better use a LinkCollection or List to populate data.
The right way to do the binding is with prop Links on xaml?
someone can sahre any documentation or example?
Thanks a lot. excuse my english.
public LinkCollection AllowedViews
{
get { return allowedViews; }
set {
allowedViews = value;
NotifyPropertyChanged("tabitem");
}
}
This "tabitem" should be "AllowedViews", right?
Here is a definition of dynamic links
<mui:ModernTab Layout="List" Links ="{Binding MyIEnumerable, Converter={StaticResource myCollectionToLinksConverter}}">
<mui:ModernTab.ContentLoader>
<app:MyControlLoader />
</mui:ModernTab.ContentLoader>
</mui:ModernTab>
then add a definition of a converter to your window or control
<UserControl.Resources>
<MyCollectionToLinksConverter x:Key="myCollectionToLinksConverter"/>
</UserControl.Resources>
then add the converter class
public class MyCollectionToLinksConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var source = (ICollection<MyCollectionItem>)value;
return new LinkCollection(source.Select(i => new Link() {DisplayName = i.Name, Source = new Uri(v.i, UriKind.Relative)}));
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
then add your content Loader
class MyControlLoader: DefaultContentLoader
{
protected override object LoadContent(Uri uri)
{
var myTarget = UIModel.Instance.GetMyTargetObjectById(v => v.Name == uri.OriginalString);
return new YourTabContentControl() {DataContext = myTarget};
}
}
I am trying to bind a dependency property to an INotifyPropertyChanged-enabled property with a multi-level property path.
When the owner object of the property is not null, the binding works, but if the owner object is null, the binding does not do anything (the converter is not called and TargetNullValue is not used).
Here is some minimal sample code to reproduce the problem:
Window1.xaml.cs:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel;
namespace NPCPropertyPath
{
public abstract class VMBase : INotifyPropertyChanged
{
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (e == null) {
throw new ArgumentNullException("e");
}
if (PropertyChanged != null) {
PropertyChanged(this, e);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Window1 : Window
{
private class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) {
return null;
} else if (value is int) {
return (((int)value) + 15).ToString();
} else {
return "no int";
}
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
private class InnerVM : VMBase
{
private int myValue;
public int MyValue {
get {
return myValue;
}
set {
if (myValue != value) {
myValue = value;
OnPropertyChanged("MyValue");
}
}
}
}
private class OuterVM : VMBase
{
private InnerVM thing;
public InnerVM Thing {
get {
return thing;
}
set {
if (thing != value) {
thing = value;
OnPropertyChanged("Thing");
}
}
}
}
private readonly OuterVM vm = new OuterVM();
public Window1()
{
InitializeComponent();
var txt = new TextBlock();
txt.SetBinding(TextBlock.TextProperty,
new Binding("Thing.MyValue") {
Source = vm,
Mode = BindingMode.OneWay,
Converter = new MyConverter(),
TargetNullValue = "(error)"
});
container.Content = txt;
var txt2 = new TextBlock();
txt2.SetBinding(TextBlock.TextProperty,
new Binding("Thing") {
Source = vm,
Mode = BindingMode.OneWay,
Converter = new MyConverter(),
TargetNullValue = "(error)"
});
container2.Content = txt2;
}
void Button_Click(object sender, RoutedEventArgs e)
{
vm.Thing = new InnerVM();
vm.Thing.MyValue += 10;
}
}
}
Window1.xaml:
<Window x:Class="NPCPropertyPath.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NPCPropertyPath" Height="300" Width="300">
<StackPanel>
<Button Content="Change value" Click="Button_Click"/>
<ContentControl Name="container"/>
<ContentControl Name="container2"/>
</StackPanel>
</Window>
Of course, this is a significantly simplified form of my real application, where there is quite a bit more going on and the involved classes are not crammed together in two files (or even in the same assembly) where they can all see each other.
This sample displays a window with a button and two content controls. Each of the content controls contains a TextBlock whose Text property is bound to a property from a view-model. The view-model instance (of type OuterVM) is assigned to the Source property of each binding.
OuterVM implements INotifyPropertyChanged; it has a property Thing of type InnerVM (which also implements INotifyPropertyChanged), which in turn has a property MyValue.
The first text block is bound to Thing.MyValue, the second one just to Thing. Both of the bindings have a converter set, as well as a value for the TargetNullValue property that should be displayed on the text block if the target property is null.
The Thing property of the OuterVM instance is initially null. When clicking the button, something is assigned to that property.
The problem: Only the second text block displays anything initially. The one that is bound to Thing.MyValue neither invokes the converter (as evidenced by setting breakpoints), nor does it use the value of the TargetNullValue property.
Why? And how can I have the first text block display a default value instead of Thing.MyValue while Thing is not assigned?
For this purpose you should not use TargetNullValue, you should use FallbackValue property of the Binding.
is it possible to bind the Path property of a binding to another property?
I want to realize this code:
Text="{Binding Path={Binding Path=CurrentPath}}"
So I can adjust dynamically to which Property my actual binding is refering.
Thanks for your Help
Jonny
I worked it out on myself.
Heres the solution, I hope it might help anyone got the same problem like me.
public class CustomBindingBehavior : Behavior<FrameworkElement>
{
public bool IsBinding
{
get
{
return (bool)GetValue(IsBindingProperty);
}
set
{
SetValue(IsBindingProperty, value);
}
}
public string PropertyPath
{
get
{
return (string)GetValue(PropertyPathProperty);
}
set
{
SetValue(PropertyPathProperty, value);
}
}
public static DependencyProperty
PropertyPathProperty = DependencyProperty.Register("PropertyPath", typeof(string),
typeof(CustomBindingBehavior), null);
public static DependencyProperty
IsBindingProperty = DependencyProperty.Register("IsBinding", typeof(bool),
typeof(CustomBindingBehavior), null);
protected override void OnAttached()
{
if (AssociatedObject is TextBlock)
{
var tb = AssociatedObject as TextBlock;
tb.Loaded += new RoutedEventHandler(tb_Loaded);
}
}
private void tb_Loaded(object sender, RoutedEventArgs e)
{
AddBinding(sender as TextBlock, TextBlock.TextProperty);
}
private void AddBinding(DependencyObject targetObj, DependencyProperty targetProp)
{
if (IsBinding)
{
Binding binding = new Binding();
binding.Path = new PropertyPath(this.PropertyPath, null);
BindingOperations.SetBinding(targetObj, targetProp, binding);
}
else
{
targetObj.SetValue(targetProp, this.PropertyPath);
}
}
}
And heres the implementation in XAML:
<TextBlock >
<i:Interaction.Behaviors>
<behaviors:CustomBindingBehavior PropertyPath="{Binding Path=HeaderPropertyBinding}" IsBinding="{Binding Path=HeaderIsBinding}" />
</i:Interaction.Behaviors>
</TextBlock>
Greetings
Jonny
As other posters have mentioned, you can only set a binding on a dependency property - which path is not. The underlying reason is that xaml is source code that gets compiled. At compile time the compiler has no idea what the value of 'CurrentPath' is, and would not be able to compile. Essentially what you are looking to do is runtime reflection of a property value - which could be done using another property in the ViewModel you are binding to, or using a converter.
ViewModel:
public string CurrentValue
{
get
{
var property = this.GetType().GetProperty(CurrentPath);
return property.GetValue(this, null);
}
}
Using a converter:
public class CurrentPathToValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var viewModel = (ViewModel)value;
var property = viewModel.GetType().GetProperty(viewModel.CurrentPath);
var currentValue = property.GetValue(viewModel, null);
return currentValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Of couse these only work if you want to get a simple property of the object - if you want to get something more complex your reflection code is going to get a lot more complex.
Unless you are building something like a property grid, or for some other reason you actually want to introspect the objects running in your application, I would suggest you revisit your design, as reflection is really only suited to a few situations.
Path is not a dependency property, therefore the binding will not work.
Perhaps you could bind to a property that returns another property based on a switch statement and bind to that. Change the 'switch' property and you change the output of the other property.
Just don't forget to include your NotifyPropertyChanged stuff in the switch property for the bound property otherwise your view will not update.
e.g.
private int _mySwitch;
//Set this to determine what the other property will return.
public int SwitchProperty
{
get { return _mySwitch; }
set
{
_mySwitch = value;
NotifyPropertyChanged("MySwitchableProperty");
}
}
public String PropertyA { get; set; }
public String PropertyB { get; set; }
//Bind to this property
public String MySwitchableProperty
{
get
{
switch (SwitchProperty)
{
case 1:
return PropertyA;
break;
case 2:
return PropertyB;
break;
default :
return String.Empty;
break;
}
}
}
I think converter can helps your.
Expample
First control
Text="{Binding Path=CurrentPath}"
Second control
Text="{Binding Path=CurrentPath, Convertor={converters:MyConvertor}}"
Base converter
public abstract class ConvertorBase<T> : MarkupExtension, IValueConverter
where T : class, new()
{
public abstract object Convert(object value, Type targetType, object parameter,
CultureInfo culture);
public virtual object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
#region MarkupExtension members
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (_converter == null)
_converter = new T();
return _converter;
}
private static T _converter = null;
#endregion
}
MyConverter
public class MyConverter: ConvertorBase<MyConverter>
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (string)value.Equals("blabla") ? "Yes" : "No"; // here return necessary parametr
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
Silverlight 3 app with a TabControl bound to an ObservableCollection using an IValueConverter. Initial the binding works (converter called) on app startup. Changes, Clear() or Add(), to the bound collection are not reflected in the TabControl... converter not called.
note: the bound ListBox reflects the changes to the bound collection while the TabControl does not.
Ideas?
/jhd
The XAML binding...
<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>
The 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; }
}
}
}
The converter (for completeness)...
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();
}
}
}
Update 8/19
The concise answer is you have to implement INotifyPropertyChanged on the view model and notify listeners when the Property/Collection is changed.
Implement INotifyPropertyChanged on the ViewModel
* implement the interface INotifyPropertyChanged
* define the event (public event PropertyChangedEventHandler PropertyChanged)
* subscribe to the CollectionChanged event (Classnames.CollectionChanged += ...)
* fire the event for listeners
Best,
/jhd
ViewModel update per above... ValueConverter now called on all changes to the Property/Collection
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; }
}
}
The XAML binding...
<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>
The ValueConverter (basically unchanged
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();
}
}
I realize this is a slightly old question at this point, but I don't know that anyone has explained why you need to do the INotifyPropertyChanged on the bound property on your view model.
The ItemsControl itself needs to be bound to an ObservableCollection for the collection change events to cause the ItemsControl to re-evaluate. Your converter is returning a distinct List (or Observable) collection each time it is called rather than holding on to a single ObservableCollection and adding items to it. Therefore, these collections never have any of the collection changed events raised on them... they're always new, each time the binding is re-done.
Raising PropertyChanged forces the binding to be re-evaluated and re-runs your converter, returning a distinct collection and reflecting your changes.
I feel a better approach may be to do the conversion in your ViewModel rather than in a Converter. Expose an ObservableCollection of TabItem that you bind directly to and that you modify in place. The TabControl should then see changes made directly to your collection without the need to raise PropertyChanged and re-evaluate the entire binding.
[Edit - Added my approach]
ViewModel:
public class 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 );
}
}
View:
<controls:TabControl ItemsSource="{Binding TabItems}"/>
Expose
public ObservableCollection<TabItem> Classnames
{
get { return _classnames; }
set { _classnames = value; }
}
If you debug the valueconverter you'll see it's not being called as often as you think it is.
The problem could be that your ValueConverter returns a List<TabItem> instead of an ObservableCollection<TabItem>. Try that one line change and see if it helps.