The mainpage:
MainPage.xaml
<Canvas x:Name="LayoutRoot" Background="White">
</Canvas>
MainPage.xaml.cs
List<Usol> list = new List<Usol>();
for (int i = 0; i < 10; i++)
{
var element = new Usol();
list.Add(element);
Canvas.SetTop(element, i * 25);
LayoutRoot.Children.Add(list[i]);
}
foreach (var item in list)
{
item.context.name = "Varken";
}
A usercontrol
Usol.xaml
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="{Binding Name}" />
</Grid>
Usol.xaml.cs
public Context context;
public Usol()
{
InitializeComponent();
context = new Context();
this.DataContext = context;
}
A class
Context.cs
public class Context : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#region Fields
/// <summary>
/// Field Declaration for the <see cref="Name"/>
/// </summary>
private string name;
#endregion
#region Properties
/// <summary>
/// Gets or Sets the Name
/// </summary>
public string Name
{
get { return name; }
set
{
if (this.name != value)
{
this.name = value;
OnPropertyChanged("Name");
}
}
}
#endregion
}
Situation
I have created this small test application to copy a problem I have in a bigger application. It works about the same way (not exactly, but close enough).
It adds several custom made usercontrols and each get a own instance of a datacontext class.
However, none of the properties are willing to update themselfs due to a empty PropertyChangedEventHandler.
Question
Why is public event PropertyChangedEventHandler PropertyChanged; always null?
Context.cs needs to implement INotifyPropertyChanged interface. Are you doing that?
Edit: Post your update.
I have generally seen this kind of problem when programmers create "two" instances of Model/ViewModel. While you attach one instance with View, it's always the other one that gets update (which ofcourse will have a null PropertyChanged subscribers). Thus, you must make sure that your view is using the same instance as being updated at other parts.
Hope my point is clear.
Your code is wrong,
OnPropertyChanged("Name"); <-- should update "name" not "Name"
You are firing event saying that "Name" is changed, but name of property is "name", C# and binding are case sensitive.
Change it to,
#region Fields
/// <summary>
/// Field Declaration for the <see cref="name"/>
/// </summary>
private string _Name;
#endregion
#region Properties
/// <summary>
/// Gets or Sets the name
/// </summary>
public string Name
{
get { return _Name; }
set
{
if (this._Name != value)
{
this._Name = value;
OnPropertyChanged("Name");
}
}
}
#endregion
From C# 6 on wards, please use nameof() keyword...
#region Fields
/// <summary>
/// Field Declaration for the <see cref="name"/>
/// </summary>
private string _Name;
#endregion
#region Properties
/// <summary>
/// Gets or Sets the name
/// </summary>
public string Name
{
get { return _Name; }
set
{
if (this._Name != value)
{
this._Name = value;
OnPropertyChanged(nameof(Name));
}
}
}
#endregion
Related
I need to add logic of a property setter.
For example, I have a property named "CurrentTab":
private WorkspaceViewModel _currentTab;
public WorkspaceViewModel CurrentTab
{
get
{
return _currentTab;
}
set
{
_currentTab = value;
OnPropertyChanged("CurrentTab");
}
}
This is all good and works, but I want to be able to just define it like this:
public WorkspaceViewModel CurrentTab { get; set; }
So that the system automatically performs the OnPropertyChanged() function for the property name after the setter has run without me adding any specific code.
How to identify which properties need to follow this logic is no problem, I just need to find a way how to actually do it.
I want to make this simpler because I'll be having quite a lot of those kind of properties and I'd like to keep it clean.
Is there a way?
Any help is much appreciated!
Take a look: Fody. There is an add-in for INotifyPropertyChange: github
It is manipulating IL code while building the solution.
You need only to add attribute to view model:
[ImplementPropertyChanged]
public class Person
{
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
}
When code gets compiled:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string givenNames;
public string GivenNames
{
get { return givenNames; }
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged("GivenNames");
OnPropertyChanged("FullName");
}
}
}
string familyName;
public string FamilyName
{
get { return familyName; }
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
public virtual void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This can be achieved using PostSharp, which is an Aspect Oriented Programming approach:
In computing, aspect-oriented programming (AOP) is a programming
paradigm that aims to increase modularity by allowing the separation
of cross-cutting concerns. AOP forms a basis for aspect-oriented
software development.
You can implement this using an Aspect called InstanceLevelAspect:
/// <summary>
/// Aspect that, when apply on a class, fully implements the interface
/// <see cref="INotifyPropertyChanged"/> into that class, and overrides all properties to
/// that they raise the event <see cref="INotifyPropertyChanged.PropertyChanged"/>.
/// </summary>
[Serializable]
[IntroduceInterface(typeof(INotifyPropertyChanged),
OverrideAction = InterfaceOverrideAction.Ignore)]
[MulticastAttributeUsage(MulticastTargets.Class | MulticastTargets.Property,
Inheritance = MulticastInheritance.Strict)]
public sealed class NotifyPropertyChangedAttribute : InstanceLevelAspect,
INotifyPropertyChanged
{
/// <summary>
/// Field bound at runtime to a delegate of the method OnPropertyChanged
/// </summary>
[ImportMember("OnPropertyChanged", IsRequired = false)]
public Action<string> OnPropertyChangedMethod;
/// <summary>
/// Method introduced in the target type (unless it is already present);
/// raises the <see cref="PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
[IntroduceMember(Visibility = Visibility.Family, IsVirtual = true,
OverrideAction = MemberOverrideAction.Ignore)]
public void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this.Instance,
new PropertyChangedEventArgs(propertyName));
}
}
/// <summary>
/// Event introduced in the target type (unless it is already present);
/// raised whenever a property has changed.
/// </summary>
[IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)]
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Method intercepting any call to a property setter.
/// </summary>
/// <param name="args">Aspect arguments.</param>
[OnLocationSetValueAdvice,
MulticastPointcut( Targets = MulticastTargets.Property,
Attributes = MulticastAttributes.Instance)]
public void OnPropertySet(LocationInterceptionArgs args)
{
// Don't go further if the new value is equal to the old one.
// (Possibly use object.Equals here).
if (args.Value == args.GetCurrentValue())
{
return;
}
// Actually sets the value.
args.ProceedSetValue();
// Invoke method OnPropertyChanged (our, the base one, or the overridden one).
this.OnPropertyChangedMethod.Invoke(args.Location.Name);
}
}
Then, use it on your property like this:
[NotifyPropertyChanged]
public WorkspaceViewModel CurrentTab { get; set; }
This attirubte can also be applied at the class level, if you want all your properties to implement NotifyPropertyChanged. More on the example can be found here
In my WPF MVVM project application, click event command (AddUserCmd) is not get firing. Can you please find out the issue from my code part?
User.xaml
<Window x:Class="JP.User"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Window" Title="User" WindowStartupLocation="CenterScreen"
Width="795" Height="500">
...
...
<TextBox Canvas.Left="114" TextWrapping="Wrap" Canvas.Top="98" RenderTransformOrigin="-0.093,-0.274" Width="157" Height="20" Background="{x:Null}" Foreground="#FF6B6C6C" Text="{Binding Path=Name}">
<TextBox Canvas.Left="114" TextWrapping="Wrap" Canvas.Top="129" RenderTransformOrigin="-0.093,-0.274" Width="157" Height="21" Background="{x:Null}" Foreground="#FF6B6C6C" Text="{Binding Path=Pwd}">
...
...
<Button Content="Save" Height="23" Name="button2" Width="75" Margin="605,0,0,0" Command="{Binding AddUserCmd}"/>
...
...
</Window>
UserViewModel.cs
public class UserViewModel : INotifyPropertyChanged
{
#region Private Variables
//The Variables are meant to be readonly as we mustnot change the address of any of them by creating new instances.
//Problem with new istances is that since address changes the binding becomes invalid.
//Instantiate all the variables in the constructor.
private readonly JanathaPOS.Model.User _user;
private readonly ObservableCollection<JanathaPOS.Model.User> _users;
private readonly UserManager _userManager;
private readonly ICommand _addUserCmd;
//private readonly ICommand _deleteUserCmd;
#endregion
#region Constructors
/// <summary>
/// Instatiates all the readonly variables
/// </summary>
public UserViewModel()
{
_user = new JanathaPOS.Model.User();
_userManager = new UserManager();
_users = new ObservableCollection<Model.User>();
_addUserCmd = new RelayCommand(Add, CanAdd);
}
#endregion
#region Public Properties
/// <summary>
/// Gets or Sets User Name. Ready to be binded to UI.
/// Impelments INotifyPropertyChanged which enables the binded element to refresh itself whenever the value changes.
/// </summary>
public string Name
{
get
{
return _user.Name;
}
set
{
_user.Name = value;
OnPropertyChanged("Name");
}
}
/// <summary>
/// Gets or Sets User Password. Ready to be binded to UI.
/// Impelments INotifyPropertyChanged which enables the binded element to refresh itself whenever the value changes.
/// </summary>
public string Pwd
{
get
{
return _user.Pwd;
}
set
{
_user.Pwd = value;
OnPropertyChanged("Pwd");
}
}
/// <summary>
/// Gets the Users. Used to maintain the User List.
/// Since this observable collection it makes sure all changes will automatically reflect in UI
/// as it implements both CollectionChanged, PropertyChanged;
/// </summary>
public ObservableCollection<JanathaPOS.Model.User> Users
{
get
{
return _users;
}
}
#endregion
#region Command Properties
/// <summary>
/// Gets the AddPatientCommand. Used for Add patient Button Operations
/// </summary>
public ICommand AddUserCmd
{
get
{
return _addUserCmd;
}
}
#endregion
#region Commands
#region AddCommand
/// <summary>
/// Add operation of the AddUserCmd.
/// Operation that will be performormed on the control click.
/// </summary>
/// <param name="obj"></param>
public void Add(object obj)
{
//Always create a new instance of patient before adding.
//Otherwise we will endup sending the same instance that is binded, to the BL which will cause complications
var user = new JanathaPOS.Model.User { Id = "123", Name = Name, Pwd = Pwd };
//Add patient will be successfull only if the patient with same ID does not exist.
if (_userManager.Add(user))
{
Users.Add(user);
ResetUser();
MessageBox.Show("User add Successful!");
}
else
{
MessageBox.Show("User with this ID already exists!");
}
}
/// <summary>
/// CanAdd operation of the AddUserCmd.
/// Tells us if the control is to be enabled or disabled.
/// This method will be fired repeatedly as long as the view is open.
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public bool CanAdd(object obj)
{
//Enable the Button only if the mandatory fields are filled
if (Name != string.Empty && Pwd != string.Empty)
return true;
return false;
}
#endregion
#endregion
#region Private Methods
/// <summary>
/// Resets the Patient properties which will in turn auto reset the UI aswell
/// </summary>
private void ResetUser()
{
//Id = string.Empty;
Name = string.Empty;
Pwd = string.Empty;
}
#endregion
#region INotifyPropertyChanged Members
/// <summary>
/// Event to which the view's controls will subscribe.
/// This will enable them to refresh themselves when the binded property changes provided you fire this event.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// When property is changed call this method to fire the PropertyChanged Event
/// </summary>
/// <param name="propertyName"></param>
public void OnPropertyChanged(string propertyName)
{
//Fire the PropertyChanged event in case somebody subscribed to it
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
RelayCommand.cs
public class RelayCommand: ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
I have a problem when UI does not update to changing in variables, that are binded to control properties.
Help me understand why.
1) I have a class which inherited from UserControl and from InotifyPropertyChanged
public class BindableControl:UserControl, INotifyPropertyChanged
{
#region Data
private static readonly Dictionary<string, PropertyChangedEventArgs> eventArgCache;
private const string ERROR_MSG = "{0} is not a public property of {1}";
#endregion // Data
#region Constructors
static BindableControl()
{
eventArgCache = new Dictionary<string, PropertyChangedEventArgs>();
}
protected BindableControl()
{
}
#endregion // Constructors
#region Public Members
/// <summary>
/// Raised when a public property of this object is set.
/// </summary>
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Returns an instance of PropertyChangedEventArgs for
/// the specified property name.
/// </summary>
/// <param name="propertyName">
/// The name of the property to create event args for.
/// </param>
public static PropertyChangedEventArgs
GetPropertyChangedEventArgs(string propertyName)
{
if (String.IsNullOrEmpty(propertyName))
throw new ArgumentException(
"propertyName cannot be null or empty.");
PropertyChangedEventArgs args;
// Get the event args from the cache, creating them
// and adding to the cache if necessary.
lock (typeof(BindableObject))
{
bool isCached = eventArgCache.ContainsKey(propertyName);
if (!isCached)
{
eventArgCache.Add(
propertyName,
new PropertyChangedEventArgs(propertyName));
}
args = eventArgCache[propertyName];
}
return args;
}
#endregion // Public Members
#region Protected Members
/// <summary>
/// Derived classes can override this method to
/// execute logic after a property is set. The
/// base implementation does nothing.
/// </summary>
/// <param name="propertyName">
/// The property which was changed.
/// </param>
protected virtual void AfterPropertyChanged(string propertyName)
{
}
/// <summary>
/// Attempts to raise the PropertyChanged event, and
/// invokes the virtual AfterPropertyChanged method,
/// regardless of whether the event was raised or not.
/// </summary>
/// <param name="propertyName">
/// The property which was changed.
/// </param>
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
this.VerifyProperty(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
// Get the cached event args.
PropertyChangedEventArgs args =
GetPropertyChangedEventArgs(propertyName);
// Raise the PropertyChanged event.
handler(this, args);
}
this.AfterPropertyChanged(propertyName);
}
#endregion
#region Private Helpers
[Conditional("DEBUG")]
private void VerifyProperty(string propertyName)
{
Type type = this.GetType();
// Look for a public property with the specified name.
PropertyInfo propInfo = type.GetProperty(propertyName);
if (propInfo == null)
{
// The property could not be found,
// so alert the developer of the problem.
string msg = string.Format(
ERROR_MSG,
propertyName,
type.FullName);
Debug.Fail(msg);
}
}
#endregion
}
2) Then I have another classes, each of them inherited from BindableControl like this
public class CameraLocalization : BindableControl
{
public CameraLocalization()
{
headers = new CameraHeaders();
toolTips = new CameraToolTips();
SetRuLocal();
//SetEnLocal();
}
private Language lang = SettingsManager.Language.ru_RU;
private CameraHeaders headers;
private CameraToolTips toolTips;
public Language Lang
{
get { return lang; }
set
{
lang = value;
SetLocal();
RaisePropertyChanged();
}
}
3) In XAML I link this class as usercontrol and do binding like this:
xmlns:language ="clr-namespace:SettingsManager.Localization.Camera"
<Grid>
<language:CameraLocalization x:Name="Localization"></language:CameraLocalization>
<GroupBox Header="{Binding ElementName=Localization, Path=Headers.PositionGroupHeader, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
4) from another page I try to change language:
xmlns:language ="clr-namespace:SettingsManager.Localization.Camera"
<Grid Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Width="Auto" Margin="0,0,0,5">
<language:CameraLocalization x:Name="Localization"></language:CameraLocalization>
<ComboBox ItemsSource="{Binding Source={StaticResource Language}}" Width="70" HorizontalAlignment="Left"
SelectedValue="{Binding ElementName=Localization, Path=Lang, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></ComboBox>
And nothing happens. In debug mode i see that value of propertis changing, but they doesnot update on UI. What is the problem here? Who knows?
Your properties should NOT be declared in the UI elements (UserControl), and these should NOT implement INotifyPropertyChanged. You must separate UI from data/logic by using the MVVM Pattern.
You should create a proper ViewModel and put your properties and property change notification there.
I've searched and searched but can't obtain a proper, helpful answer.
I have a MainWindow wpf window. Its DataContext is set to its ViewModel.
I have a ListView which is binded to an ObservableCollection in the ViewModel:
<ListView Grid.Row="1" Grid.Column="0" Margin="2" Name="sources_ListView" Grid.RowSpan="1" ItemsSource="{Binding Path=Sources}">
<ListView.View>
<GridView>
<GridViewColumn Width="290" Header="Name"
DisplayMemberBinding="{Binding Path=OriginalPath}"/>
<GridViewColumn Width="80" Header="Type"
DisplayMemberBinding="{Binding Path=Type}"/>
</GridView>
</ListView.View>
</ListView>
RelayCommand:
public ICommand BrowseFileFolderCommand
{
get
{
if (_browseFileFolderCommand == null)
{
_browseFileFolderCommand = new RelayCommand(o =>
{
_sources.Add(new SourceItem(selectedPath, new DirectoryInfo(selectedPath)));
}, null);
}
return _browseFileFolderCommand;
}
}
Now obviously what the Lambda function does, wouldn't work in the real world as I have taken it out of context, but accept the fact that it does add SourceItem to the ObservableCollection _sources and that there is a Public Sources which gets the _sources. I have also made the type that ObservableCollection takes use INotifyChangedProperty.
When I use that RelayCommand which is inside a button which adds a source to the ObservableCollection, the ListView doesn't update?
Thanks for any help
EDIT SourceItem:
public class SourceItem : ISourceItem, INotifyPropertyChanged
{
DirectoryInfo _sourceFolder;
public DirectoryInfo SourceFolder { get { return _sourceFolder; } private set { _sourceFolder = value; } }
FileInfo _sourceFile;
public FileInfo SourceFiles { get { return _sourceFile; } private set { _sourceFile = value; } }
string _originalPath;
public string OriginalPath { get { return _originalPath; } private set { _originalPath = value; OnPropertyChanged("OriginalPath"); } }
bool _isFolder;
public bool IsFolder { get { return _isFolder; } }
// display friendly property of IsFolder
public string Type { get { return _isFolder == true ? "Folder" : "File"; } }
public SourceItem(string originalPath, DirectoryInfo sourceFolder)
{
_originalPath = originalPath;
_sourceFolder = sourceFolder;
_sourceFile = null;
_isFolder = true;
}
public SourceItem(string originalPath, FileInfo sourceFile)
{
_originalPath = originalPath;
_sourceFile = sourceFile;
_sourceFolder = null;
_isFolder = false;
}
#region INotifyPropertyChanged Members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion // INotifyPropertyChanged Members
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real.
// public, instance property on this object
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = String.Format("Invalid property name: {0}", propertyName);
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion
}
Use the public version of the property to add the new item
Sources.Add(new SourceItem(selectedPath, new DirectoryInfo(selectedPath)));
You are currently adding the item to the private version of your property (_sources), while your UI is bound to the public version of the property (Sources), so your UI does not get the CollectionChanged notification the private version of the property raises, so does not know it needs to update.
The alternative is to simply raise the PropertyChanged event for your class manually to tell the UI to update. This is usually what I do when I want to add a lot of items to my collection at the same time, but only have the UI update once.
_sources.Add(new SourceItem(selectedPath, new DirectoryInfo(selectedPath)));
RaisePropertyChanged("Sources");
I'm binding an UltraTree control (version 10.3) to a custom data source, like so:
public void Populate(List<FilterDimension> data)
{
DataBindings.Clear();
DataSource = data;
Nodes[0].DataColumnSetResolved.NodeTextColumn = Nodes[0].DataColumnSetResolved.Columns["DisplayText"];
}
My expectation is that changing the DisplayText property on any of the bound FilterDimension objects will cause the UltraTree node's text to update. In reality, the text in the tree does not update, and the PropertyChanged event remains null indicating that the UltraTree doesn't even listen for this notification. How do I get the UltraTree to listen for property changes in FilterDimension?
Here's the relevant code from FilterDimension:
internal class FilterDimension : INotifyPropertyChanged
{
private string _displayText = null;
private string _name = null;
private BindingList<string> _values = new BindingList<string>();
/// <summary>
/// Gets or sets the display friendly name.
/// </summary>
public string Name
{
get { return _name; }
set
{
_name = value;
FirePropertyChangedNotification("Name");
if (_displayText == null) { FirePropertyChangedNotification("DisplayText"); }
}
}
/// <summary>
/// Gets or sets the display text that is used in TreeView nodes. When null, uses the Name.
/// </summary>
public string DisplayText
{
get { return _displayText ?? Name; }
set { _displayText = value; FirePropertyChangedNotification("DisplayText"); }
}
/// <summary>
/// Gets a read/write list of values. Is never null.
/// </summary>
public BindingList<string> Values
{
get { return _values; }
set { _values = value ?? new BindingList<string>(); }
}
#region Events
public event PropertyChangedEventHandler PropertyChanged;
protected void FirePropertyChangedNotification(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
It turns out that all I needed to do was change to BindingList<FilterDimension> instead of List<FilterDimension... I completely missed that the control expects notifications to bubble up from the list.