Category Archives: Success

Mixing Style Setters and Bindings in WinRT

To say the truth, this caught me by surprise. When I stumbled upon the fact that in WinRT you cannot just assign a Binding to a Property Setter. Surprisingly enough, this is not supported.

So no <Setter Property=”Blah” Value=”{Binding Path=Bleh}” />

But some smart dudes have coined a method to make it work. At least, the did in Silverlight 4 (the version 5 already supports Bindings in those Setters). Later, the trick came to WinRT… they say WinRT was forked from Silverlight 4. Now a lot of things make sense :S

What’s the trick? Using Attached Properties.

It uses a helper class to wire up everything. It may seem ugly at first sight, but while WinRT is so restrictive (and much more for a WPF developer like me), it’s the only way I can think of that is more of less XAML oriented.

This is the helper class (WinRT, of course):

// Copyright (C) Microsoft Corporation. All Rights Reserved.
// This code released under the terms of the Microsoft Public License
// (Ms-PL, http://opensource.org/licenses/ms-pl.html).

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Markup;

namespace Delay
{
    /// <summary>
    /// Class that implements a workaround for a Silverlight XAML parser
    /// limitation that prevents the following syntax from working:
    ///    &lt;Setter Property="IsSelected" Value="{Binding IsSelected}"/&gt;.
    /// </summary>
    [ContentProperty(Name = "Values")]
    public class SetterValueBindingHelper
    {
        /// <summary>
        /// Gets or sets an optional type parameter used to specify the type
        /// of an attached DependencyProperty as an assembly-qualified name,
        /// full name, or short name.
        /// </summary>
        [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods",
            Justification = "Unambiguous in XAML.")]
        public string Type { get; set; }

        /// <summary>
        /// Gets or sets a property name for the normal/attached
        /// DependencyProperty on which to set the Binding.
        /// </summary>
        public string Property { get; set; }

        /// <summary>
        /// Gets or sets a Binding to set on the specified property.
        /// </summary>
        public Binding Binding { get; set; }

        /// <summary>
        /// Gets a Collection of SetterValueBindingHelper instances to apply
        /// to the target element.
        /// </summary>
        /// <remarks>
        /// Used when multiple Bindings need to be applied to the same element.
        /// </remarks>
        public Collection<SetterValueBindingHelper> Values
        {
            get
            {
                // Defer creating collection until needed
                if (null == _values)
                {
                    _values = new Collection<SetterValueBindingHelper>();
                }
                return _values;
            }
        }

        /// <summary>
        /// Backing store for the Values property.
        /// </summary>
        private Collection<SetterValueBindingHelper> _values;

        /// <summary>
        /// Gets the value of the PropertyBinding attached DependencyProperty.
        /// </summary>
        /// <param name="element">Element for which to get the property.</param>
        /// <returns>Value of PropertyBinding attached DependencyProperty.</returns>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
            Justification = "SetBinding is only available on FrameworkElement.")]
        public static SetterValueBindingHelper GetPropertyBinding(FrameworkElement element)
        {
            if (null == element)
            {
                throw new ArgumentNullException("element");
            }
            return (SetterValueBindingHelper)element.GetValue(PropertyBindingProperty);
        }

        /// <summary>
        /// Sets the value of the PropertyBinding attached DependencyProperty.
        /// </summary>
        /// <param name="element">Element on which to set the property.</param>
        /// <param name="value">Value forPropertyBinding attached DependencyProperty.</param>
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
            Justification = "SetBinding is only available on FrameworkElement.")]
        public static void SetPropertyBinding(FrameworkElement element, SetterValueBindingHelper value)
        {
            if (null == element)
            {
                throw new ArgumentNullException("element");
            }
            element.SetValue(PropertyBindingProperty, value);
        }

        /// <summary>
        /// PropertyBinding attached DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty PropertyBindingProperty =
            DependencyProperty.RegisterAttached(
                "PropertyBinding",
                typeof(SetterValueBindingHelper),
                typeof(SetterValueBindingHelper),
                new PropertyMetadata(null, OnPropertyBindingPropertyChanged));

        /// <summary>
        /// Change handler for the PropertyBinding attached DependencyProperty.
        /// </summary>
        /// <param name="d">Object on which the property was changed.</param>
        /// <param name="e">Property change arguments.</param>
        private static void OnPropertyBindingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Get/validate parameters
            var element = (FrameworkElement)d;
            var item = (SetterValueBindingHelper)e.NewValue;

            if (null != item)
            {
                // Item value present
                if ((null == item.Values) || (0 == item.Values.Count))
                {
                    // No children; apply the relevant binding
                    ApplyBinding(element, item);
                }
                else
                {
                    // Apply the bindings of each child
                    foreach (var child in item.Values)
                    {
                        if ((null != item.Property) || (null != item.Binding))
                        {
                            throw new ArgumentException(
                                "A SetterValueBindingHelper with Values may not have its Property or Binding set.");
                        }
                        if (0 != child.Values.Count)
                        {
                            throw new ArgumentException(
                                "Values of a SetterValueBindingHelper may not have Values themselves.");
                        }
                        ApplyBinding(element, child);
                    }
                }
            }
        }

        /// <summary>
        /// Applies the Binding represented by the SetterValueBindingHelper.
        /// </summary>
        /// <param name="element">Element to apply the Binding to.</param>
        /// <param name="item">SetterValueBindingHelper representing the Binding.</param>
        private static void ApplyBinding(FrameworkElement element, SetterValueBindingHelper item)
        {
            if ((null == item.Property) || (null == item.Binding))
            {
                throw new ArgumentException(
                    "SetterValueBindingHelper's Property and Binding must both be set to non-null values.");
            }

            // Get the type on which to set the Binding
            TypeInfo type = null;
            if (null == item.Type)
            {
                // No type specified; setting for the specified element
                type = element.GetType().GetTypeInfo();
            }
            else
            {
                // Try to get the type from the type system
                type = System.Type.GetType(item.Type).GetTypeInfo();
                if (null == type)
                {
                    // Search for the type in the list of assemblies
                    foreach (var assembly in AssembliesToSearch)
                    {
                        // Match on short or full name
                        type = assembly.DefinedTypes
                            .Where(t => (t.FullName == item.Type) || (t.Name == item.Type))
                            .FirstOrDefault();
                        if (null != type)
                        {
                            // Found; done searching
                            break;
                        }
                    }
                    if (null == type)
                    {
                        // Unable to find the requested type anywhere
                        throw new ArgumentException(
                            string.Format(
                                CultureInfo.CurrentCulture,
                                "Unable to access type "{0}". Try using an assembly qualified type name.",
                                item.Type));
                    }
                }
            }

            // Get the DependencyProperty for which to set the Binding
            DependencyProperty property = null;

            var allProperties = type.GetAllProperties();
            var field = allProperties.FirstOrDefault(info => info.Name.Equals(item.Property + "Property"));

            if (null != field)
            {
                property = field.GetValue(null) as DependencyProperty;
            }
            if (null == property)
            {
                // Unable to find the requsted property
                throw new ArgumentException(
                    string.Format(
                        CultureInfo.CurrentCulture,
                        "Unable to access DependencyProperty "{0}" on type "{1}".",
                        item.Property,
                        type.Name));
            }

            // Set the specified Binding on the specified property
            element.SetBinding(property, item.Binding);
        }

        /// <summary>
        /// Gets a sequence of assemblies to search for the provided type name.
        /// </summary>
        private static IEnumerable<Assembly> AssembliesToSearch
        {
            get
            {
                // Start with the System.Windows assembly (home of all core controls)
                yield return typeof(Control).GetTypeInfo().Assembly;

#if SILVERLIGHT && !WINDOWS_PHONE
                // Fall back by trying each of the assemblies in the Deployment's Parts list
                foreach (var part in Deployment.Current.Parts)
                {
                    var streamResourceInfo = Application.GetResourceStream(
                        new Uri(part.Source, UriKind.Relative));
                    using (var stream = streamResourceInfo.Stream)
                    {
                        yield return part.Load(stream);
                    }
                }
#endif
            }
        }

    }

    public static class ReflectionExtensions
    {
        public static IEnumerable<PropertyInfo> GetAllProperties(this TypeInfo type)
        {
            var list = type.DeclaredProperties.ToList();

            var subtype = type.BaseType;
            if (subtype != null)
                list.AddRange(subtype.GetTypeInfo().GetAllProperties());

            return list.ToArray();
        }
    }
}

And the other important thing is to know how to use it inside XAML:

 <Button
            Grid.Column="1"
            Grid.ColumnSpan="2"
            DataContext="Coco">
            <Button.Style>
                <Style TargetType="Button">
                    <!-- Equivalent WPF syntax:
                    <Setter Property="Content" Value="{Binding}"/> -->
                    <Setter Property="delay:SetterValueBindingHelper.PropertyBinding">
                        <Setter.Value>
                            <delay:SetterValueBindingHelper
                                Property="Content"
                                Binding="{Binding}"/>
                        </Setter.Value>
                    </Setter>
                </Style>
            </Button.Style>
        </Button>

Another example from my Project VisualDesigner:

   <designSurface:DesignSurface Background="PowderBlue"
                                     ItemTemplateSelector="{StaticResource TypedTemplateSelector}"
                                     ItemsSource="{Binding  Items}" Grid.Row="1">
            <!--<designSurface:DesignSurface.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </designSurface:DesignSurface.ItemsPanel>-->
            <designSurface:DesignSurface.ItemContainerStyle>
                <Style TargetType="winRt:CanvasItemControl">
                    <Setter Property="delay:SetterValueBindingHelper.PropertyBinding">
                        <Setter.Value>
                            <delay:SetterValueBindingHelper>
                                <delay:SetterValueBindingHelper
                                    Type="Canvas"
                                    Property="Left"
                                    Binding="{Binding Left, Mode=TwoWay}" />
                                <delay:SetterValueBindingHelper
                                    Type="Canvas"
                                    Property="Top"
                                    Binding="{Binding Top, Mode=TwoWay}" />
                                <delay:SetterValueBindingHelper
                                    Property="Width"
                                    Binding="{Binding Width, Mode=TwoWay}" />
                                <delay:SetterValueBindingHelper
                                    Property="Height"
                                    Binding="{Binding Height, Mode=TwoWay}" />
                            </delay:SetterValueBindingHelper>
                        </Setter.Value>
                    </Setter>
                </Style>
            </designSurface:DesignSurface.ItemContainerStyle>
        </designSurface:DesignSurface>

Finally, I would like to give thanks to Mark Smith (@marksm in Twitter), for pointing me out some interesting posts to the solution. He has supported me from the beginning. These were the links that he gave me 🙂

Thanks to them, too!

Hard things with Expressions (property selector)

It has been hard at least for me.

This method applies the value to the specified property of the target items:

 private static void SetPropertyToAll<T, TValue>(IEnumerable<T> targetItems, Expression<Func<T, TValue>> propertyExpression, TValue value)
        {
            if (propertyExpression.Body is MemberExpression)
            {
                var memberExpression = (MemberExpression)propertyExpression.Body;

                var propInfo = (PropertyInfo)memberExpression.Member;

                foreach (var item in targetItems)
                {
                    propInfo.SetValue(item, value, null);
                }
            }
            else
            {
                throw new InvalidOperationException("See link to the Stack Overflow question below!!");
            }
        }

And this is the original question in which I found all the information I needed in order for this to work like fine cinnamon 😉

http://stackoverflow.com/a/2789606/1025407

Good luck, Expression Boy!

RelativeCanvas Super Charged Edition :)

Pues gracias a Sergio, ese Rick Astley de .NET que apuesta por la programación de calidad, he retocado el RelativeFuckerCanvas, ese canvas que hará que tus controlpillos ocupen su lugar por muchos meneos que le des a las ventanas.

Ahora con +LECHE y –CACAO!

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace WPFBasics.Design.Panels.RelativeCanvas {
    public class RelativeCanvas : Canvas {

        private bool firstLoad = true;

        public RelativeCanvas() {
            Loaded += OnLoaded;
            Unloaded += OnUnloaded;
        }

        private void OnUnloaded(object sender, RoutedEventArgs routedEventArgs) {
            SizeChanged -= OnSizeChanged;
        }

        void OnLoaded(object sender, RoutedEventArgs e) {

            if (firstLoad) {
                foreach (UIElement child in Children) {
                    UpdateHorizontalProportions(child);
                    UpdateVerticalProportions(child);
                }
                firstLoad = false;
            } else {
                var newSizeInfo = new SizeInfo(new Size(ActualWidth, ActualHeight)) { WidthChanged = true, HeightChanged = true };
                UpdateChildLocationsAfterSizeChange(newSizeInfo);
            }

            SizeChanged += OnSizeChanged;
        }

        private void UpdateChildLocationsAfterSizeChange(SizeInfo newSizeInfo) {

            foreach (UIElement uiElement in Children) {

                if (newSizeInfo.WidthChanged) {
                    var relativeX = (double)uiElement.GetValue(HorizontalProportionProperty);

                    if (!double.IsInfinity(relativeX)) {
                        var newX = relativeX * newSizeInfo.Size.Width;
                        SetLeftOfRelativePoint(uiElement, newX);
                    }
                }

                if (newSizeInfo.HeightChanged) {
                    var relativeY = (double)uiElement.GetValue(VerticalProportionProperty);

                    if (!double.IsInfinity(relativeY)) {
                        var newY = relativeY * newSizeInfo.Size.Height;
                        SetTopOfRelativePoint(uiElement, newY);
                    }
                }
            }

        }

        private void OnSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs) {

            UpdateChildLocationsAfterSizeChange(new SizeInfo(sizeChangedEventArgs.NewSize) {
                WidthChanged = sizeChangedEventArgs.WidthChanged,
                HeightChanged = sizeChangedEventArgs.HeightChanged,
            });

        }

        protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved) {

            if (visualAdded != null) {
                if (IsLoaded) {
                    UpdateHorizontalProportions(visualAdded);
                    UpdateVerticalProportions(visualAdded);
                }
                AttachToPositionChanged(visualAdded);
            }
            if (visualRemoved != null) {
                DettachToPositionChanged(visualRemoved);
            }

            base.OnVisualChildrenChanged(visualAdded, visualRemoved);
        }

        private void AttachToPositionChanged(DependencyObject visualAdded) {
            var leftDescriptor = DependencyPropertyDescriptor.FromProperty(LeftProperty, typeof(Canvas));
            leftDescriptor.AddValueChanged(visualAdded, OnChildrenLeftChanged);

            var topDescriptor = DependencyPropertyDescriptor.FromProperty(TopProperty, typeof(Canvas));
            topDescriptor.AddValueChanged(visualAdded, OnChildrenTopChanged);
        }

        private void DettachToPositionChanged(DependencyObject visualAdded) {
            var leftDescriptor = DependencyPropertyDescriptor.FromProperty(LeftProperty, typeof(Canvas));
            leftDescriptor.RemoveValueChanged(visualAdded, OnChildrenLeftChanged);

            var topDescriptor = DependencyPropertyDescriptor.FromProperty(TopProperty, typeof(Canvas));
            topDescriptor.RemoveValueChanged(visualAdded, OnChildrenTopChanged);
        }

        private void UpdateHorizontalProportions(DependencyObject dependencyObject) {

            var currentX = GetLeftOfRelativePoint((UIElement)dependencyObject);

            var relativeX = currentX / ActualWidth;
            SetHorizontalProportion(dependencyObject, relativeX);
        }

        private void UpdateVerticalProportions(DependencyObject dependencyObject) {
            var currentY = GetTopOfRelativePoint((UIElement)dependencyObject);

            var relativeY = currentY / ActualHeight;

            if (!double.IsNaN(relativeY))
                SetVerticalProportion(dependencyObject, relativeY);
        }

        private void OnChildrenLeftChanged(object sender, EventArgs eventArgs) {
            var child = (DependencyObject)sender;
            UpdateHorizontalProportions(child);
        }

        private void OnChildrenTopChanged(object sender, EventArgs e) {
            var child = (DependencyObject)sender;
            UpdateVerticalProportions(child);
        }

        private static double GetLeftOfRelativePoint(UIElement uiElement) {

            var renderOrigin = (Point)uiElement.GetValue(RelativeOriginProperty);

            return GetLeft(uiElement) + renderOrigin.X * uiElement.RenderSize.Width;
        }

        private static double GetTopOfRelativePoint(UIElement uiElement) {

            var renderOrigin = (Point)uiElement.GetValue(RelativeOriginProperty);

            return GetTop(uiElement) + renderOrigin.Y * uiElement.RenderSize.Height;
        }

        private static void SetLeftOfRelativePoint(UIElement uiElement, double left) {

            var renderOrigin = (Point)uiElement.GetValue(RelativeOriginProperty);

            SetLeft(uiElement, left - renderOrigin.X * uiElement.RenderSize.Width);
        }

        private static void SetTopOfRelativePoint(UIElement uiElement, double top) {

            var renderOrigin = (Point)uiElement.GetValue(RelativeOriginProperty);

            SetTop(uiElement, top - renderOrigin.Y * uiElement.RenderSize.Height);
        }

        #region HorizontalProportion

        public static readonly DependencyProperty HorizontalProportionProperty =
            DependencyProperty.RegisterAttached("HorizontalProportion", typeof(double), typeof(RelativeCanvas),
                new FrameworkPropertyMetadata(double.NaN,
                    OnHorizontalProportionChanged));

        public static double GetHorizontalProportion(DependencyObject d) {
            return (double)d.GetValue(HorizontalProportionProperty);
        }

        private static void SetHorizontalProportion(DependencyObject d, double value) {
            d.SetValue(HorizontalProportionProperty, value);
        }

        private static void OnHorizontalProportionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
            var oldHorizontalProportion = (double)e.OldValue;
            var newHorizontalProportion = (double)d.GetValue(HorizontalProportionProperty);
        }

        #endregion

        #region VerticalProportion

        public static readonly DependencyProperty VerticalProportionProperty =
            DependencyProperty.RegisterAttached("VerticalProportion", typeof(double), typeof(RelativeCanvas),
                new FrameworkPropertyMetadata(double.NaN,
                    OnVerticalProportionChanged));

        public static double GetVerticalProportion(DependencyObject d) {
            return (double)d.GetValue(VerticalProportionProperty);
        }

        public static void SetVerticalProportion(DependencyObject d, double value) {
            d.SetValue(VerticalProportionProperty, value);
        }

        private static void OnVerticalProportionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
            var oldVerticalProportion = (double)e.OldValue;
            var newVerticalProportion = (double)d.GetValue(VerticalProportionProperty);
        }

        #endregion

        #region RelativeOrigin

        public static readonly DependencyProperty RelativeOriginProperty =
            DependencyProperty.RegisterAttached("RelativeOrigin", typeof(Point), typeof(RelativeCanvas),
                new FrameworkPropertyMetadata(new Point(0.5, 0.5),
                    OnRelativeOriginChanged));

        public static Point GetRelativeOrigin(DependencyObject d) {
            return (Point)d.GetValue(RelativeOriginProperty);
        }

        public static void SetRelativeOrigin(DependencyObject d, Point value) {
            d.SetValue(RelativeOriginProperty, value);
        }

        private static void OnRelativeOriginChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
            var oldRelativeOrigin = (Point)e.OldValue;
            var newRelativeOrigin = (Point)d.GetValue(RelativeOriginProperty);
        }

        #endregion

    }

    internal struct SizeInfo {
        public SizeInfo(Size size)
            : this() {
            Size = size;
            WidthChanged = false;
            HeightChanged = true;
        }

        public Size Size { get; set; }
        public bool WidthChanged { get; set; }
        public bool HeightChanged { get; set; }
    }
}

¿Lo viste? Huele a limpio Guiño

Databinding to Structs

Well, this is going to be rude: Binding to struct types can be a tricky. You will absolutely love when the UI just ignores every change. It refuses to update and eventually you’ll get mad about this, wondering why those nice bindings to Paddings or Margins are ignored. It’s kind of frustrating.

This boils down the nature of structs. They are value types and they’re taken as a whole by the dependency property mechanism.

When you define a DP that carries a struct, any binding to a child attribute that composes that struct reflects the actual value of that member, but ONLY when the struct is replaced by another “instance” of the struct (another value). Changing its attributes will not notify any change (Property Changed callbacks won’t be called).

For instance, you have a DP with a Point and a binding link this:

Text=”{Binding Path=MyPoint.X}”

Suppose that the X value of the struct is modified the some other binding (or whatever). You may expect the Text to reflect the X value anytime it is changed, but no way! WPF will blow you off, sticking out its long and complex tongue.

This is (not easily) solved replacing the value of the struct whenever you want the change to be reflected in the UI. “What? you mean that I have to care about changes in every member of the struct in order to have the bindings fresh like my cotton underwear”. Yes, that’s it!

Pretty annoying! but this is the price you have to pay for using those cute simplistic and handy structs!

Don’t forget it. Never! You sweated too much because of this Sonrisa! I hope you finally understood, Suppa JMN!

Y todo esto para tirarme el pegote de escribir en Inglés, ¡copón! Bueno, también es verdad que si algún lobo de mar angloparlante viene a mi blog por alguna casualidad de la vida, quizá le guste entender algo, ¿no?

¡Menús dinámicos! Harás cacota si no sabes esto

Imagina lo siguiente: Tienes un menú que se compone mediante un binding, pero resulta que es una combinación de varias colecciones. Por ejemplo, un menú que presenta una serie de opciones fijas (o variables), y entre medias, una colección enlazada con Binding. Pues colleiga, ¡se hace así!

<MenuItem.ItemsSource>

    <CompositeCollection>

        <!--<MenuItem Header="Menú de coña1" />

        <MenuItem Header="Menú de coña2" />

        <Separator/>-->

        <CollectionContainer Collection="{Binding Source={StaticResource EditorsCollectionViewSource}}" />

        <!--<Separator/>

        <MenuItem Header="Menú de coña3" />-->

    </CompositeCollection>

</MenuItem.ItemsSource>

Con la ayuda de un lobo de mar que dejó su info en Internet, ¡como los buenos samaritanos de Dios!

http://wilberbeast.com/2011/05/31/compositecollection-binding-problem/#comment-2337

En referencia a este error guarrero que da si no enlazas con la CollectionViewSource:

Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=FilterOptions; DataItem=null; target element is ‘CollectionContainer’ (HashCode=23627591); target property is ‘Collection’ (type ‘IEnumerable’)

¡Buen provecho, y una patada en el pecho!