PDFx – Property Dependency Framework – Part XII, Callbacks

The PDFx is a lightweight open source .NET library that allows developers to describe dependencies between Properties in declarative C# code. Once relationships are registered, the framework monitors property changes and ensures that the INotifyPropertyChanged.PropertyChanged event is fired for all directly and indirectly dependent properties in a very efficient way. The library is available for WPF, Silverlight, WinRT (Windows Store) and Windows Phone.

I’ve developed the PDFx as an InterKnowlogy RECESS project and published the source code and examples on codeplex.

In a series of blog posts I am going to cover the library’s most important features:

Callbacks

When a source property changes, the PDFx allows you not only to automatically fire the PropertyChanged event for dependent properties, but also to execute callbacks in numerous scenarios.

The registration API for callbacks is not declarative, as the Property Dependency Registration API is, but rather imperative. Furthermore, the registration should not take place within a Property Getter but rather within a dedicated Registration method, as the following examples show.

Callbacks on Property Changes

The following example demonstrates how you can register Callbacks for normal Property Changes:

class SimpleCallbackExample : BindableExt
{
	public SimpleCallbackExample()
	{
		RegisterCallbacks();
	}

	private void RegisterCallbacks()
	{
		RegisterCallbackDependency(Property1, OnProperty1Changed);
	}

	private void OnProperty1Changed()
	{
		Debug.WriteLine("Property1 has changed!");
	}

	private AnyType _property1;
	public AnyType Property1
	{
		get { return _property1; }
		set
		{
			_property1 = value;
			NotifyPropertyChanged(() => Property1);
		}
	}
}

The used overload of RegisterCallbackDependency expects you to pass in the owner of a property as the first parameter, a delegate that points to the Property that is to be monitored as the second parameter and finally a pointer to the callback that is supposed to be executed when the Property changes.

In the example above, OnProperty1Changed will be executed by the PDFx whenever Property1 changes.

Callbacks on Objects

The following example demonstrates how you can register one callback for all Property Changes of an object:

class ObjectCallbackExample : BindableExt
{
	public ObjectCallbackExample()
	{
		RegisterCallbacks();
	}

	private void RegisterCallbacks()
	{
		RegisterCallbackDependency(this, OnAnyPropertyChanged);
	}

	private void OnAnyPropertyChanged()
	{
		Debug.WriteLine("Any Property has changed!");
	}

	private AnyType _property1;
	public AnyType Property1
	{
		get { return _property1; }
		set
		{
			_property1 = value;
			NotifyPropertyChanged(() => Property1);
		}
	}

	private AnyType _property2;
	public AnyType Property2
	{
		get { return _property2; }
		set
		{
			_property2 = value;
			NotifyPropertyChanged(() => Property2);
		}
	}
}

The used overload of RegisterCallbackDependency expects you to pass in a property owner that is to be monitored as the first parameter and a pointer to the callback that is supposed to be executed when any Property changes as the second parameter.

In the example above, OnAnyPropertyChanged will be executed by the PDFx whenever Property1 or Property2 changes.

Callbacks on Collections’ children

The PDFx also allows you to register callbacks that are to be fired when a specific property of an ObservableCollection’s child changes:

class CollectionPropertyCallbackExample : BindableExt
{
	private DependencyFrameworkObservableCollection<Child> _children 
		= new DependencyFrameworkObservableCollection<Child>();

	public CollectionPropertyCallbackExample()
	{
		RegisterCallbacks();
	}

	private void RegisterCallbacks()
	{
		RegisterCallbackDependency(_children, k => k.Property1, OnCollectionChildPropertyChanged);
	}

	private void OnCollectionChildPropertyChanged()
	{
		Debug.WriteLine("Property1 of a child has changed!");
	}

	private class Child : BindableExt
	{
		private AnyType _property1;
		public AnyType Property1
		{
			get { return _property1; }
			set
			{
				_property1 = value;
				NotifyPropertyChanged(() => Property1);
			}
		}
	}
}

The first parameter of this RegisterCallbackDependency overload expects the collection that is to be monitored. The second parameter is to point to the Children’s Property that is of interest. The third parameter finally points to the callback that is to be executed.

In the example above, the method OnCollectionChildPropertyChanged will get fired whenever Property1 of any child changes.

Callbacks on entire Collections

The PDFx also allows to register callbacks that get executed whenever any child’s property changes or a child gets added or removed:

class CollectionCallbackExample : BindableExt
{
	private DependencyFrameworkObservableCollection<Child> _children
		= new DependencyFrameworkObservableCollection<Child>();

	public CollectionCallbackExample()
	{
		RegisterCallbacks();
	}

	private void RegisterCallbacks()
	{
		RegisterCallbackDependency(_children, OnCollectionChanged);
	}

	private void OnCollectionChanged()
	{
		Debug.WriteLine("Collection has changed!");
	}

	private class Child : BindableExt
	{
		private AnyType _property1;
		public AnyType Property1
		{
			get { return _property1; }
			set
			{
				_property1 = value;
				NotifyPropertyChanged(() => Property1);
			}
		}

		private AnyType _property2;
		public AnyType Property2
		{
			get { return _property2; }
			set
			{
				_property2 = value;
				NotifyPropertyChanged(() => Property2);
			}
		}
	}
}

The first parameter of the used RegisterCallbackDependency overload expects the ObservableCollection that is to be monitored. The second parameter points to the callback.

PDFx will automatically execute the callback whenever any property of a child changes and whenever the collection itself is modified either by adding or by removing a child.

Deferred Callbacks

Please refer to Library Versions to find out whether the platform specific version of PDFx that you are using supports “Deferred Callbacks”

All the RegisterCallbackDependency overloads that are described in the paragraphs above are also available with the method RegisterDeferredCallbackDependency that allows you to register deferred callbacks.
If you register deferred callbacks, the execution of the callback is deferred by a certain amount of time (by default 100ms). Furthermore, if the callback trigger (such as a Property change) fires again before the wait time threshold has passed, the wait timer is reset. This feature gets especially useful when your callback is rather resource expensive and the underlying trigger might fire multiple times in a row.

Let’s look at the following example:

class SimpleCallbackExample : BindableExt
{
	public SimpleCallbackExample()
	{
		RegisterCallbacks();
	}

	private void RegisterCallbacks()
	{
		RegisterDeferredCallbackDependency(this, k => SliderValue, SubmitDataToServer);
	}

	private void SubmitDataToServer()
	{
		Debug.WriteLine("Expensive call to submit data to server...");
	}

	private int _sliderValue;
	public int SliderValue
	{
		get { return _sliderValue; }
		set
		{
			_sliderValue = value;
			NotifyPropertyChanged(() => SliderValue);
		}
	}
}

In the example above, the SliderValue property could be bound to a slider. The requirement is to submit the slider’s new value to a server as soon as the user changes value. However, since a server call is rather expensive, the value should not be submitted while the user is still changing the value but rather as soon as the user finishes the change operation.
Such a requirement can easily be implemented by employing deferred callbacks as shown above.

Extensive Example

Please refer to WPFSample’s CallbacksDemonstrationVM to explore an example that makes heavy usage of the callback features.

2 thoughts on “PDFx – Property Dependency Framework – Part XII, Callbacks

  1. Pingback: PDFx – Property Dependency Framework – Part I, Introduction | //InterKnowlogy/ Blogs

  2. Pingback: PDFx – Property Dependency Framework – Part III, Getting started | //InterKnowlogy/ Blogs

Leave a Reply

Your email address will not be published. Required fields are marked *