Working on an windows store app, I try to update/refresh a listView when some data is updated. But despite all samples and documentations I read, it doesn't work...
Here my code behind :
(I do not provide my xaml files, it's just a sample listView).
So the class which implements the INotifyPropertyChanged interface:
public class Download : INotifyPropertyChanged
{
public enum DownloadState
{
Running,
Waiting,
Pausing,
Paused,
Cancelling,
Cancelled
};
private String Uri;
private StorageFile storageFile;
private String tempFileName;
private String fileName;
private String version ;
private long totalSize ;
private long downloadedBytes;
private DownloadState state;
private Protocol protocol;
public Download(String Uri, StorageFile file, String fileName, String version, long totalSize, Protocol protocol)
{
this.Uri = Uri;
this.storageFile = file;
this.tempFileName = "";
this.fileName = fileName;
this.version = version;
this.totalSize = totalSize;
this.downloadedBytes = 0;
this.state = DownloadState.Waiting;
this.protocol = protocol;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
System.Diagnostics.Debug.WriteLine("Update!"); //ok
if (PropertyChanged != null)
{
//PropertyChanged is always null and shouldn't.
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public DownloadState State
{
get{return this.state;}
set {
this.state = value;
NotifyPropertyChanged();
}
}
//+some others methods
}
}
And the main page of the metro app :
// The Basic Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234237
namespace ClientAirNavLight_WS
{
/// <summary>
/// A basic page that provides characteristics common to most applications.
/// </summary>
public sealed partial class MainPage : ClientAirNavLight_WS.Common.LayoutAwarePage
{
/// <summary>
/// Represent a Web Service proxy.
/// </summary>
private AirNavLight_WSClientClient proxyWS;
/// <summary>
/// Initiialize the component of the application's main page.
/// </summary>
public MainPage()
{
this.InitializeComponent();
}
/// <summary>
/// Populates the page with content passed during navigation. Any saved state is also
/// provided when recreating a page from a prior session.
/// </summary>
/// <param name="navigationParameter">The parameter value passed to
/// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
/// </param>
/// <param name="pageState">A dictionary of state preserved by this page during an earlier
/// session. This will be null the first time a page is visited.</param>
protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
{
}
/// <summary>
/// Preserves state associated with this page in case the application is suspended or the
/// page is discarded from the navigation cache. Values must conform to the serialization
/// requirements of <see cref="SuspensionManager.SessionState"/>.
/// </summary>
/// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
protected override void SaveState(Dictionary<String, Object> pageState)
{
}
//Simulate data update.
private async void Button_Click(object sender, RoutedEventArgs e)
{
object selected = this.ListResult.SelectedItem;
if (selected != null)
{
//simulate an update in data.
// ListView should be refresh in order to reflect changes made here.
Download dl = (Download)selected;
if (dl.State == Download.DownloadState.Paused)
{
dl.State = Download.DownloadState.Running;
}
else
{
dl.State = Download.DownloadState.Paused;
}
}
else
{
//Just add an item to the list view.
StorageFile file = await this.getStorageFile("test");
Download dl = new Download("192.128.2.14", file, "test", "1.2", 100, Protocol.HTTP);
this.ListResult.Items.Add(dl);
this.ListResult.DataContext = dl;// Does not work.
}
}
private async Task<StorageFile> getStorageFile(String suggestedFileName)
{
FileSavePicker savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
// Dropdown of file types the user can save the file as
savePicker.FileTypeChoices.Add("Application/pdf", new List<string>() { ".pdf" });
savePicker.FileTypeChoices.Add("Archive", new List<string>() { ".zip", ".rar", ".7z" });
savePicker.FileTypeChoices.Add("Plain-text", new List<string>() { ".txt" });
// Default file name if the user does not type one in or select a file to replace
savePicker.SuggestedFileName = suggestedFileName;
return await savePicker.PickSaveFileAsync();
}
}
}
So How am I supposed to use listView.DataContext? Am I misunderstanding how use INotifyPropertyChanged?
EDIT :
How my listView is define :
<ListView x:Name="ListResult" HorizontalAlignment="Left" Height="200" Margin="10,56,0,0" VerticalAlignment="Top" Width="653" SelectionMode="Single"/>
Why are you setting this.ListResult.DataContext = dl? When dealing with ListViews, the ItemsSource is the collection for the all the objects that get iterated, not the DataContext. Furthermore, ItemsSource should be a collection and not one item. So if you're binding to a property, that property should exist as an item's property in the ItemsSource collection.
However, it looks like you don't have a collection so you're adding the dl directly to the ListView with this.ListResult.Items.Add(dl). That approach should work, however take out the this.ListResult.DataContext = dl or set this.ListResult.ItemsSource to a collection and add dl to that collection
I'm not familiar with [CallerMemberName] but if you're having problems with propertyName being null, try the following
public DownloadState State
{
get{return this.state;}
set {
if( this.state != value )
{
this.state = value;
NotifyPropertyChanged("State");
}
}
}
Furthermore, all properties that you want to bind to should be public and call NotifyPropertyChanged, like the following
private long totalSize ;
public long TotalSize
{
get{return this.totalSize;}
set {
if( this.totalSize != value )
{
this.totalSize = value;
NotifyPropertyChanged("TotalSize");
}
}
}
In your Download class, the only Property that will update in the UI is State, since it has a getter and setter.
Without your XAML it's not possible to see what you are trying to display, but normally, you would bind properties of your DataContext object (dl in this case) to different controls in a datatemplate that the listview could then display.
But these properties need to be public and have getters and setters for INotifyPropertyChanged to update the UI that these properties are bound to.
For instance, if you wanted the filename to show up in the ListView, you would need, as a minimum, to declare the filename property like this:
public String fileName {get; set; }
Zangdak -- One way around that problem is each time the items change in your collection just set set the ItemSource of the ListView to null and then set it back to your collection. If you do this the UI will update and you will see your new items added to your collection.
Paige Ake
Related
I've been trying to make binding work for two labels in a WinForm, but I can't seem to figure out what I'm doing wrong. Currently, I'm implementing the INotifyPropertyChanged interface, and rigged it to a couple of properties within a Form. The current classes this affects are SessionForm.cs, the actual form, and Session.cs, the place where I keep all the information of the program. The labels in question, which are not mentioned in either class, are L_No, which holds the numerical reference of the Note in the musical Scale, and L_Note, which holds the visceral Note value (e.g. C, C#, etc.).
Allow me to explain what everything does within the classes. The program is designed to test your scale knowledge by asking you, based on the your chosen scale, what nth note of the scale is. You use the buttons on the form to make your choice.
These choices are recorded within the Session class, which has been edited to make this more succinct. The array of integers holds the indices of the notes in relation to the scale array, which is in the Scale object. For example, a typical Note array may hold these values: {1,3,0,2,6,1,3,...}. By using the array in the Scale object as a reference, these would translate into musical notes (e.g. D, F, C, E, B, D, F,...). The player's choices are stored within an array of NoteData objects.
In SessionForm.cs I'm manipulating that information over time. Each time a choice is or isn't made (depending on whether or not they attempted to guess in time), the value of the two Labels are changed: L_No, and L_Note. These two Labels are manipulated by the variables NoteIndex and LastNote, respectively. When these change in value, NotifyPropertyChanged occurs, and then the Labels should be updated...but they're not doing so.
Now, in the design section of the form, in the Properties window, I set up the Text property of each Label to be bound to their respective variables within the form, and set to update upon Property Change, but nothing seems to be working.
So what am I doing wrong?
Session.cs:
public class Session
{
public struct NoteData
{
public int Note;
public bool Correct;
public int GuessTime;
}
public Scale Scale;
/// <summary>
/// Holds the notes for one game
/// </summary>
public int[] Notes { get; private set; }
public NoteData[] Data { get; private set; }
/// <summary>
/// Creates a Session
/// </summary>
/// <param name="difficulty">The difficult of the session, refer to the Resources Class for determination.</param>
/// <param name="scale_used">The scale to be used. Refer to the Resources Class for determination.</param>
/// <param name="notes">The notes being used within this Session</param>
public Session(Resources.Difficulties difficulty, Scale scale_used, int[] notes)
{
ID = DateTime.Now;
Diff = difficulty;
Scale = scale_used;
Notes = notes;
Data = new NoteData[notes.Length];
internalIndex = 0;
}
/// <summary>
/// Stores Note input for each guessed
/// </summary>
/// <param name="index">The index of the note the player is currently on</param>
/// <param name="correct">Was the guess correct?</param>
/// <param name="remaining_time">How long did it take for them to guess?</param>
public void StoreNoteInput(int index, bool correct, int remaining_time)
{
if (internalIndex < Data.Length)
Data[internalIndex++] = new NoteData(index, remaining_time, correct);
}
}
SessionForm.cs:
public partial class SessionForm : Form, INotifyPropertyChanged
{
public Session curSession { get; private set; }
Resources.Notes last_note;
/// <summary>
/// The note index number in relation to the scale
/// </summary>
public int NoteIndex
{
get
{ return note_index; }
private set
{
if (note_index != value)
{
note_index = value;
NotifyPropertyChanged("NoteIndex");
}
}
}
/// <summary>
/// Represents the previous note being tested
/// </summary>
public Resources.Notes LastNote
{
get
{
return last_note;
}
private set
{
if (last_note != value)
{
last_note = value;
NotifyPropertyChanged("LastNote");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void TickDownTimer_Tick(object sender, EventArgs e)
{
remainingTime -= countingDown ? 1000 : 100;
if (remainingTime == 0)
{
if (countingDown)
{
countingDown = false;
TickDownTimer.Interval = 100;
}
if (curIndex > 0)
{
//you ran out of time on the last note
RecordNoteInput(curIndex - 1, false);
}
NextNote();
}
SetTimerText();
}
private void RecordNoteInput(int index, bool correct)
{
curSession.StoreNoteInput(index, correct, remainingTime);
NoteIndex = curSession.Notes[curIndex - 1];
LastNote = curSession.Scale.Notes[NoteIndex];
L_Note.ForeColor = correct ? Color.Green : Color.Red;
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
UPDATE: Here's the binding code that comes from SessionForm.Designer.cs:
this.sessionFormBindingSource1 = new System.Windows.Forms.BindingSource(this.components);
this.sessionFormBindingSource2 = new System.Windows.Forms.BindingSource(this.components);
this.sessionFormBindingSource = new System.Windows.Forms.BindingSource(this.components);
//
// L_Note
//
this.L_Note.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.sessionFormBindingSource1, "LastNote", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged, "C"));
this.L_Note.Text = " ";
//
// L_No
//
this.L_No.DataBindings.Add(new System.Windows.Forms.Binding("Text", this.sessionFormBindingSource2, "NoteIndex", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged, "1", "N0"));
this.L_No.Text = " ";
The problem is the way you call NotifyPropertyChanged:
NotifyPropertyChanged("note_index");
and
NotifyPropertyChanged("last_note");
Just remove the strings from the calls like this
NotifyPropertyChanged();
and everything should be fine.
Edit: If it's not, then your bindings are not initialized correctly. Prove:
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
namespace Tests
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());
}
}
class TestForm : Form, INotifyPropertyChanged
{
public TestForm()
{
var label = new Label { Parent = this, Left = 16, Top = 16, AutoSize = false, BorderStyle = BorderStyle.FixedSingle };
label.DataBindings.Add("Text", this, "NoteIndex");
var timer = new Timer { Interval = 200, Enabled = true };
timer.Tick += (sender, e) => NoteIndex = (NoteIndex + 1) % 10;
}
int note_index;
public int NoteIndex
{
get { return note_index; }
private set
{
if (note_index != value)
{
note_index = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I have the following custom user control that derives from ComboBox:
public class EnumSourceComboBox : ComboBox
{
public static readonly DependencyProperty ExcludedItemsProperty =
DependencyProperty.Register(
"ExcludedItems",
typeof(IList),
typeof(EnumSourceComboBox),
new PropertyMetadata(
new List<object>()));
public EnumSourceComboBox()
{
this.ExcludedItems = new List<object>();
}
public IList ExcludedItems
{
get
{
return (IList)this.GetValue(EnumSourceComboBox.ExcludedItemsProperty);
}
set
{
this.SetValue(EnumSourceComboBox.ExcludedItemsProperty, value);
}
}
}
and I use it in XAML as follows:
<controls:EnumSourceComboBox>
<controls:EnumSourceComboBox.ExcludedItems>
<employeeMasterData:TaxAtSourceCategory>FixedAmount</employeeMasterData:TaxAtSourceCategory>
<employeeMasterData:TaxAtSourceCategory>FixedPercentage</employeeMasterData:TaxAtSourceCategory>
</controls:EnumSourceComboBox.ExcludedItems>
</controls:EnumSourceComboBox>
This builds fine and there's no XAML parse exception or something similar. However, the ExcludedItems always have count == 0, so the two items defined in XAML are not added to the list.
How do you define a dependency property to support this behavior?
Update: Here's the TaxAtSourceCategory enumeration:
public enum TaxAtSourceCategory
{
/// <summary>
/// The none.
/// </summary>
None,
/// <summary>
/// The fixed amount.
/// </summary>
FixedAmount,
/// <summary>
/// The fixed percentage.
/// </summary>
FixedPercentage,
// Has some more values, but I don't think that matters
}
In the following code there is the EnumSourceCombobox with the tracking of the changes in the collection.
There are both tracking of :
-the collection change (collection replaced with another on)
-and changes in the collection ( add/remove/clear)
public class EnumSourceComboBox : ComboBox
{
private ObservableCollection<TaxAtSourceCategory> previousCollection;
public static readonly DependencyProperty ExcludedItemsProperty =
DependencyProperty.Register(
"ExcludedItems",
typeof(ObservableCollection<TaxAtSourceCategory>),
typeof(EnumSourceComboBox),
new PropertyMetadata(null));
public ObservableCollection<TaxAtSourceCategory> ExcludedItems
{
get
{
return (ObservableCollection<TaxAtSourceCategory>)this.GetValue(EnumSourceComboBox.ExcludedItemsProperty);
}
set
{
this.SetValue(EnumSourceComboBox.ExcludedItemsProperty, value);
}
}
public EnumSourceComboBox()
{
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(EnumSourceComboBox.ExcludedItemsProperty, typeof(EnumSourceComboBox));
dpd.AddValueChanged(this, (o, e) =>
{
if (previousCollection != null)
previousCollection.CollectionChanged -= ExcludedItemsChanged;
previousCollection = ExcludedItems;
if (previousCollection != null)
previousCollection.CollectionChanged += ExcludedItemsChanged;
});
this.ExcludedItems = new ObservableCollection<TaxAtSourceCategory>();
}
void ExcludedItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// Take into account change in the excluded items more seriously !
MessageBox.Show("CollectionChanged to count " + ExcludedItems.Count);
}
}
Here is a link to the solution :
http://1drv.ms/1P8cMVc
Best use
#cguedel,
I tried your DependencyProperty code, and it runs fine.
I added two strings in the XAML content of the property instead of TaxAtSourceCategory instances
In the Loaded event of the window I had a count for the list of 2.
So may be you ve got Something to do with the TaxAtSourceCategory class.
And may be you could show the TaxAtSourceCategory code, check the constructor call...
Regards
i'm learning MVVM pattern with wpf and i'm trying to create a simple splashscreen for loading applications.
I have a simple class called Loading with two property which are bounded to my interface.
public class Loading : INotifyPropertyChanged
{
/// <summary>
/// Define current status value from 0 to 100.
/// </summary>
private int _currentStatus;
/// <summary>
/// Define current status text.
/// </summary>
private string _textStatus;
/// <summary>
/// Define constructor.
/// </summary>
public Loading(int status, string statusText)
{
_currentStatus = status;
_textStatus = statusText;
}
public int CurrentStatus
{
get { return _currentStatus; }
set
{
_currentStatus = value;
OnPropertyChanged("CurrentStatus");
}
}
public string TextStatus
{
get { return _textStatus; }
set
{
_textStatus = value;
OnPropertyChanged("TextStatus");
}
}
#region Interfaces
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
From my ctor ViewModel i instanciate this model
Loading = new Loading(0, "Loading...");
and run a new thread calling the function GetSystemInfo() which perform some stuff in order to load some information.
Thread systemInfo = new Thread(GetSystemInfo);
systemInfo.IsBackground = true;
systemInfo.Start();
I'm updating the ui from GetSystemInfo() with
Loading.TextStatus = "Loading User Information...";
Loading.CurrentStatus = 50;
So far so good.. the thread is correctly updating the ui but the problem is that i wish to close this splashcreen and open a new window when the loading is complete but i'm unable to check if the thread is complete or at least i don't found a way to do that.
Is there any way i can solve this problem?
Thanks.
You achieve this rather easily by using the Task class (via Task Parallel Library) with a combination of async-await.
What happens when you await on a Task is that the control is yielded back to the caller. In your case, the caller comes from the UI thread, so the control will return to the UI message loop, keeping you app responsive. Once the thread finishes it's work, it will return to the next line after the await, where you can then open the splash screen.
It will look like this:
public async void MyEventHandler(object sender, EventArgs e)
{
await Task.Run(() => GetSystemInfo());
// Here, you're back on the UI thread.
// You can open a splash screen.
}
I'm developing a Terminal Software that send command and data, receive log and data, etc...
Terminal S/W communicate with Arm Cortex via USB Interface.
For developing, I used C# and WPF textbox for terminal screen.
But, I faced some problem.
Updating textbox is very slow compared to USB interfaced.
How can I solve this problem?
If you have solutions or know usercontrol made by WPF for terminal.
Please let me know.
Thank you.
Additional Infomation.
1.string property for binding to TextBox
public string TestString
{
get { return _testString; }
set
{
_testString = value;
RaisePropertyChangedEvent("TestString");
}
}
2.test functions for updating log
StringBuilder testBulider = new StringBuilder();
testBulider.Capacity = 1000000;
for (int i = 0; i < 1000; ++i)
{
testBulider.Append(i.ToString("x4") + "\n");
testString = testBulider.ToString(); // Very Slow Point
}
3.Capture of performance analyzer result in VS2012
StringBuilder may not be the way to go here, and is typically used when you are appending a lot of data in a loop or other process, and wish to call ToString at the very end. The performance is better in that case than say, doing a bunch of explicit string concatenations because of the memory allocation overhead.
I'm not entirely certain on your application but I'd guess you just want to display some output from the terminal. I'd suggest you store the lines of text in an ObservableCollection property and add to it as you receive new lines. I do something similar and have an extended RichTextBox with a DataSource property that I use to display the lines. I'm sure there are faster ways to do this (RichTextBox is NOT known for its performance, and I'm constructing a new inline block every time a line is added), but I've never noticed any performance issues - and it allowed me to get creative with color coding formatted based on event type etc.
Anyway, I've tried my best to copy what I have while stripping out my specific implementation details, I didn't test this specifically.
EventLogBox class:
public class EventLogBox : RichTextBox
{
public EventLogBox()
{
}
static EventLogBox()
{
RichTextBox.IsReadOnlyProperty.OverrideMetadata(
typeof(EventLogBox),
new FrameworkPropertyMetadata(true));
RichTextBox.VerticalScrollBarVisibilityProperty.OverrideMetadata(
typeof(EventLogBox),
new FrameworkPropertyMetadata(ScrollBarVisibility.Visible));
RichTextBox.FontFamilyProperty.OverrideMetadata(
typeof(EventLogBox),
new FrameworkPropertyMetadata(new FontFamily("Courier New")));
}
private void AddItem(string pEntry)
{
Paragraph a = new Paragraph();
Run messageRun = new Run(pEntry);
a.Inlines.Add(messageRun);
this.Document.Blocks.Add(a);
this.ScrollToEnd();
}
private void LoadNewItems()
{
this.Document.Blocks.Clear();
foreach (string entry in DataSource)
{
this.AddItem(entry);
}
}
/// <summary>
/// The source of content (EventLogEntrys)
/// </summary>
public IEnumerable<string> DataSource
{
get
{
return (IEnumerable<string>)GetValue(DataSourceProperty);
}
set
{
SetValue(DataSourceProperty, value);
}
}
/// <summary>
/// Using a DependencyProperty as the backing store for <see cref="DataSource"/>.
/// This enables animation, styling, binding, etc...
/// </summary>
public static readonly DependencyProperty DataSourceProperty =
DependencyProperty.Register
(
"DataSource",
typeof(IEnumerable<string>), typeof(EventLogBox),
new PropertyMetadata
(
new ObservableQueue<string>(),
new PropertyChangedCallback(DataSourcePropertyChanged)
),
new ValidateValueCallback(IsValidDataSourceProperty)
);
/// <summary>
/// We also ned to impliment the INotifyCollectionChanged interface
/// in order for everything to work properly.
/// </summary>
/// <param name="pValue"></param>
/// <returns></returns>
private static bool IsValidDataSourceProperty(object pValue)
{
return pValue is INotifyCollectionChanged;
}
/// <summary>
/// Callback when the DataSourceProperty changes. Setup the new
/// CollectionChanged events, etc.
/// </summary>
/// <param name="pSender"></param>
/// <param name="pArgs"></param>
private static void DataSourcePropertyChanged(DependencyObject pSender,
DependencyPropertyChangedEventArgs pArgs)
{
EventLogBox s = ((EventLogBox)pSender);
((INotifyCollectionChanged)s.DataSource).CollectionChanged -=
new NotifyCollectionChangedEventHandler(
s.DataSource_CollectionChanged);
((INotifyCollectionChanged)s.DataSource).CollectionChanged +=
new NotifyCollectionChangedEventHandler(
s.DataSource_CollectionChanged);
s.LoadNewItems();
}
/// <summary>
///
/// </summary>
/// <param name="pSender"></param>
/// <param name="pArgs"></param>
private void DataSource_CollectionChanged(object pSender,
NotifyCollectionChangedEventArgs pArgs)
{
IList n = pArgs.NewItems;
switch (pArgs.Action)
{
case NotifyCollectionChangedAction.Add:
{
foreach (string cle in pArgs.NewItems)
{
AddItem(cle);
}
break;
}
case NotifyCollectionChangedAction.Remove:
{
for (int i = pArgs.OldStartingIndex;
i < pArgs.OldItems.Count; i++)
{
this.Document.Blocks.Remove(this.Document.Blocks.ElementAt(i));
}
break;
}
case NotifyCollectionChangedAction.Replace:
{
break;
}
}
}
}
Property In ViewModel
private ObservableCollection<string> mLines = new ObservableCollection<string>();
public ObservableCollection<string> Lines
{
get
{
return mLines;
}
}
In XAML:
<local:EventLogBox DataSource="{Binding Path=Lines}" Height="100"/>
Then when you receive a new line from your terminal, call
Lines.Add(newLine);
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.