Use reflection to get actual value of the property notified by INotifyPropertyChanged? - c#

I am working on a project that will use INotifyPropertyChanged to announce property changes to subscriber classes.
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Quantity")
....
It appears to me that when the subscribing class receives the notification, the only available value it can get is the name of the property. Is there a way to get a reference of the actual object that has the property change? Then I can get the new value of this property from the reference. Maybe using reflection?
Would anyone mind writing a code snippet to help me out? Greatly appreciated.

Actual object is sender (at least, it should be):
void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var propertyValue = sender.GetType().GetProperty(e.PropertyName).GetValue(sender);
}
If you care about performance, then cache sender.GetType().GetProperty(e.PropertyName) results.

Note: this interface is primarily a data-binding API, and data-binding is not limited to simple models like reflection. As such, I would suggest you use the TypeDescriptor API. This will allow you to correctly detect changes for both simple and complex models:
var prop = TypeDescriptor.GetProperties(sender)[e.PropertyName];
if(prop != null) {
object val = prop.GetValue(sender);
//...
}
(with a using System.ComponentModel; directive)

Related

c# show Settings value in debug mode at runtime

Hello potential Readers
I wanted to know if one can see the stored value of a settingsobjet at runtime in debug. Like how you see the value of a variable if you hover above them. I know i could just always make :
string a = Properties.Settings.Default.(myObjectName) and hover over a, but I want to know if there is a quicker way. I already tried Google but it didn't really show me anything that answer my question:(
image of the settings
(i'm using visualstudios 2015 and .Net Framework 4.5.2).
Properties.Settings.Default.FileLocation = ProfileListBox.OpenFile;
//problem: I use the Filelocation quite often in my code but have to always backtrack to see what the currently assigned value for Filelocation is.
If you know of an other way to see the Filelocationvalue (in my case) it would be very appriciated if you could tell.
My be this approach helps you with your problem:
By simply handling the PropertyChanged Event of the Settings.
void test() {
Settings.Default.PropertyChanged += Default_PropertyChanged;
}
private void Default_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "MyProperty")
{
var tValue = Settings.Default.PropertyValues[e.PropertyName].PropertyValue.ToString();
toolStripStatusLabelMessage.Text = String.Format("Property {0} changed to {1} ", e.PropertyName , tValue);
}
}

TwoWay Collection Binding Sync/Lock

What is the best way to synchronize 2 sets of data via Binding?
Target = Custom Setters - raises custom events whenever something changed
Source = ObservableCollection - raises events whenever collection changed
Now my question is, when I receive an update from one collection (e.g. Source.CollectionChanged event) I need to call the custom TargetSetters, and ignore the events called which originated from my update.
And also the other way, when the Target custom events get fired, i need to update the source, but ignore the CollectionChanged event.
At the moment, I am keeping a reference to my handlers, and removing that before updating any of the collections. e.g.
private void ObservableCollection_OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
CustomObject.SelectionChanged -= CustomObject_SelectionChanged;
// Do change logic and update Custom Object....
CustomObject.SelectionChanged += CustomObject_SelectionChanged;
}
void CustomObject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ObservableCollection.CollectionChanged -= ObservableCollection_OnCollectionChanged;
// Do change logic and update ObservableCollection...
ObservableCollection.CollectionChanged += ObservableCollection_OnCollectionChanged;
}
I have seen that you can use an if statement to check if the updates are from source, and if they are ignore them. e.g.
private void ObservableCollection_OnCollectionChanged2(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
if (BindingTargetUpdating) return;
BindingSourceUpdating = true;
// Do change logic and update Custom Object....
BindingSourceUpdating = false;
}
void CustomObject_SelectionChanged2(object sender, SelectionChangedEventArgs e)
{
if (BindingSourceUpdating) return;
BindingTargetUpdating = true;
// Do change logic and update ObservableCollection...
BindingTargetUpdating = false;
}
After Google + SO Search came back with nothing, I wanted to see how other people are doing this, and is there something really simple I am missing here that solves this problem? (I know that the examples are not thread-safe)
If not, what is the preferred way? Removing and attaching handlers, or setting a boolean flag? What is more performant (yes i know this is highly unlikely to cause a bottleneck but out of curiosity)
Reason I am asking is because, currently I am implementing Attached Behaviours and for each behaviour, I am creating 2 sets of Dictionaries which hold the references to the handlers for each object as state has to be passed around.
I can't seem to find the the source code for the binding mechanism of the .NET Binding classes, to see how MS implemented it. If anyone has a link to those it would be greatly appreciated.
The mechanism you're using - having a boolean which tracks when updates occur, and blocking around it, is the most common approach.
Personally, I prefer to wrap that logic into a small utility that implements IDisposable. This makes it easier to guarantee that you'll always clean up after yourself.
A utility you can use for this would look something like:
class Guard : IDisposable
{
readonly Func<bool> getter;
readonly Action<bool> setter;
readonly bool acquired = false;
public Guard(Func<bool> getter, Action<bool> setter)
{
this.getter = getter;
this.setter = setter;
if (this.getter() == false)
{
this.setter(true);
this.acquired = true;
}
}
public bool Acquired { get { return this.acquired; } }
void IDisposable.Dispose()
{
if (acquired)
{
this.setter(false);
}
}
}
You can then write:
private void ObservableCollection_OnCollectionChanged2(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
using(var guard = new Guard(() => BindingTargetUpdating, v => BindingTargetUpdating = value))
{
if (guard.Acquired)
{
// Do change logic and update Custom Object....
}
}
}
This isn't necessarily any shorter - its probably longer to write, but does provide guarantees that you'll release your blocks if exceptions occur. You can always subclass Guard to shrink the usage down if you'll be using it frequently.

WeakSubscribe in MvvmCross doesn't work correct

Context:
I have an unknown Model-Object which contains one or more ObservableCollections.
These collections contains objects which implements INotifyPropertyChanged.
I now want to observe the PropertyChangedEvents for all objects inside the Model-Object.
What I have done:
So I wrote this method which uses reflection to find the specific objects. This all works fine. Except the part where it comes to the MvvmCross function WeakSubscribe. I really like the idea behind, but it seems it loses the reference and don't fire the event.
Strange: If I debug this code it works correct, but without breakpoints it's not working.
private void SubscribeToDetailData()
{
var tempTokenList = new List<MvxNotifyPropertyChangedEventSubscription>();
var fieldInfos =
DetailData.GetType().GetRuntimeProperties().Where(f => Helpers.IsSubclassOfRawGeneric(typeof (ObservableCollection<>), f.PropertyType));
foreach (var fieldInfo in fieldInfos)
{
var collection = fieldInfo.GetValue(DetailData) as IEnumerable<object>;
if (collection == null)
continue;
foreach (var inpc in collection.Cast<INotifyPropertyChanged>())
{
tempTokenList.Add(inpc.WeakSubscribe((sender, e) => DetailDataPropertyChanged(e.PropertyName)));
}
}
_subscriptionTokens = tempTokenList.ToArray();
}
// This method is never raised
private void DetailDataPropertyChanged(string propertyName)
{
if (_enabledFields.Evaluate(DetailData, propertyName))
RaisePropertyChanged(() => FieldEnabledState);
}
It might be that your subscribed Action is getting garbage collected.
This can be caused, I think, if the compiler creates an instance of an anonymous class to implement your anonymous Action. I wouldn't normally expect this to happen in your code because you are not using any local variables in your Action - but this could be the case.
Does your code work if you change the subscription to:
tempTokenList.Add(inpc.WeakSubscribe(DetailDataPropertyChanged));
with the method signature changed to:
private void DetailDataPropertyChanged(object sender, PropertyChangedEventArgs e)

Extract and compare eventargs data

I have a problem comparing data I get from an eventargument, to be more specific, I have 2 classes that uses an interface, lets call it 'IInt'. I also have a list that is filled with objects of these two classes.
I currently use the OnDragDrop event to drag objects from this list, but I need a way to determine if it was an object of class1 or class2 that I draged. Is there a way to extract the data and compare it using the DragEventArgs drgevent?
So first of all, when I grab an object from the list.
foreach (IInt d in dlist)
DoDragDrop(d.GetType(), DragDropEffects.Move);
And when I want to extract the data ie check what object was draged.
protected override void OnDragDrop(DragEventArgs drgevent)
{
if (drgevent.GetType() == typeof(DragedObject))
do stuff...
}
After finally getting to the root of this, it appears that your answer is here
if (e.Data.GetDataPresent(typeof(YourType))) {
YourType item = (YourType)e.Data.GetData(typeof(YourType));
If I am understanding you correctly, then you are looking for reflection
You can use GetType
arg.GetType() == typeof(Class1)
or is
arg is Class1
UPDATE
Without more code than provided, here is what it sounds like you need to do:
foreach (IInt d in dlist)
DoDragDrop(d, DragDropEffects.Move);
DoDragDrop sounds like it will create the DragEventArgs from the object and effect, so you would want something like this:
protected override void OnDragDrop(DragEventArgs drgevent)
{
if (drgevent.dObject.GetType() == typeof(DraggedObject))
do stuff...
}
Notice that you are not testing the arg itself, instead you are testing what it contains.

How to detect if ASP.NET control properties contain DataBinding expressions?

I have a custom control which inherits from System.Web.UI.Control and some of its properties can be declaratively set using databinding expressions. e.g.
<foo:Foo runat="server" MyFoo="<%# this.GetFoo() %>" />
Now, when I do that I need to call .DataBind() on the control (or one of its parents) to evaluate these expressions.
What I would like to be able to do is detect if any properties were set this way and just automatically have the custom control call this.DataBind() after OnPreRender or there about.
So the question: how do I detect if databinding expressions are waiting to be executed?
I'm convinced that in some ControlBuilder or DataBindContext class lives the information needed to determine this. I've hunted around with Reflector and cannot seem to find it.
I should add, that I don't want to pay the overhead of executing DataBind() if no direct properties have been assigned this way. This is why I'd like to detect before hand. This class is extremely light but I'd like the ability to declaratively set properties without needing any code behind.
Doing some deeper looking into ControlBuilder, I noticed that the compiled factory for each control instance will attach a DataBinding event handler when there are data binding expressions present. I've found that checking for this seems to be a very reliable method for determining if data binding needs to occur. Here is the basis of my solution to the problem:
using System;
using System.Reflection;
using System.Web.UI;
public class AutoDataBindControl : Control
{
private static readonly object EventDataBinding;
private bool needsDataBinding = false;
static AutoDataBindControl()
{
try
{
FieldInfo field = typeof(Control).GetField(
"EventDataBinding",
BindingFlags.NonPublic|BindingFlags.Static);
if (field != null)
{
AutoDataBindControl.EventDataBinding = field.GetValue(null);
}
}
catch { }
if (AutoDataBindControl.EventDataBinding == null)
{
// effectively disables the auto-binding feature
AutoDataBindControl.EventDataBinding = new object();
}
}
protected override void DataBind(bool raiseOnDataBinding)
{
base.DataBind(raiseOnDataBinding);
// flag that databinding has taken place
this.needsDataBinding = false;
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
// check for the presence of DataBinding event handler
if (this.HasEvents())
{
EventHandler handler = this.Events[AutoDataBindControl.EventDataBinding] as EventHandler;
if (handler != null)
{
// flag that databinding is needed
this.needsDataBinding = true;
this.Page.PreRenderComplete += new EventHandler(this.OnPreRenderComplete);
}
}
}
void OnPreRenderComplete(object sender, EventArgs e)
{
// DataBind only if needed
if (this.needsDataBinding)
{
this.DataBind();
}
}
}
This solution disables itself if no DataBinding event handler is attached or if the control is manually data bound (directly or via a parent).
Note that most of this code is just jumping through hoops to be able to test for the existence of the event. The only reflection needed is a one-time lookup to get the object used as the key for EventDataBinding.
There is an internal ArrayList called SubBuilders on the ControlBuilder class. For each databinding expression TemplateParser enocunters, ProcessCodeBlock() adds a CodeBlockBuilder object with a BlockType property CodeBlockType.DataBinding to SubBuilders.
So if you can get a handle to the ControlBuilder you want, you should be able to reflectively iterate over SubBuilders and look for objects of type CodeBlockBuilder where BlockType == CodeBlockType.DataBinding.
Note of course this is all kinds of nasty and I'm really suspicious this is the best way to solve your core problem. If you take two steps back and look at the original problem, maybe post that on Stackoverflow instead - there's plenty of super-smart people who can help come up with a good solution.

Categories