I was just looking into persisting the state of a Silverlight application on exit. The scenario is that the application has a complex layout that we normally persist to a database via a RESTful service call. The problem is what if the user forgets to explicitly save the layout before shutting the browser down. So I looked into calling the service in the Application_Exit method in the App class. Sadly this does not work – it appears that the request is queued on the Dispatcher thread after the shutdown process and so never executes. Guess I’ll have to save the state to isolated storage and put some hacky code in to determine if there is a saved state in ISO before loading the layout from the DB. Bit of a bummer if the user uses more than one machine though…

Advertisements

Further to my previous post, Erik Meijer commented that an overload to the FromEvent method is available to convert from generic event handlers to the specific ones used by much of the BCL. So the code from the previous post would look something like this now:

        #region Ctor
        public MainPage()
        {
            InitializeComponent();

            var mouseEventSource = Observable.FromEvent<MouseEventHandler, MouseEventArgs>(
                eh => new MouseEventHandler(eh),    // Conversion
                eh => this.MouseMove += eh,         // Subscribe
                eh => this.MouseMove -= eh);        // Unsubscribe

            _mouseMoveSubscription = mouseEventSource.Subscribe(OnMouseMove);
        }
        #endregion

        private void OnMouseMove(Event<MouseEventArgs> e)
        {
            var position = e.EventArgs.GetPosition(this);

            Debug.WriteLine("Mouse moved.  Position: {0}.", position);
        }

This solves one of my major gripes. Exciting times!

I just wrote a base class that I’m going to derive all of my notifying Domain Models from. It solves the horror of passing around the property name of a changing property in the INotifyPropertyChanged implementation. This has the enormous benefit of being able to check your Property Names at compile-time and also allows you to keep everything in sync when using tools like Resharper (if you don’t use Resharper – then you really should!) to rename things. It uses lambda expressions to specify the Property that’s changing.

    public abstract class ModelBase<T> : INotifyPropertyChanged where T : ModelBase<T>
    {
        #region Events
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Public Methods
        public static string GetPropertyName<R>(Expression<Func<T, R>> expression)
        {
            var memberExpression = expression.Body as MemberExpression;
            if (memberExpression == null)
            {
                throw new ArgumentException("'expression' should be a member expression");
            }
            var propertyName = memberExpression.Member.Name;
            return propertyName;
        }
        #endregion

        #region Protected Methods
        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        
        protected void RaisePropertyChanged<R>(Expression<Func<T, R>> expression)
        {
            var propertyName = GetPropertyName(expression);
            this.OnPropertyChanged(propertyName);
        }
        #endregion
}

Following on from my woes about ObservableCollection<T>, and my annoyance at having to maintain a parallel collection to ensure all INotifyPropertyChanged and INotifyCollectionChanged subscriptions are removed, I decided to look down the avenue of Weak event subscriptions.  To that end I can up with the classes WeakPropertyChangedListner and WeakCollectionChangedListener.  The code for these is as follows:

 

    public class WeakPropertyChangedListener
    {
        #region Private Fields
        private INotifyPropertyChanged _source;
        private WeakReference _listener;
        #endregion

        #region Ctor
        public WeakPropertyChangedListener(INotifyPropertyChanged source, PropertyChangedEventHandler listener)
        {
            this._source = source;
            this._source.PropertyChanged += this.OnPropertyChanged;
            this._listener = new WeakReference(listener);
        }
        #endregion

        #region Public Methods
        public static WeakPropertyChangedListener Create(INotifyPropertyChanged source, PropertyChangedEventHandler listener)
        {
            return new WeakPropertyChangedListener(source, listener);
        }

        public void Disconnect()
        {
            if (_source != null)
            {
                _source.PropertyChanged -= this.OnPropertyChanged;
                _source = null;
                _listener = null;
            }
        }
        #endregion

        #region Private Methods
        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (_listener != null)
            {
                var handler = _listener.Target as PropertyChangedEventHandler;
                if (handler != null)
                {
                    handler(sender, e);
                }
                else
                {
                    this.Disconnect();
                }
            }
        }
        #endregion
    }

and:

    public class WeakCollectionChangedListener
    {
        #region Private Fields
        private INotifyCollectionChanged _source;
        private WeakReference _handler;
        #endregion

        #region Ctor
        public WeakCollectionChangedListener(INotifyCollectionChanged source, NotifyCollectionChangedEventHandler handler)
        {
            this._source = source;
            this._source.CollectionChanged += this.OnCollectionChanged;
            this._handler = new WeakReference(handler);
        }
        #endregion

        #region Public Methods
        public static WeakCollectionChangedListener Create(INotifyCollectionChanged source, NotifyCollectionChangedEventHandler handler)
        {
            return new WeakCollectionChangedListener(source, handler);
        }

        public void Disconnect()
        {
            if (_source != null)
            {
                _source.CollectionChanged -= this.OnCollectionChanged;
                _source = null;
                _handler = null;
            }
        }
        #endregion

        #region Private Methods
        private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (_handler != null)
            {
                var handler = _handler.Target as NotifyCollectionChangedEventHandler;
                if (handler != null)
                {
                    handler(sender, e);
                }
                else
                {
                    this.Disconnect();
                }
            }
        }
        #endregion
    }

 Then you simple subscribe like this:

WeakPropertyChangedListener.Create(person, OnPersonPropertyChanged);

WeakCollectionChangedListener.Create(person.Children, OnPersonCollectionChanged);

Safe in the knowledge that your observing the notifications will not cause your domain objects to hang around.

<rant>

Thought I’d share with you some of my gripes with the ObservableCollection<T> class that forms the basis of just about all data-bindable domain models in WPF and Silverlight.

Firstly why does it not support an AddRange(IEnumerable<T> items) method like other collections?

The EventHandler signature looks like:

private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)

and NotifyCollectionChangedEventArgs has a property NewItems of type IList.  This implies that you should be able to add multiple items, but the API doesn’t support it.  Infuriating!

More importantly, when you Clear an ObservableCollection you get called on the CollectionChanged event with NotifyCollectionChangedAction.Reset.  However when you look at e.OldItems you’d expect to find the list of items that were cleared, right?  Sadly not.  I often find that I hook into various event handlers in the OnCollectionChanged method.  In order to clear these subscriptions on reset it means that I have to hold a parallel collection of the items in the ObservableCollection.  Not good.

Also, why is there not an ObservableDictionary<TKey, TValue>?  Various people have attempted to implement this, but it should really be part of the framework and understood by data binding.

I’m hopeful that these issues will go away with the BCL changes for the very groovy System.Reactive framework.

</rant>

So I’m working on a ChartingLibrary at the moment that has to compile for both Silverlight and WPF.  I can across a particularly nasty problem with axis tick labels.  So the requirement is that the tick labels can be rotated.  In WPF this is a doddle – you just use a RotateTransform and set it as the LayoutTransform in a TextBlock.  In Silverlight, however, you only have the RenderTransform to play with.  This is the solution I came up with:

    [TemplatePart(Name = PART_TEXT_BLOCK, Type = typeof(TextBlock))]
    public class TickLabel : Control
    {
        #region Private Fields
        private TextBlock _textBlock;
        private RotateTransform _rotateTransform;
#if SILVERLIGHT
        private TranslateTransform _translateTransform;
#endif
        #endregion

        #region Ctor
        public TickLabel()
        {
            base.DefaultStyleKey = typeof(TickLabel);
        }
        #endregion

        #region Dependency Properties

        #region Text
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
            "Text", typeof(string), typeof(TickLabel), null);

        public string Text
        {
            get { return base.GetValue(TextProperty) as string; }
            set { base.SetValue(TextProperty, value); }
        }
        #endregion

        #region Angle
        public static readonly DependencyProperty AngleProperty = DependencyProperty.Register(
            "Angle", typeof(double), typeof(TickLabel),
            new PropertyMetadata(0.0, OnAngleChanged));

        public double Angle
        {
            get { return (double)base.GetValue(AngleProperty); }
            set { base.SetValue(AngleProperty, value); }
        }

        private static void OnAngleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var instance = (TickLabel) d;
            var angle = (double) e.NewValue;
            instance.OnAngleChanged(angle);
        }

        private void OnAngleChanged(double angle)
        {
            _rotateTransform.Angle = angle;
#if SILVERLIGHT
            // Have to manually translate for Silverlight
            var width = _textBlock.ActualWidth;
            var height = _textBlock.ActualHeight;
            var sourceCenter = new Point(width / 2, height / 2);
            var destinationCenter = _rotateTransform.Transform(sourceCenter);
            var sourceRect = new Rect { Width = width, Height = height };
            var destinationRect = _rotateTransform.TransformBounds(sourceRect);
            _translateTransform.X = destinationRect.Width/2 - destinationCenter.X;
            _translateTransform.Y = destinationRect.Height/2 - destinationCenter.Y;
            base.InvalidateMeasure();
#endif
        }
        #endregion

        #endregion

        #region FrameworkElement Members
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _textBlock = base.GetTemplateChild(PART_TEXT_BLOCK) as TextBlock;
            if (_textBlock == null)
            {
                throw new Exception(string.Format("TemplatePart: {0} missing.", PART_TEXT_BLOCK));
            }

            _rotateTransform = new RotateTransform { Angle = this.Angle };
#if SILVERLIGHT
            // No LayoutTransform in Silverlight so we have to use RenderTransform
            var transformGroup = new TransformGroup();
            transformGroup.Children.Add(_rotateTransform);
            _translateTransform = new TranslateTransform();
            transformGroup.Children.Add(_translateTransform);
            _textBlock.RenderTransform = transformGroup;
#else
            _textBlock.LayoutTransform = _rotateTransform;
#endif
        }

#if SILVERLIGHT
        protected override Size MeasureOverride(Size constraint)
        {
            var width = _textBlock.ActualWidth;
            var height = _textBlock.ActualHeight;
            var sourceRect = new Rect { Width = width, Height = height };
            var destinationRect = _rotateTransform.TransformBounds(sourceRect);
            return new Size(destinationRect.Width, destinationRect.Height);
        }
#endif
        #endregion

        #region Constants
        private const string PART_TEXT_BLOCK = "PART_TextBlock";
        #endregion
    }

And the default control template looks like this:

<Style TargetType="local:TickLabel>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TickLabel">
  <Canvas>
    <TextBlock Name="PART_TextBlock" Text="{TemplateBinding Text}" />
  </Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Note that you have to include the TextBlock in a Canvas otherwise it will clip.

Works like a treat!

I’ve just been checking out some of the asynchronous programming stuff that will be shipped with .Net 4.0.  I managed to obtain a preview copy of the System.Reactive.dll by downloading the lates Silverlight Toolkit source.  This seems like very exciting stuff indeed.  Basically turning the whole .Net event infrastructure on its head.  This is based on the observation that we can think of the observer pattern as being the dual of the enumerable pattern.  The power of this is that we can use Linq to enumerate over events just as we do over objects in an enumerable.  Or, in other words, Linq of pushed rather than pulled data.

Check this out for more information:

http://themechanicalbride.blogspot.com/2009/07/introducing-rx-linq-to-events.html

My one criticism of this so far is:

Say you want to create an observer on the MouseMove event on a control.  You’d do something like this:

var mouseEventSource = Observable.FromEvent<MouseEventArgs>(this, “MouseMove”);
var mouseMoveSubscription = mouseEventSource.Subscribe(mea => Debug.WriteLine(“Mouse Moved”));

And this works perfectly.  The only problem… that pesky “MouseMove” parameter when converting the event into and IObservable.  I noticed that there’s an overload of the same method that takes Actions to add and remove the EventHandler.  This would look something like this:

var mouseEventSource = Observable.FromEvent<MouseEventArgs>(eh=>this.MouseMove += eh, eh=>this.MouseMove -= eh);

This looks much nicer as I’m not passing in that ‘orrible string and therefore have compile-time checking of the event.  Only problem with this is that it requires the MouseMove event handler to be a generic EventHandler<MouseEventArgs>.  Sadly, the MouseEventHandler is a delegate in its own right rather than deriving from the generic EventHandler.

This leads me to the following conclusion.  This looks like really interesting technology, but it’ll require a massive overhauling of the .Net base class libraries to be truely useful.