I have a UITextView subclass where I add an NSNotificationCenter observer. But where do I remove the observer again?
My code:
_textDidChangeNotification = UITextView.Notifications.ObserveTextDidChange(TextDidChange);
In Objective C I would do it in the dealloc method but I am not sure where to do the same in C#
As I understand the documentation I should call
_textDidChangeNotification.Dispose()
I have tried to have a
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_textDidChangeNotification.Dispose();
}
}
but it is never called.
The complete class, as requested:
public class PlaceholderTextView : UITextView
{
public string Placeholder
{
get { return PlaceholderLabel.Text; }
set
{
PlaceholderLabel.Text = value;
PlaceholderLabel.SizeToFit();
}
}
protected UILabel PlaceholderLabel { get; set; }
protected NSObject _textDidChangeNotification;
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
AdjustPlaceholderHidden();
}
}
public PlaceholderTextView()
{
SetupLayout();
_textDidChangeNotification
= UITextView.Notifications.ObserveTextDidChange(TextDidChange);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_textDidChangeNotification.Dispose();
}
protected void SetupLayout()
{
PlaceholderLabel = new UILabel(new CGRect(0, 9, 0, 0));
PlaceholderLabel.TextColor = UIColor.FromWhiteAlpha(0.702f, 1f);
AddSubview(PlaceholderLabel);
}
protected void AdjustPlaceholderHidden()
{
if (Text.Length > 0)
{
PlaceholderLabel.Hidden = true;
}
else
{
PlaceholderLabel.Hidden = false;
}
}
protected void TextDidChange(object sender, Foundation.NSNotificationEventArgs args)
{
AdjustPlaceholderHidden();
}
}
I would do it in ViewWillDisappear like so:
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear (animated);
SubscribeMessages ();
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
UnSubscribeMessages ();
}
public void SubscribeMessages ()
{
_hideObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, OnKeyboardNotification);
_showObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, OnKeyboardNotification);
}
public void UnSubscribeMessages ()
{
if (_hideObserver != null) NSNotificationCenter.DefaultCenter.RemoveObserver (_hideObserver);
if (_showObserver != null) NSNotificationCenter.DefaultCenter.RemoveObserver(_showObserver);
}
or ViewDidDisappear like in the Xamarin sample code here
Update
I see what you mean now, I would suspect that something is preventing the custom view from being garbage collected. Have you had a look at this blog post it might help.
Also from this sample code it looks like you are calling the dispose correctly but they null out the custom view on ViewDidUnload here:
Related
public class CustomNavigationRenderer : NavigationRenderer
{
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (this.Element == null) return;
NavigationBar.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
NavigationBar.ShadowImage = new UIImage();
}
}
Try to move the code into ViewDidLayoutSubviews method .
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
if(this.NavigationBar != null)
{
NavigationBar.SetBackgroundImage(new UIImage(), UIBarMetrics.Default);
NavigationBar.ShadowImage = new UIImage();
}
}
I have subclassed UIViewController, which mimics UITableViewController == HUDTableViewController. Then I subclass from this subclassed view controller (SomeViewController : HUDTableViewController).
If I simulate a memory warning, SomeViewController doesn't get released. Here is the code of HUDTableViewController:
using System;
using Foundation;
using UIKit;
namespace MyApp
{
public class HUDTableViewController : UIViewController, IUITableViewDataSource, IUITableViewDelegate, IDisposable, IUIScrollViewDelegate
{
private UIView parentView;
private UITableView tableView;
public UITableView TableView
{
get
{
return this.tableView;
}
set
{
this.tableView = value;
}
}
public HUDTableViewController() : base()
{
Initialize();
}
private void Initialize()
{
this.tableView = new UITableView();
this.tableView.TranslatesAutoresizingMaskIntoConstraints = false;
this.tableView.WeakDelegate = this;
this.tableView.WeakDataSource = this;
this.parentView = new UIView();
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
this.parentView.AddSubview(this.tableView);
View = this.parentView;
NSMutableDictionary viewsDictionary = new NSMutableDictionary();
viewsDictionary["parent"] = this.parentView;
viewsDictionary["tableView"] = this.tableView;
this.parentView.AddConstraints(NSLayoutConstraint.FromVisualFormat("H:|[tableView]|", (NSLayoutFormatOptions)0, null, viewsDictionary));
this.parentView.AddConstraints(NSLayoutConstraint.FromVisualFormat("V:|[tableView]|", (NSLayoutFormatOptions)0, null, viewsDictionary));
}
[Foundation.Export("numberOfSectionsInTableView:")]
public virtual System.nint NumberOfSections(UIKit.UITableView tableView)
{
return 1;
}
public virtual System.nint RowsInSection(UIKit.UITableView tableview, System.nint section)
{
throw new NotImplementedException();
}
public virtual UIKit.UITableViewCell GetCell(UIKit.UITableView tableView, Foundation.NSIndexPath indexPath)
{
throw new NotImplementedException();
}
[Export("tableView:estimatedHeightForRowAtIndexPath:")]
public virtual System.nfloat EstimatedHeight(UIKit.UITableView tableView, Foundation.NSIndexPath indexPath)
{
return UITableView.AutomaticDimension;
}
[Foundation.Export("tableView:didSelectRowAtIndexPath:")]
public virtual void RowSelected(UIKit.UITableView tableView, Foundation.NSIndexPath indexPath)
{
}
[Export("tableView:heightForRowAtIndexPath:")]
public virtual System.nfloat GetHeightForRow(UIKit.UITableView tableView, Foundation.NSIndexPath indexPath)
{
return 44.0f;
}
[Foundation.Export("tableView:heightForHeaderInSection:")]
public virtual System.nfloat GetHeightForHeader(UIKit.UITableView tableView, System.nint section)
{
return UITableView.AutomaticDimension;
}
[Foundation.Export("tableView:viewForHeaderInSection:")]
public virtual UIKit.UIView GetViewForHeader(UIKit.UITableView tableView, System.nint section)
{
return null;
}
[Export("tableView:titleForHeaderInSection:")]
public virtual string TitleForHeader(UITableView tableView, nint section)
{
return string.Empty;
}
[Foundation.Export("tableView:willDisplayCell:forRowAtIndexPath:")]
public virtual void WillDisplay(UIKit.UITableView tableView, UIKit.UITableViewCell cell, Foundation.NSIndexPath indexPath)
{
}
}
}
tableView should have a reference count of 2 (because of AddSubView and my property).
This is the main view controller, which instantiates SomeViewController:
public class MasterViewContainer : UIViewController
{
private bool hasSetupHandlersAndEvents = false;
// ...
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
if (!hasSetupHandlersAndEvents) {
if (listButton != null) {
listButton.Clicked += listButton_Clicked;
}
hasSetupHandlersAndEvents = true;
}
}
public override void ViewWillDisappear (bool animated)
{
base.ViewWillDisappear (animated);
if (hasSetupHandlersAndEvents) {
if (listButton != null) {
listButton.Clicked -= listButton_Clicked;
}
hasSetupHandlersAndEvents = false;
}
}
private void listButton_Clicked(object sender, EventArgs args){
SomeViewController viewController = new SomeViewController();
viewController.SomeEvent += SomeEventHandler;
NavigationController.PushViewController(viewController, false);
}
}
As you can see SomeViewController has a reference to MasterViewContainer, because of SomeEventHandler.
SomeViewController is released if I use
public class SomeViewController : UITableViewController
, but it isn't released if I use
public class SomeViewController : HUDTableViewController
The Dispose method is never called. I don't see a reference cycle. Where do I have to release something? What I'm missing?
Try 1:
This is the only solution, which comes to my mind. I use a field (class variable) where I hold the reference to SomeViewController. In DidReceiveMemoryWarning I manually release/dispose it. When I want to access the field, I check if it has been initialised before. If not I initialise it when needed.
public class MasterViewContainer : UIViewController
{
private SomeViewController viewController;
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
if (this.viewController != null)
{
this.viewController.SomeEvent -= SomeEventHandler;
this.viewController.Dispose();
this.viewController = null;
}
}
private void listButton_Clicked(object sender, EventArgs args){
if (this.viewController == null)
{
this.viewController = new SomeViewController();
this.viewController.SomeEvent += SomeEventHandler;
}
NavigationController.PushViewController(this.viewController, false);
}
But this solution isn't perfect. The dispose is also called when the view is currently on screen. So it is very likely to have malfunctions.
Bounty:
I'd like to have a solution, which explains the memory management issue. Why it doesn't get released? What has to change to get it released (without doing stuff like in my try). It should behave like UITableViewController.
Try 2:
Now I tried to override the Dispose(bool disposing) of HUDTableViewController:
protected override void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
this.tableView.RemoveFromSuperview();
this.tableView.Dispose();
}
this.disposed = true;
}
base.Dispose(disposing);
}
Neither this Dispose method of HUDTableViewController nor the Dispose method of SomeViewController is called.
Call super if you are wanting your parent view to also call the same function handle your management from there. Depending on the arrangement you wouldn't need to do any other manual disposing.
public override void DidReceiveMemoryWarning ()
{
// If you want the superclass to fire the function first call super first
// and vice versa.
super.didReceiveMemoryWarning();
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
I have a C# WPF application built with Visual Studio 2015. I'm using MVVM and the Observer Pattern.
My Provider is a user control called 'ucClientFilter1ViewModel' that contains two text box controls where the user can search for a client(s):
namespace NSUCClientControls
{
public class ucClientFilter1ViewModel : ViewModelBase, IObservable<ClientFilterParameter>
{
private string filterLocation;
private string whereSearch1;
private string whereSearch2;
private List<IObserver<ClientFilterParameter>> observers;
public ucClientFilter1ViewModel()
{
observers = new List<IObserver<ClientFilterParameter>>();
}
public string FilterLocation
{
get { return filterLocation; }
set { filterLocation = value; }
}
public string WhereSearch1
{
get { return whereSearch1; }
set
{
whereSearch1 = value;
TestUpdateGrid(filterLocation);
}
}
public string WhereSearch2
{
get { return whereSearch2; }
set
{
whereSearch2 = value;
TestUpdateGrid(filterLocation);
}
}
private void TestUpdateGrid(string _filterLocation)
{
var filterInfo = new ClientFilterParameter(this);
foreach (var observer in observers)
{
observer.OnNext(filterInfo);
}
}
public IDisposable Subscribe(IObserver<ClientFilterParameter> observer)
{
// Check whether observer is already registered. If not, add it
if (!observers.Contains(observer))
{
observers.Add(observer);
// Provide observer with existing data
var filterInfo = new ClientFilterParameter(this);
observer.OnNext(filterInfo);
}
return new Unsubscriber<ClientFilterParameter>(observers, observer);
}
internal class Unsubscriber<ClientFilterParameter> : IDisposable
{
private IObserver<ClientFilterParameter> observer;
private List<IObserver<ClientFilterParameter>> observers;
public Unsubscriber(List<IObserver<ClientFilterParameter>> _observers, IObserver<ClientFilterParameter> _observer)
{
observers = _observers;
observer = _observer;
}
public void Dispose()
{
if (observers.Contains(observer))
{
observers.Remove(observer);
}
}
}
}
}
My Observer is a user control called 'ucClientGrid1ViewModel' that contains a datagrid where the search results are displayed.
namespace NSUCClientControls
{
public class ucClientGrid1ViewModel : ViewModelBase, IObserver<ClientFilterParameter>
{
private IDisposable cancellation;
private ObservableCollection<Client> clientsMultiple;
public ucClientGrid1ViewModel()
{
}
public ObservableCollection<Client> ClientsMultiple
{
get
{
var myClientDataAccess = new ClientDataAccess();
clientsMultiple = myClientDataAccess.GetClientListFromSQL_Test2();
return clientsMultiple;
}
set
{
}
}
public virtual void Subscribe(ucClientFilter1ViewModel provider)
{
cancellation = provider.Subscribe(this);
}
public void OnNext(ClientFilterParameter myFilter)
{
OnPropertyChanged("ClientsMultiple");
var myDummyWindow = new dummyWindow();
myDummyWindow.Show();
myDummyWindow.Close();
}
public void OnError(Exception error)
{
throw new NotImplementedException();
}
public void OnCompleted()
{
throw new NotImplementedException();
}
}
}
This all works and I get the search results that I am expecting. But what I don't understand is why the inclusion of the following lines actually speed things up!
var myDummyWindow = new dummyWindow();
myDummyWindow.Show();
myDummyWindow.Close();
I'm new to MVVM and the observer pattern, so as I was writing the code I had included message boxes at various points to help me to follow the flow of it. It was all working as expected. Then I removed the message boxes and it still worked but the application was pausing at the end before you could continue to keep searching.
Putting a message box back in at the end prevented this pause. Replacing the message box with a "DummyWindow" that just opens and closes has the same affect and prevents the pause at the end. This is what I currently have but I'd rather not leave this in there.
Presumably opening the window causes something else to happen which stops some redundant process, and this then prevents the pause? What else could I do to prevent the pause at the end, without using this DummyWindow?
I've tried searching on here and with Bing with no luck.
Thanks in advance!
Edit:
ViewModelBase...
namespace NSCommon
{
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
protected ViewModelBase()
{
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
public void Dispose()
{
OnDispose();
}
protected virtual void OnDispose()
{
}
}
}
ClientFilterParameter...
namespace NSCommon
{
public class ClientFilterParameter
{
public ClientFilterParameter(ucClientFilter1ViewModel myFilter)
{
FilterLocation = myFilter.FilterLocation;
WhereSearch1 = myFilter.WhereSearch1;
WhereSearch2 = myFilter.WhereSearch2;
}
private string filterLocation;
private string whereSearch1;
private string whereSearch2;
public string FilterLocation
{
get { return filterLocation; }
set { filterLocation = value; }
}
public string WhereSearch1
{
get { return whereSearch1; }
set { whereSearch1 = value; }
}
public string WhereSearch2
{
get { return whereSearch2; }
set { whereSearch2 = value; }
}
}
}
Hi I want to add pull to refresh on my apps using this component PullToResharp, The project is great but im having problem adding custom listview in fragment, does anyone here done this before help me resolve it, thanks.
here is the code
fragment
namespace ListViewPullToRefresh.Fragments
{
public class SampleListFragment : SupportListFragment
{
private IPullToRefresharpView _ptrView;
private List<TableItem> _itemHeading = new List<TableItem>();
public SampleListFragment() : base()
{
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.nav, null, false);
}
public override void OnViewStateRestored(Bundle savedInstanceState)
{
base.OnViewStateRestored(savedInstanceState);
_itemHeading.Add(new TableItem(){Heading = "Ivan", Subheading = "Guinto"});
_itemHeading.Add(new TableItem(){Heading = "Jonathan", Subheading = "Guinto"});
_itemHeading.Add(new TableItem(){Heading = "Keneth", Subheading = "Guinto"});
if (_ptrView == null && ListView is IPullToRefresharpView)
{
_ptrView = (IPullToRefresharpView)ListView;
_ptrView.RefreshActivated += ptr_view_RefreshActivated;
}
ListView.Adapter = new HomeScreenAdapter(this, _itemHeading);
}
private void ptr_view_RefreshActivated(object sender, EventArgs e)
{
View.PostDelayed(() =>
{
if (_ptrView != null)
{
_ptrView.OnRefreshCompleted();
}
}, 2000);
}
public override void OnDestroyView()
{
if (_ptrView != null)
{
_ptrView.RefreshActivated -= ptr_view_RefreshActivated;
_ptrView = null;
}
base.OnDestroyView();
}
public override void OnResume()
{
base.OnResume();
ListView.ItemClick += ListView_ItemClick;
}
public override void OnPause()
{
base.OnPause();
ListView.ItemClick -= ListView_ItemClick;
}
void ListView_ItemClick(object sender, AdapterView.ItemClickEventArgs e)
{
Toast.MakeText(Activity,e.Position+ " Clicked",ToastLength.Short).Show();
}
}
HomeScreenAdapter.cs
public class HomeScreenAdapter :BaseAdapter<TableItem>
{
public HomeScreenAdapter(Activity content, List<TableItem> items)
{
_content = content;
_items = items;
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = _items[position];
var view = convertView;
if (view == null)
view = _content.LayoutInflater.Inflate(Resource.Layout.CustomView, null);
view.FindViewById<TextView>(Resource.Id.Text1).Text = item.Heading;
view.FindViewById<TextView>(Resource.Id.Text2).Text = item.Subheading;
return view;
}
public override int Count
{
get { return _items.Count; }
}
public override TableItem this[int position]
{
get { return _items[position]; }
}
}
}
im getting this error
Error 1 The best overloaded method match for 'ListViewPullToRefresh.HomeScreenAdapter.HomeScreenAdapter(Android.App.Activity, System.Collections.Generic.List)' has some invalid arguments C:\Users******\Documents\Visual Studio 2013\Projects\ListViewPullToRefresh\ListViewPullToRefresh\Fragments\SampleListFragment.cs 43 32 ListViewPullToRefresh
Error 2 Argument 1: cannot convert from 'ListViewPullToRefresh.Fragments.SampleListFragment' to 'Android.App.Activity' C:\Users****\Documents\Visual Studio 2013\Projects\ListViewPullToRefresh\ListViewPullToRefresh\Fragments\SampleListFragment.cs 43 54 ListViewPullToRefresh
when i putting my custom adapter to the fragment, i get the error here ListView.Adapter = new HomeScreenAdapter(this, _itemHeading); and here public HomeScreenAdapter(Activity content, List<TableItem> items)
{
_content = content;
_items = items;
}
Suppose, I have objects:
public interface ITest
{
string Data { get; set; }
}
public class Test1 : ITest, INotifyPropertyChanged
{
private string _data;
public string Data
{
get { return _data; }
set
{
if (_data == value) return;
_data = value;
OnPropertyChanged("Data");
}
}
protected void OnPropertyChanged(string propertyName)
{
var h = PropertyChanged;
if (null != h) h(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
and its holder:
private BindingList<ITest> _listTest1;
public BindingList<ITest> ListTest1 { get { return _listTest1 ?? (_listTest1 = new BindingList<ITest>() { RaiseListChangedEvents = true }); }
}
Also, I subscribe to ListChangedEvent
public MainWindow()
{
InitializeComponent();
ListTest1.ListChanged += new ListChangedEventHandler(ListTest1_ListChanged);
}
void ListTest1_ListChanged(object sender, ListChangedEventArgs e)
{
MessageBox.Show("ListChanged1: " + e.ListChangedType);
}
And 2 test handlers:
For adding object
private void AddITestHandler(object sender, RoutedEventArgs e)
{
ListTest1.Add(new Test1 { Data = Guid.NewGuid().ToString() });
}
and for changing
private void ChangeITestHandler(object sender, RoutedEventArgs e)
{
if (ListTest1.Count == 0) return;
ListTest1[0].Data = Guid.NewGuid().ToString();
//if (ListTest1[0] is INotifyPropertyChanged)
// MessageBox.Show("really pch");
}
ItemAdded occurs, but ItemChanged not. Inside seeting proprty "Data" I found that no subscribers for my event PropertyChanged:
protected void OnPropertyChanged(string propertyName)
{
var h = PropertyChanged; // h is null! why??
if (null != h) h(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
Digging deeper i took reflector and discover BindingList:
protected override void InsertItem(int index, T item)
{
this.EndNew(this.addNewPos);
base.InsertItem(index, item);
if (this.raiseItemChangedEvents)
{
this.HookPropertyChanged(item);
}
this.FireListChanged(ListChangedType.ItemAdded, index);
}
private void HookPropertyChanged(T item)
{
INotifyPropertyChanged changed = item as INotifyPropertyChanged;
if (changed != null) // Its seems like null reference! really??
{
if (this.propertyChangedEventHandler == null)
{
this.propertyChangedEventHandler = new PropertyChangedEventHandler(this.Child_PropertyChanged);
}
changed.PropertyChanged += this.propertyChangedEventHandler;
}
}
Where am I wrong? Or this is known bug and i need to find some workaround?
Thanks!
BindingList<T> doesn't check if each particular item implements INotifyPropertyChanged. Instead, it checks it once for the Generic Type Parameter. So if your BindingList<T> is declared as follows:
private BindingList<ITest> _listTest1;
Then ITest should be inherited fromINotifyPropertyChanged in order to get BindingList raise ItemChanged events.
I think we may not have the full picture from your code here, because if I take the ITest interface and Test1 class verbatim (edit Oops - not exactly - because, as Nikolay says, it's failing for you because you're using ITest as the generic type parameter for the BindingList<T> which I don't here) from your code and write this test:
[TestClass]
public class UnitTest1
{
int counter = 0;
[TestMethod]
public void TestMethod1()
{
BindingList<Test1> list = new BindingList<Test1>();
list.RaiseListChangedEvents = true;
int evtCount = 0;
list.ListChanged += (object sender, ListChangedEventArgs e) =>
{
Console.WriteLine("Changed, type: {0}", e.ListChangedType);
++evtCount;
};
list.Add(new Test1() { Data = "yo yo" });
Assert.AreEqual(1, evtCount);
list[0].Data = "ya ya";
Assert.AreEqual(2, evtCount);
}
}
The test passes correctly - with evtCount ending up at 2, as it should be.
I found in constructor some interesting things:
public BindingList()
{
// ...
this.Initialize();
}
private void Initialize()
{
this.allowNew = this.ItemTypeHasDefaultConstructor;
if (typeof(INotifyPropertyChanged).IsAssignableFrom(typeof(T))) // yes! all you're right
{
this.raiseItemChangedEvents = true;
foreach (T local in base.Items)
{
this.HookPropertyChanged(local);
}
}
}
Quick fix 4 this behavior:
public class BindingListFixed<T> : BindingList<T>
{
[NonSerialized]
private readonly bool _fix;
public BindingListFixed()
{
_fix = !typeof (INotifyPropertyChanged).IsAssignableFrom(typeof (T));
}
protected override void InsertItem(int index, T item)
{
base.InsertItem(index, item);
if (RaiseListChangedEvents && _fix)
{
var c = item as INotifyPropertyChanged;
if (null!=c)
c.PropertyChanged += FixPropertyChanged;
}
}
protected override void RemoveItem(int index)
{
var item = base[index] as INotifyPropertyChanged;
base.RemoveItem(index);
if (RaiseListChangedEvents && _fix && null!=item)
{
item.PropertyChanged -= FixPropertyChanged;
}
}
void FixPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!RaiseListChangedEvents) return;
if (_itemTypeProperties == null)
{
_itemTypeProperties = TypeDescriptor.GetProperties(typeof(T));
}
var propDesc = _itemTypeProperties.Find(e.PropertyName, true);
OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, IndexOf((T)sender), propDesc));
}
[NonSerialized]
private PropertyDescriptorCollection _itemTypeProperties;
}
Thanks for replies!
The type of elements that you parameterize BindingList<> with (ITest in your case) must be inherited from INotifyPropertyChanged. Options:
Change you inheritance tree ITest: INotifyPropertyChanged
Pass concrete class to the generic BindingList