Mittwoch, 11. Januar 2012

Simple Drag and Drop Handler using MVVM and Attached Properties

I wanted to implement some Drag & Drop behaviour to my ListBoxes. Because the Drag & Drop is handled through Events I created some Attached Properties that would handle these events and pass the needed information to my ViewModel through binding.

First I created an Interface that I could pass to the Attached Property that contained two Method definitions that would get executed when the DropEvent gets fired.
One method returnes a bool defining if the Drop can be executed.
The second method handles the DropEvent.
    public interface IDragDropHandler
    {
        bool CanDrop(IDataObject dropObject, IEnumerable dropTarget);

        void OnDrop(IDataObject dropObject, IEnumerable dropTarget);
    }

Then I created the DependencyProperty that gets attached. In the PropertyMetadata I created a new object containing the PropertyChangedHandler that gets executed when the dragDropHandlerProperty is changed. Inside the instance I hooked to the DragEvent of the DependencyObject and added a handler that gets executed when the DragEvent gets fired.
    public static class DragDropBehaviour
    {
        #region DragDropHandler

        #region DragDropHandler DependencyProperty

        /// 
        /// attached property that handles drag and drop
        /// 
        public static readonly DependencyProperty DragDropHandlerProperty =
           DependencyProperty.RegisterAttached("DragDropHandler", 
           typeof(IDragDropHandler), 
           typeof(DragDropBehaviour),
           new PropertyMetadata(null, new ExecuteDragDropBehaviour().PropertyChangedHandler));

        public static void SetDragDropHandler(DependencyObject o, object propertyValue)
        {
            o.SetValue(DragDropHandlerProperty, propertyValue);
        }
        public static object GetDragDropHandler(DependencyObject o)
        {
            return o.GetValue(DragDropHandlerProperty);
        }

        #endregion

        internal abstract class DragDropBehaviourBase
        {
            protected DependencyProperty _property;

            /// <summary>
            /// attach the events
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="oldValue"></param>
            /// <param name="newValue"></param>
            protected abstract void AdjustEventHandlers(DependencyObject sender, 
                                                        object oldValue, object newValue);

            /// <summary>
            /// Listens for a change in the DependencyProperty
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            public void PropertyChangedHandler(DependencyObject sender, 
                                               DependencyPropertyChangedEventArgs e)
            {
                if (_property == null)
                {
                    _property = e.Property;
                }

                object oldValue = e.OldValue;
                object newValue = e.NewValue;

                AdjustEventHandlers(sender, oldValue, newValue);
            }
        }

        /// <summary>
        /// an internal class to handle listening for the drop event and executing the dropaction
        /// </summary>
        private class ExecuteDragDropBehaviour : DragDropBehaviourBase
        {
            /// <summary>
            /// attach the events
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="oldValue"></param>
            /// <param name="newValue"></param>
            protected override void AdjustEventHandlers(DependencyObject sender, 
                                                   object oldValue, object newValue)
            {
                var element = sender as UIElement;
                if (element == null) { return; }

                if (oldValue != null)
                {
                    element.RemoveHandler(UIElement.DropEvent, 
                                          new DragEventHandler(ReceiveDrop));
                }

                if (newValue != null)
                {
                    element.AddHandler(UIElement.DropEvent, 
                                       new DragEventHandler(ReceiveDrop));
                }
            }

            /// <summary>
            /// eventhandler that gets executed when the DropEvent fires
            /// </summary>
            private void ReceiveDrop(object sender, DragEventArgs e)
            {
                var dp = sender as DependencyObject;
                if (dp == null)
                    return;

                var action = dp.GetValue(_property) as IDragDropHandler;
                if (action == null)
                    return;

                IEnumerable dropTarget = null;
                if (sender is ItemsControl)
                    dropTarget = (sender as ItemsControl).ItemsSource;

                if (action.CanDrop(e.Data, dropTarget))
                    action.OnDrop(e.Data, dropTarget);
                else
                    e.Handled = true;
            }
        }

        #endregion
    } 

Next I created an Attached DependencyProperty that starts the Draging when the MouseDownEvent gets fired. The Draging only gets initialized when the value that gets passed to the Property is "True".
    public static class DragDropBehaviour
    {
        #region DragDropHandler
        ... 
        #endregion

        #region IsDragSource DependencyProperty

        /// 
        /// attached property that defines if the source is a drag source
        /// 
        public static readonly DependencyProperty IsDragSourceProperty =
           DependencyProperty.RegisterAttached("IsDragSource", 
           typeof(bool?), 
           typeof(DragDropBehaviour),
           new PropertyMetadata(null, new IsDragSourceBehaviour().PropertyChangedHandler));

        public static void SetIsDragSource(DependencyObject o, object propertyValue)
        {
            o.SetValue(IsDragSourceProperty, propertyValue);
        }
        public static object GetIsDragSource(DependencyObject o)
        {
            return o.GetValue(IsDragSourceProperty);
        }

        #endregion

        /// <summary>
        /// Internal class that starts the draging
        /// </summary>
        private class IsDragSourceBehaviour : DragDropBehaviourBase
        {
            /// <summary>
            /// Hattach the events
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="oldValue"></param>
            /// <param name="newValue"></param>
            protected override void AdjustEventHandlers(DependencyObject sender, 
                                                        object oldValue, object newValue)
            {
                var element = sender as UIElement;
                if (element == null) 
                    return;

                if (oldValue != null)
                {
                    element.RemoveHandler(UIElement.MouseMoveEvent, 
                                          new MouseEventHandler(OnMouseMove));
                }

                if (newValue != null && newValue is bool && (bool)newValue)
                {
                    element.AddHandler(UIElement.MouseMoveEvent, 
                                       new MouseEventHandler(OnMouseMove));
                }
            }

            /// <summary>
            /// eventhandler for the MouseMoveEvent
            /// </summary>
            private void OnMouseMove(object sender, MouseEventArgs e)
            {
                if (sender is Selector && e.LeftButton == MouseButtonState.Pressed)
                {
                    var lst = (Selector)sender;
                    var selectedItem = lst.SelectedItem;

                    var dragDropEffect = DragDropEffects.Move;

                    if (dragDropEffect != DragDropEffects.None)
                    {
                        DragDropEffects enmEffect = 
                                   DragDrop.DoDragDrop(sender as DependencyObject, 
                                                       selectedItem, dragDropEffect);
                    }
                }
            }
        }
   }


The implementation of the DragDropHandler can be used in different ways.

Firs I tried an approach similar to the way the RelayCommand is implemented with an Object that implements the IDragDropHandler interface and can be used as a Property that gets attached to the DragDropHandlerPeoperty through data binding.
This object accepts a Action and a Func as parameters whereas the Func is optional.
    public class DragDropHandler : IDragDropHandler
    {
        readonly Action<IDataObject, IEnumerable> _drop;
        readonly Func<IDataObject, IEnumerable, bool> _canDrop;

        public DragDropHandler(Action<IDataObject, IEnumerable> drop)
            : this(drop, null)
        {
        }

        public DragDropHandler(Action<IDataObject, IEnumerable> drop, 
                               Func<IDataObject, IEnumerable, bool> canDrop)
        {
            if (drop == null)
                throw new ArgumentNullException("drop");

            _drop = drop;
            _canDrop = canDrop;
        }

        [DebuggerStepThrough]
        public bool CanDrop(IDataObject dropObject, IEnumerable dropTarget)
        {
            if (_canDrop != null)
                return _canDrop(dropObject, dropTarget);

            return true;
        }

        [DebuggerStepThrough]
        public void OnDrop(IDataObject dropObject, IEnumerable dropTarget)
        {
            _drop(dropObject, dropTarget);
        }
    }

In the ViewModel I created a Property of type DragDropHandler. Here I passed a method that accepts a IDataObject and IEnumerable as parameter for the Action<IDataObject, IEnumerable> and a method that accepts a IDataObject and IEnumerable as parameter for the Func<IDataObject, IEnumerable> (alternatively ony if needed) through the constructor. These methods will handle the DropActions similar to the RelayCommand.
The nice thing about this approach is that the DragDopHandler accepts Lambda Expressions as parameters instead of the methods.
        IDragDropHandler _dragDropAction;
        public IDragDropHandler DragDropAction
        {
            get
            {
                if (_dragDropAction == null)
                    _dragDropAction = new DragDropHandler(OnDrop, CanDrop);
                return _dragDropAction;
            }
        }

        public bool CanDrop(IDataObject dropObject, IEnumerable dropTarget)
        {
            if (!(dropTarget is IList))
                return false;
            return !(dropTarget as IList).Contains(dropObject.GetData(typeof(BaseGroup)));
        }

        public void OnDrop(IDataObject dropObject, IEnumerable dropTarget)
        {
            if (dropTarget is IList)
            {
                if (dropObject.GetDataPresent(typeof(BaseGroup)))
                {
                    (dropTarget as IList).Add(dropObject.GetData(typeof(BaseGroup)));
                }
            }
        }

In the XAML I added the AttachedProperties and made a DataBinding to the DragDropAction Property.
            <ListBox IsSynchronizedWithCurrentItem="True"
                     AllowDrop="True"
                     ItemsSource="{Binding Objects}" 
                     i:DragDropBehaviour.DragDropHandler="{Binding DragDropAction}"
                     i:DragDropBehaviour.IsDragSource="True"/>

            <ListBox Grid.Column="1"
                     IsSynchronizedWithCurrentItem="True"
                     AllowDrop="True"
                     ItemsSource="{Binding Objects2}" 
                     i:DragDropBehaviour.DragDropHandler="{Binding DragDropAction}"
                     i:DragDropBehaviour.IsDragSource="True"/>


Alternatively the ViewModel itself could implement the IDragDropHandler interface. This way the Binding can be made directly in the ViewModel instead of creating a roundtrip over the DragDropHandler object.
    internal class DragDropListBoxViewModel : IDragDropHandler
    {
        #region DragDropAction

        public bool CanDrop(IDataObject dropObject, IEnumerable dropTarget)
        {
            if (!(dropTarget is IList))
                return false;
            return !(dropTarget as IList).Contains(dropObject.GetData(typeof(BaseGroup)));
        }

        public void OnDrop(IDataObject dropObject, IEnumerable dropTarget)
        {
            if (dropTarget is IList)
            {
                if (dropObject.GetDataPresent(typeof(BaseGroup)))
                {
                    (dropTarget as IList).Add(dropObject.GetData(typeof(BaseGroup)));
                }
            }
        }

        #endregion
    }

The DataBinding can be made by directly binding to the ViewModel.
            <ListBox IsSynchronizedWithCurrentItem="True"
                     AllowDrop="True"
                     ItemsSource="{Binding Objects}" 
                     i:DragDropBehaviour.DragDropHandler="{Binding}"
                     i:DragDropBehaviour.IsDragSource="True"/>

            <ListBox Grid.Column="3" 
                     IsSynchronizedWithCurrentItem="True"
                     AllowDrop="True"
                     ItemsSource="{Binding Objects2}" 
                     i:DragDropBehaviour.DragDropHandler="{Binding}"
                     i:DragDropBehaviour.IsDragSource="True"/>

Dienstag, 10. Januar 2012

Syntax Highlighting

I just edited the layout of the Blog and added Syntax Highlighting for the code examples.

I found a description here http://heisencoder.net/2009/01/adding-syntax-highlighting-to-blogger.html which uses the Syntax Highlighter from http://alexgorbatchev.com/SyntaxHighlighter/

Possible suported languages are cpp, c, c++, c#, c-sharp, csharp, css, delphi, pascal, java, js, jscript, javascript, php, py, python, rb, ruby, rails, ror, sql, vb, vb.net, xml, html, xhtml, xslt. Full list

I'm realy surprised at how easy it was to implement this...

Set Properties to ReadOnly at runtime

Lately I needed to set the ReadOnly Attribute of a Property in C# at runtime. I wanted to change the IsReadOnly Property dynamically. The problem was that the IsReadOnly Property of the ReadOnlyAttribute does not contain a setter meaning that it is ReadOnly...

After doing a lot of searching I stumbled on this Post by David Morton. Here he describes how to use Reflection to edit internal fields.

I created a Method that uses Reflection to change the IsReadOnly Property of the ReadOnlyAttribute.
     /// <summary>  
     /// Uses reflection to set the readonly attribute on a property.  
     /// This value is set to the class definition and not to the instance.   
     /// If it is set once than it has to be reset again to get the default.  
     /// </summary>  
     /// <param name="instance">instance containing the property</param>  
     /// <param name="property">the property to set the readonly value</param>  
     /// <param name="value">value to set to the property</param>  
     private static void SetReadOnly(object instance, string property, bool value)  
     {  
       // Create a PropertyDescriptor for the Property by calling the static 
       // GetProperties on TypeDescriptor.  
       PropertyDescriptor descriptor = 
                        TypeDescriptor.GetProperties(instance.GetType())[property];  
       // Fetch the ReadOnlyAttribute from the descriptor.   
       ReadOnlyAttribute attrib = 
                       (ReadOnlyAttribute)descriptor.Attributes[typeof(ReadOnlyAttribute)];  
       // Get the internal isReadOnly field from the ReadOnlyAttribute using reflection.   
       System.Reflection.FieldInfo isReadOnly = 
                       attrib.GetType().GetField("isReadOnly", 
                              System.Reflection.BindingFlags.NonPublic | 
                              System.Reflection.BindingFlags.Instance);  
       // Using Reflection, set the internal isReadOnly field.   
       isReadOnly.SetValue(attrib, value);   
     }  

In this excample I want to change the value of the ReadOnlyAttribute of the TestProperty.
   class PropertyTest  
   {  
     string _testProperty;  
     [ReadOnly(false)]  
     public string TestProperty  
     {  
       get  
       {  
         return _testProperty;  
       }  
       set  
       {  
         _testProperty = value;  
       }  
     }  
   }  
Now I can pass a instance and make any Property I want ReadOnly or Readable again.
     public void Edit()  
     {  
       var instance = new PropertyTest();  
       SetReadOnly(instance, "TestProperty", true);  
     }  
A big drawback about this is that it breaks encapsulation.
Another problem with this approach ist that it changes the Attribute in the Class definition and not only in the instance. If a new object is created the ReadOnlyAttribute of Property has the value that was set previously. In that case the Attribute has to be set every time a new Instance is created.

Montag, 21. November 2011

Binding to the SelectedItems of ListBox using attached properties

In the last post I wrote about how to extend the ListBox to enable a DataBinding to the SelectedItems property.
It is not always possible to extend a control, so I took another look at the post by Marlon Grech where he uses a attached property to do the trick. I merged the two examples and resulted with a attached property that can handle a two way binding.

The only thing with this approach is that the collection, that the SelectedItems bind to, has to be created on initialization of the ViewModel

Here is the code to the attached property:
 public class ListBoxHelper  
 {  
    #region SelectedItems  
   
    ///  
    /// SelectedItems Attached Dependency Property  
    ///  
    public static readonly DependencyProperty SelectedItemsProperty =  
       DependencyProperty.RegisterAttached("SelectedItems", typeof (IList), 
                          typeof (ListBoxHelper),  
                          new FrameworkPropertyMetadata((IList) null,  
                                       new PropertyChangedCallback(OnSelectedItemsChanged)));  
   
    /// <summary>  
    /// Gets the SelectedItems property.  
    /// </summary>  
    /// <param name="d"></param>  
    /// <returns></returns>  
    public static IList GetSelectedItems(DependencyObject d)  
    {  
       return (IList) d.GetValue(SelectedItemsProperty);  
    }  
   
    /// <summary>  
    /// Sets the SelectedItems property.  
    /// </summary>  
    /// <param name="d"></param>  
    /// <param name="value"></param>  
    public static void SetSelectedItems(DependencyObject d, IList value)  
    {  
       d.SetValue(SelectedItemsProperty, value);  
    }  
   
    /// <summary>  
    /// Called when SelectedItems is set  
    /// </summary>  
    /// <param name="d"></param>  
    /// <param name="e"></param>  
    private static void OnSelectedItemsChanged(DependencyObject d, 
                                             DependencyPropertyChangedEventArgs e)  
    {  
       var listBox = (ListBox) d;  
       SetInternalSelectedItemsToPublic(listBox);  
       IList selectedItems = GetSelectedItems(listBox);  
   
       if (selectedItems is ObservableCollection<object>)  
       {  
          // if the list is a observable collection binde to the changed event 
          // to update the internal collection
          (selectedItems as ObservableCollection<object>).CollectionChanged += delegate  
                             {  
                                        SetSelectedDataItemsToInternal(listBox);  
                             };  
       }  
   
       listBox.SelectionChanged += delegate  
                      {  
                               SetInternalSelectedItemsToPublic(listBox);  
                      };  
    }  
   
   
    #region Implementation  
   
    static bool _isChanging;  
   
    private static void SetSelectedDataItemsToInternal(ListBox listBox)  
    {  
       if (_isChanging)  
          return;  
   
       _isChanging = true;  
   
       var selectedDataItems = GetSelectedItems(listBox);  
   
       if (listBox.SelectedItems != null)  
       {  
          listBox.SelectedItems.Clear();  
   
          if (selectedDataItems == null)  
          {  
             _isChanging = false;  
             return;  
          }  
   
          foreach (var item in selectedDataItems)  
             listBox.SelectedItems.Add(item);  
       }  
   
       _isChanging = false;  
    }  
   
    private static void SetInternalSelectedItemsToPublic(ListBox listBox)  
    {  
       if (_isChanging)  
          return;  
   
       _isChanging = true;  
   
       var selectedDataItems = GetSelectedItems(listBox);  
   
       if (selectedDataItems == null)  
       {  
          // the bound data type is not of typ IList / IList<object> / 
          // IEnumerable / Ienumerable<object>  
          // in this case the bound property has to be initialized first by the caller  
          _isChanging = false;  
          return;  
       }  
   
       selectedDataItems.Clear();  
   
       if (listBox.SelectedItems != null)  
       {  
          foreach (var item in listBox.SelectedItems)  
             selectedDataItems.Add(item);  
       }  
   
       _isChanging = false;  
    }  
         
    #endregion  
   
    #endregion  
 }  

And here is the XAML with the binding:
<ListBox ItemsSource="{Binding ListItems}" 
   c:ListBoxHelper.SelectedItems="{Binding SlectedListItems}" 
   SelectionMode="Multiple"/>

Sonntag, 20. November 2011

Binding to the SelectedItems of ListBox by extending the ListBox

A short while ago I needed to get access to the SelectedItems with DataBinding in my MVVM implementation. The SelectedItems property in the ListBox is read only and so there is no way to have a Binding to it. While searching for a solution I stumbled upon this post by Marlon Grech.
He explains how to create a bindable property for the SelectedItems using attached properties.

The problem with his example is that the binding only works one way when the user selects items in the ListBox. I needed to have a way where I could select items from the ViewModel as well.
The other thing was that I already had an extension of the ListBox.

So instead of creating an attached property I simply took the example and extended the ListBox with the SelectedDataItems property.

Here is the code to the extended ListBox
 public class ListBoxExt : ListBox  
 {  
    static ListBoxExt()  
    {  
       DefaultStyleKeyProperty.OverrideMetadata(typeof (ListBoxExt), 
           new FrameworkPropertyMetadata(typeof (ListBoxExt)));  
    }  
   
    protected override void OnSelectionChanged(SelectionChangedEventArgs e)  
    {  
       SetInternalSelectedItemsToPublic();  
   
       base.OnSelectionChanged(e);  
    }  
   
    #region SelectedDataItems  
   
    public static readonly DependencyProperty SelectedDataItemsProperty = 
       DependencyProperty.Register(  
       "SelectedDataItems",   
       typeof(IList),   
       typeof(ListBoxExt),  
       new FrameworkPropertyMetadata(  
          (IList) null,   
          FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,  
          new PropertyChangedCallback(OnSelectedDataItemsPropertyChangedCallback)));  
   
    /// <summary>  
    /// Gets an IList containing all selected items  
    /// </summary>  
    public IList SelectedDataItems  
    {  
       get  
       {  
          return (IList) GetValue(SelectedDataItemsProperty);  
       }  
       set  
       {  
          SetValue(SelectedDataItemsProperty, value);  
       }  
    }  
   
   
    /// <summary>  
    /// Handles changes to the SelectedDataItems property.  
    /// </summary>  
    private static void OnSelectedDataItemsPropertyChangedCallback(
            DependencyObject d, 
            DependencyPropertyChangedEventArgs e)  
    {  
       var listBox = d as ListBoxExt;  
       if (listBox == null)  
          return;  
   
       listBox.SetSelectedDataItemsCollection(e.NewValue as IList);  
    }  
   
    private void SetSelectedDataItemsCollection(IList selection)  
    {  
       if (_isChanging)  
          return;  
   
       SetSelectedDataItemsToInternal(selection);  
   
       if (selection != null && selection is ObservableCollection<object>)  
       {  
          // add a eventlistner to keep a two way binding  
          (selection as ObservableCollection<object>).CollectionChanged += 
                new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
                                                BoundSelectedDataItemsCollectionChanged);  
       }  
    }  
   
    #region Implementation  
   
    bool _isChanging;  
   
    private void SetSelectedDataItemsToInternal(IList selectedDataItems)  
    {  
       if (_isChanging)  
          return;  
   
       _isChanging = true;  
   
       if (SelectedItems != null)  
       {  
          SelectedItems.Clear();  
   
          if (selectedDataItems == null)  
          {  
             _isChanging = false;  
             return;  
          }  
   
          foreach (var item in selectedDataItems)  
             SelectedItems.Add(item);  
       }  
   
       _isChanging = false;  
    }  
   
    private void SetInternalSelectedItemsToPublic()  
    {  
       if (_isChanging)  
          return;  
   
       _isChanging = true;  
   
       if (SelectedDataItems == null)  
       {  
          var selection = new ObservableCollection<object>();  
          selection.CollectionChanged += 
               new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
                                              BoundSelectedDataItemsCollectionChanged);  
          SelectedDataItems = selection;  
       }  
   
       if (SelectedDataItems == null)  
       {  
          // the bound data type is not of typ IList / IList<object> / 
          // IEnumerable / Ienumerable<object>  
          // in this case the bound property has to be initialized first by the caller  
          _isChanging = false;  
          return;  
       }  
   
       SelectedDataItems.Clear();  
   
       if (base.SelectedItems != null)  
       {  
          foreach (var item in base.SelectedItems)  
             SelectedDataItems.Add(item);  
       }  
   
       _isChanging = false;  
    }  
   
    /// <summary>  
    /// gets executed when the bound collection is of type ObservableCollection 
    /// and the collection gets changed  
    /// </summary>  
    /// <param name="sender"></param>  
    /// <param name="e"></param>  
    void BoundSelectedDataItemsCollectionChanged(object sender, 
                        System.Collections.Specialized.NotifyCollectionChangedEventArgs e)  
    {  
       if (_isChanging)  
          return;  
   
       if (SelectedDataItems == null)  
          return;  
       SetSelectedDataItemsToInternal(SelectedDataItems);  
    }  
   
    #endregion  
   
    #endregion  
 }  


To be able to create a two way binding, the property for the SelectedDataItems has to be IList, IList<object>, IEnumerable or IEnumerable<object>. If a concrete type is used in the generic enumerations the binding will only work one way to the list.
Here is the XAML with the bindings:
<c:ListBoxExt ItemsSource="{Binding ListItems}" 
    SelectedDataItems="{Binding SlectedListItems}" 
    SelectionMode="Multiple"/>

Freitag, 18. November 2011

My first blog was refound

Today I just found my first blog at http://blog.uniface.ch/blogs/blog1.php

I was so amazed an happy about the refound blog, that I copied most posts to blogspot where I have all other blogs.

voodoo programming: n.


[from George Bush Sr.'s “voodoo economics”]

Link: Link: http://catb.org/~esr/jargon/html/V/voodoo-programming.html

1. The use by guess or cookbook of an obscure or hairy system, feature, or algorithm that one does not truly understand. The implication is that the technique may not work, and if it doesn't, one will never know why. Almost synonymous with black magic, except that black magic typically isn't documented and nobody understands it. Compare magic, deep magic, heavy wizardry, rain dance, cargo cult programming, wave a dead chicken, SCSI voodoo.

2. Things programmers do that they know shouldn't work but they try anyway, and which sometimes actually work, such as recompiling everything.