C# DataBinding / Update DynamicObject property from another thread - c#

Previously I was working on an implementation of DynamicObject data binding (see Dynamic object two way data binding), and the solution works great. Since then I have run into the need to be able to update the values from different threads it seems to break the bindings. I am able to update the DynamicObject instance from multiple threads however am unable to keep the bindings updated.
I tried to implement the SynchronizedNotifyPropertyChanged solution provided by Cody Barnes in the SO: INotifyPropertyChanged causes cross-thread error with no success.
Any help with binding a DynamicObject implementation that is updated via a non-ui thread would be greatly appreciated. The solution and gist attached work great (values updated on any thread (ui or non-ui) no problem), it is just the DataBinding to a Form Control that is not.
Edit #1 - (Thank you Reza Aghaei)
I see where my question is a bit vague, here is the implementation and goal I am trying to accomplish.
First, I have the Form which handles the creation of a logic 'engine' which can run tasks (based on internal class methods as well as external method calls or events). So somewhere inside the Form class, I generate this logic 'engine' object (will call it GameEngine for this example).
// GameEngine initializes and provides the DynamicData (MyCustomObject)
// --> engine is defined via public or private field of (this) Form class
engine = new GameEngine();
// Create the binding
LabelTestCounter.Databindings.Add("Text", engine.Data, "TestValue", true, DataSourceUpdateMode.OnPropertyChanged);
Now in the GameEngine class, I instantiate the DynamicData (MyCustomObject)
public class GameEngine
{
public dynamic Data;
// Constructor
public GameEngine()
{
Data = new MyCustomObject();
// I would like to initialize the data here as ownership really
// belongs to the 'GameEngine' object, not the Form
engine.Data.TestValue = "Initial test value";
}
public void StartBusinessLogic()
{
// In reality, this would be a long-running loop that updates
// multiple values and performs business logic.
Task.Run(() => {
data.TestValue = "Another Text";
});
}
}
Note in this example above, the MyCustomObject is (currently) an exact copy of what Reza has provided in his gist in his answer below. In the blog post and the gist provided, I lean towards the 'Option 1' provided as I would like the DynamicData object (MyCustomObject), to be self-containing the logic for synchronization for portability purposes.
Another note, the GameEngine, outside of holding the DynamicData object in a property, should not care whether or not the UI thread is using it, the responsibility of synchronization should (in my mind) remain with the UI thread. With that said, the DynamicData object should be aware of cross threaded calls and handle them accordingly as needed.
Edit #2 - Original question reference update
In reference to:
I tried to implement the SynchronizedNotifyPropertyChanged solution
provided by Cody Barnes in the SO: INotifyPropertyChanged causes cross-thread error with no success.
When working with the original SO Dynamic object two way data binding, and the original solution (again thank you Reza), I attempted to implement the referenced 'wrapper' to overcome the problem with cross-threaded updates. However, the only way that I could get the above solution to work was when the properties were hard-coded into the class. When any attempts were made to use a dynamic object, the bindings would either update once and lose the binding or throw some kind of binding exception.
I hope this clarifies the original question a little bit better.
Edit #3 - Symbol not resolved
Not sure if this is a problem, as compilation is working, (and this may be Resharper, not sure as I don't recall the problem previously).
In 'MyCustomObject':
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
var properties = new List<MyCustomPropertyDescriptor>();
foreach (var e in dictionary)
properties.Add(new MyCustomPropertyDescriptor(e.Key, (e.Value?.GetType()) ?? typeof(object)));
return new PropertyDescriptorCollection(properties.ToArray());
}
(e.Value?.GetType()) <-- GetType is showing 'Cannot resolve symbol 'GetType'.
however the code does compile without errors so I am not sure why/when exactly I started seeing this.
Edit #4 - Resolved Edit #3
No idea what caused the problem in Edit 3, but it grew into showing other errors (mysteriously) such as Cannot apply indexing with to an expression of type 'System.Collections.Generic.Dictionary', however, I did a Build -> Clean Solution and then closed the solution and reopened it in Visual Studio and the problem seemed to go away with that error highlighting in the editor (which I have been seeing a bit of strange behavior like that since I started playing with Resharper (EAP), so possibly early access bugs? but that problem is unrelated to this SO, the original SO has been resolved and the strange behavior in edit 3 will be better handled by JetBrains/Resharper Team rather than here at this point.
Edit #5 - Special Thanks
I hope this is not inappropriate (if it is, admins feel free to delete/edit this), however, I would like to send a special thank you to Reza Aghaei for all your help both here on SO as well as in the background. Reza, your blog, gists and other assistance with this ongoing problem has really helped both in solving the problem and helping me understand the reasons behind the solution.

To raise OnPropertyChanged from a non-UI thread in a way which updates the UI, you need to pass an instance of ISynchronizeInvoke to your implementation of ICustomTypeDescriptor:
ISynchronizeInvoke syncronzeInvoke;
public MyCustomObject(ISynchronizeInvoke value = null)
{
syncronzeInvoke = value;
}
Then on OnPropertyChanged use that ISynchronizeInvoke to Invoke when InvokeRequired:
private void OnPropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
{
if (syncronzeInvoke != null && syncronzeInvoke.InvokeRequired)
syncronzeInvoke.Invoke(handler, new object[]
{ this, new PropertyChangedEventArgs(name) });
else
handler(this, new PropertyChangedEventArgs(name));
}
}
This way, the event will raise in UI thread when needed.

Related

C# Xamarin - AddTarget to UILongPressGestureRecognizer passing it a Selector implemented in a parent UIViewController

I am having difficulties raising an event from a UICollectionViewCell to a handler which exists within a parent UIViewController. I am trying to detect a LongPress upon a UICollectionViewCell and fire a method in the UIViewController - placing the view into an "Edit Mode" show/hiding UI elements and reloading etc. To do so my UIViewController method must call a data access layer and thus it makes sense the method is placed at this level in the view hierarchy.
I have followed the official documentation and techniques from Xamarin and the great advice from Adam Kemp here.
There are so many conflicting methodologies for achieving this in Xamarin.IOS. Alas, at present, none seem to work in my case. I must be doing something fundamentally wrong!
I have a custom class which inherits from UICollectionViewCell which contains a private UILongPressGestureRecognizer _recognizer; and method named UpdateCell as follows:
public virtual void UpdateCell(bool CanEdit)
{
// Other properties and logic omitted for brevity (no relevance to question)
this._recognizer = new UILongPressGestureRecognizer();
this._recognizer.MinimumPressDuration = 0.5;
this._recognizer.AddTarget(this, new ObjCRuntime.Selector("OnEditModeActivated"));
this.AddGestureRecognizer(_recognizer);
}
I want the Long press to fire the following method within my UIViewController:
[Export("OnEditModeActivated")]
public async void OnEditModeActivated()
{
if (recognizer.State == UIGestureRecognizerState.Ended)
{
this._canEdit = true;
// call Data Access layer using await
_source.Dispose(); // dispose of current UICollectionViewSource object
_source = new UICollectionViewSource(_data, _canEdit);
UICollectionView.Source = _source;
}
}
The line causing issues is
`this._recognizer.AddTarget(this, new ObjCRuntime.Selector("OnEditModeActivated:"));`
The selector should point to the parent UIViewController which has a public void OnEditModeActivated decorated with [Export("OnEditModeActivated:")].
I can see the compiler running into the "unrecognized selector" exception due to this (1st arg passed to AddTarget) obviously being the UICollectionViewCell rather than the UIViewController and thus it will look to resolve AddTarget by UICollectionViewCell (instance) -> OnEditActivated (does not exist).
Instead of this I need to reference the parent UIViewController but I don't want to pass it by reference to the UpdateCell method - this would mean passing the reference through many layers thus my code becoming somewhat spaghetti like.
If anyone can explain a better way, by all means feel free. I looked at raising an event within the UICollectionViewCell instance but to no avail. Moreover I can't seem to get the delegation pattern right in Xamarin!
The problem is the hierarchy I am using being: UIViewController -> UICollectionView -> UICollectionViewSource -> UICollectionViewCell.
I am sure it is possible to raise an event/call delegate/call method and resolve at the view controller level...
Thanks for taking the time to help on this.
All the best,
John
Wiring up the _recognizer to the ContentView and declaring a method within this fixed my issue.
I changed:
this.AddGestureRecognizer(_recognizer);
to:
cell.ContentView.AddGestureRecognizer(_recognizer);
and created a OnLongPressed method at the same level decorated with Export["OnLongPressed:"] which raises an event resolved at theUIViewController` level.
As an aside, the UILongPressGestureRecognizer raises mutliple events and one should check the long press has actually ended otherwise any further events / method calls will occur more than once, for example:
if(recognizer.State == UIGestureRecognizerState.Ended)
{
// Raise events call methods here only when the long press ended
}
UILongPressGestureRecognizers will fire with multiple states which you can test using the State property documentation here.

CollectionChanged and IList of Items - why the difficulties

I am looking into the topic why a ObservableCollection/ListCollectionView/CollectionView raises a NotSuportedException when calling the CollectionChanged with the parameter of IList.
//Throws an exception
private void collectionChanged_Removed(IList items)
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items));
}
I have found several Webpages, talking about this topic and they suggest either using the Reset ability to force a complete redraw of the UI, or just simply call for each item the CollectionChanged or some more creative way: http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/listcollectionviewcollectionview-doesnt-support-notifycollectionchanged-with-multiple-items.aspx
I just cant find the WHY?
For me it makes no sense why this would be the case.
Is there any chance that this lacking feature, which we all face at some point in our Development Cycle, since the Add method just has to much of an overhead when you want to Add multiple items fast, will be implemented any time (.Net 5, C# 6...).
Edit:
In my specific case, I have written my own class :
public class ObservableList<T> : IList<T>, IList, IEnumerable<T>,
INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
//other stuff...
}
And still throws the said NotSupportedException.
Inspired by VirtualBlackFox's answer I took a look under the hood of the CollectionView classes in ILSpy. It appears that the primary reason why the lack of support for Range operations is because internally the CollectionView uses a change log to centrally manage pending changes of all kinds and dispatch messages on a per/item basis.
By its very purpose, the CollectionView could store 1000s of records used simultaneously with multiple UI controls representing its underlying data. So, adding or deleting records must be done on an atomic basis to maintain the integrity of the UI controls that access the view information. You can't synchronize incremental changes with multiple UI subscribers using bulk change events without passing the grouping, sorting, and filtering functionality of the CollectionView onto the UI controls that use it.
The CollectionView also derives from System.Windows.Threading.Dispatcher so the issue may be co-related to how it manages work items on it's thread. The call path includes a protected ProcessCollectionChanged method that specifically processes individual changes on the UI thread. So, updating ranges may interfere with the whole threading model it uses to interact with UI elements that use it.
I totally agree that having consumers of the CollectionView pass in an IList to NotifyCollectionChangedEventArgs is silly. It specifically rejects anything with a length != 1 and hard-codes for args.NewItems[0] internally.
As #nmclean said in the comments the problem isn't in the collection emitting CollectionChanged but on the receiving end.
If you look at the code of ListCollectionView (Using DotPeek for example or on new versions of visual studio you can access the reference source code) you will notice that each time the attached collection change it call a method ValidateCollectionChangedEventArgs that throw when there is more than one element changed
private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (e.NewItems.Count == 1)
break;
else
throw new NotSupportedException(System.Windows.SR.Get("RangeActionsNotSupported"));
...
The rest of the class and it's CollectionView base class are already big beasts (2710 and 2027 lines in the source published on the reference source code) so Microsoft might have wanted to avoid supporting a complex case that the collection that they recommend don't create anyway.
(The method handling collection change is already 141 lines in the reference source code and adding multi element support will make it grow even more or need a careful split and potentially break other things...)
I didn't find any suggestions linked to adding support for range events in Microsoft Connect but you should submit your own if it is important for you.
I guess this is mainly for performance reasons. I was also shocked when I saw that CollectionView also does not accept the -1 value for NewStartingIndex or OldStartingIndex. So basically, CollectionView always wants a mapping from items to their indices. However, it does not require this mapping to be exact (which is strange from my point of view), it is allowed that NewStartingIndex is smaller than the correct index (unless it is -1).
I think the root of the problem is that large parts within Microsoft still think that a list is the one and only way to implement a collection, which of course just is not true. Here, the creators of NotifyCollectionChangedEventArgs thought about alternatives (such as linked lists or hashing collections), but the UI guys did not. Or at least they did not want to support these collections as they appear rather rarely.
The temporary solution is useless. It only hides the problems.
The solution could lie in making events where you provide an entire new list to the observers. That way Microsoft won't have to implement efficient range handlers for each type of observer.

Application.Current is null when calling from a unittest

I have a method that I'm trying to call from a unit test.
This method will in real life be run from a background thread. It uses some code to kick off in the invoke updates to the UI thread (using Application.Current.Dispatcher.BeginInvoke .... ).
However Application.Current is null when being called from the unit tests.
I don't really what to put an if (Application.Current !=null) around everything to fix.
Is there any other way around this?
_statusUpdates is an ObservableCollection
Below is the part of the code in the method I'm looking to test (it is more of an integration test than a unit test to be fair).
Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (EventHandler)delegate
{
_statusUpdates.Add(new StatusUpdate
{
DateTime = DateTime.Now,
Message = "Checking For Messages"
});
}, null, null);
The following code snippet works for me:
if (System.Windows.Application.Current == null)
{ new System.Windows.Application { ShutdownMode = ShutdownMode.OnExplicitShutdown }; }
IIRC, I had a problem where Application was null using a WPF control embedded in a WinForms application and that code snippet was suggested as a solution to the problem in another question on StackOverflow (sorry, can not find the source). It solves the same problem in unit tests (and I don't believe the ShutdownMode property needs to be explicitly set in that case).
As already stated, you simply won't have an Application class during unit tests.
That said, there's an issue here I think needs addressing - by having code that relies on a defined static property, in your case Application.Current.Dispatch, you are now very tightly coupled to the specific implementation of that class, namely the WPF Application class, where you do not need to be.
Even if you simply wrap the idea of "the current root dispatcher" in a Singleton-style class wrapper, now you have a way of decoupling yourself from the vagaries of the Application class and dealing directly with what you care about, a Dispatcher:
Note, there are MANY MANY ways to write this, I'm just putting up the simplest possible implementation; hence, I will not be doing any multithreaded safety checks, etc.
public class RootDispatcherFetcher
{
private static Dispatcher _rootDispatcher = null;
public static Dispatcher RootDispatcher
{
get
{
_rootDispatcher = _rootDispatcher ??
Application.Current != null
? Application.Current.Dispatcher
: new Dispatcher(...);
return _rootDispatcher;
}
// unit tests can get access to this via InternalsVisibleTo
internal set
{
_rootDispatcher = value;
}
}
}
Ok, now this implementation is only slightly better than before, but at least you now have finer control over access to the type and are no longer strictly dependent on the existence of an Application instance.
Use Dispatcher.CurrentDispatcher instead of Application.Current.Dispatcher
Gets the System.Windows.Threading.Dispatcher for the thread currently
executing and creates a new System.Windows.Threading.Dispatcher if one
is not already associated with the thread.
You will not have an Application object in a unit-test runner. These are usually "console" based applications that simply run and execute non-UI code ("units").
I suggest you don't use a unit test framework to test UI-specific information, I suggest a automated UI testing framework to do that.
So the issue here is that somewhere your Application object has to be created. So you need to find where the System.Windows.Application (or some descendent) class is being instantiated.
If the project was built from a template, then you'll probably find this class in the App.xaml file. You just need to make sure that this gets instantiated somehow. Else, search your entire project for an Application class, and then you'll have to instantiate it manually. Should fix it.

C# Null Reference Exception

I am working on the following program which deals with threads and thread priorities.
I have constructed a windows form containing a rich textbox (DisplayTextBox) and two buttons (StartButton for running app and ExitButton for closing app).
In the form, I am creating several threads and running them one after another. The method used by each thread is found in the Threading class. The method in question is PrintOnScreen().
In this method, I am appending the thread name as well as its priority to str of type StringBuilder. Then I want to display the contents of str in DisplayTextBox (which is in Form.cs).
However, I am getting a "NullReferenceException was unhandled: Object reference not set to an instance of an object" error. The line where the error crops up is the following:
DisplayTextBox.Text = Convert.ToString(str);
Can you please help me to solve this error? Thanks.
Edit
Thanks to all for your help. In order to solve the problem, I copied the PrintOnScreen method to the Form.cs class and discarded Threading.cs.
I later used the code given by Anand and placed it below t2.Join(). Now it works like a charm.
The problem occurs in the constructor of your form. You declare DisplayText again as local member, so that the field of your form isn't initialized. Change your constructor like this:
private void Form1_Load(object sender, EventArgs e)
{
DescTextBox.Visible = false;
DisplayTextBox = new RichTextBox();
DisplayTextBox.Location = new Point(15, 31);
DisplayTextBox.Height = 258;
DisplayTextBox.Width = 303;
panel1.Controls.Add(DisplayTextBox);
}
and it should work like you expect.
Edit: But beware of the problems you will get, when you want to change UI elements from a background worker thread. In those cases you will have to use the invoke pattern.
I tried this in simple console application and it worked fine.
The issue is with DisplayTextBox. If you want to interact with any control, then you have to use UI thread or Dispatcher.
Use the following code in your Threading Class,
instead of
DisplayTextBox.Text = Convert.ToString(str);
use this one
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action(
delegate()
{
DisplayTextBox.Text = Convert.ToString(str);
}
));
You cannot communicate with controls from any other thread than main application's thread. You need to use the dispather for this. Have a look here: http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.aspx
EDIT
#Fischermaen - heh, didn't notice this but it still won't work because of the line, where he wrote it fails. He still needs to use dispatcher to set the Text property on the control from the non-main threads
EDIT 2
Related threads:
How to update the GUI from another thread in C#?
In WinForms, why can't you update UI controls from other threads?
Um.
Can't see from here, but part of the reason you are having difficulty is the lack of encapsulation and poor lifetime management in your code.
Instantiate str inside the PrintOnScreen method, give it a better name as well, no appranrent need to be a member variable, no need at all to make it public.
Instead of having a void result, have it return the string result
e.g.
SomeTextBox = PrintOnScreen(); // (GetThreadDetails might be a better name...)
As a tip don't mix presentation and logic. Keep you UI controls thoroughly inside whatever owns them.
And don't do
public SomeType SomeName;
make it a property and give it getter and a setter , even if it's the short form
public SomeType SomeName {get; set;}
Any other bit of code could do something really foolish like
Form1.SomeType = (SomeOtherType)someVar; // with appaling consequences.
Nothing should have more than one owner, any other path leads to ruin.
PS str.ToString() would be a better option than Convert.ToString(str);
Well, this is not very clear for me what you are trying to achieve, however the problem is that the base form Threading class inherits from not being loaded when you are trying to access control on it (window handle still not exists) Also this is the reason you see nothing on Form1 itself, because your threads outputs into hidden form, which is not shown.
I think that you are trying to solve thread access issue, raised by SOReader. This is not proper way to do this.

A nasty COM interop problem in VSIX

For some time now, I've observed an intermittent COM problem in my VSIX package for Visual Studio 2010. Attempting to subscribe to one of the IDE's COM-based event sinks randomly throws the following error:
"COM object that has been separated from its underlying RCW cannot be used"
A repro case boils down to this code (which must be used in VSIX, obviously):
using System;
using EnvDTE;
using EnvDTE80;
class Test
{
private readonly Events _events;
private readonly Events2 _events2;
private readonly BuildEvents _buildEvents;
private readonly ProjectItemsEvents _projectItemsEvents;
public Test(IServiceProvider provider)
{
var dte = (DTE)provider.GetService(typeof(DTE));
var dte2 = (DTE2)dte;
// Store all references in fields as a GC precaution.
_events = dte.Events;
_events2 = (Events2)dte2.Events;
_buildEvents = _events.BuildEvents;
_projectItemsEvents = _events2.ProjectItemsEvents;
// Proceed to subscribe to event sinks.
_buildEvents.OnBuildBegin += BuildBeginHandler; // BOOM!
_projectItemsEvents.ItemAdded += ItemAddedHandler;
}
private void ItemAddedHandler(ProjectItem projectItem) { }
private void BuildBeginHandler(vsBuildScope scope, vsBuildAction action) { }
}
I've learned about a possible cause from numerous descriptions of similar problems that can be found on the net. It's basically a side effect of the way Runtime Callable Wrappers and GC interact during COM interop. Here's a link to a similar problem complete with explanation.
I'm fine with that explanation, especially because it suggests an easy workaround - storing the event sink reference in a field in order to prevent it from being prematurely GC'ed. Indeed, many people seem to have solved their problem this way.
What bothers me is that it doesn't work in my case. I'm really stumped as to why. As you can plainly see, I already store all object references in fields as a precaution. Yet the error still occurs. I tried being even more explicit using GC.KeepAlive() calls at the end of the ctor, but to no avail. Is there anything else left to do?
Without a solution, my VSIX randomly fails to load, leaving the user with a single option: to restart Visual Studio and hope it doesn't happen the next time.
Any help will truly be appreciated!
Well, I gave up and simply did the only thing that crossed my mind. I figured that since this is obviously a race condition I can't affect in a predictable manner, I might as well reenter the race if I lose.
So I moved the subscription lines into a while loop that try..catch-es them and retries after a bit of Thread.Sleep(). The loop exits either when both subscriptions succeed or when I've been continuously losing the race for more than 2 seconds.
The kicker is, I haven't lost the race once since I've implemented the change. A true Heisenbug, if I ever saw one.
Anyway, I'm going to stick with this until a proper solution occurs to me or the bug reappears.
I suspect that your problem is really that you are attempting to wire up your event handlers too soon. You normally need to be doing these sorts of things in the Initialize method of your package / toolwindow / whatever - generally speaking if you need to use a service you need to do if after the Initialize method has been called, definitely don't do this in the constructor of your Package.
(This is just a hunch - your Test class doesn't implement any VSX interfaces and so I can't see from your sample when the constructor is being called)

Categories

Resources