Another Way to Undo Implicit Styles (Revisited)

| 0 comments

In my previous monthly long installment, I showed another way to undo the implicit style, I demonstrated how to leverage the InheritanceBehaviors to break the style lookup chains so you can have a portion of element tree which will directly pick up the system default theme styles. in this post, I will show a different approach to this problem based on the trick Mike Hillberg mentioned in his blog article.

In Mike's original article, he showed that you can undo implicit style by explicitly set the interesting Element's Style property to null, based on this concept, I come up with the following code:

using System;
using System.IO;
using System.Windows;
using System.Diagnostics;
using System.Windows.Markup;
using System.Windows.Controls;
using System.Collections.Generic;

namespace Sheva.Windows.Components
{
    public class StyleManager
    {
        public static DependencyProperty IsDefaultStyleEnabledProperty = DependencyProperty.RegisterAttached(
            "IsDefaultStyleEnabled",
            typeof(Boolean),
            typeof(StyleManager),
            new FrameworkPropertyMetadata(false,
                                          FrameworkPropertyMetadataOptions.Inherits,
                                          new PropertyChangedCallback(OnIsDefaultStyleEnabledPropertyChanged)));
        private static Dictionary<FrameworkElement, Style> oldStyleStore = new Dictionary<FrameworkElement, Style>();

        public static void SetIsDefaultStyleEnabled(FrameworkElement element, Boolean value)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            if (value)
            {
                AddStyleToStore(element);
            }

            element.SetValue(IsDefaultStyleEnabledProperty, value);
        }


        private static void OnIsDefaultStyleEnabledPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement element = sender as FrameworkElement;
            if (element == null) return;

            if ((Boolean)e.NewValue)
            {
                if (!(Boolean)e.OldValue)
                {
                    AddStyleToStore(element);
                }

                element.Style = null;
            }
            else
            {
                if (oldStyleStore.ContainsKey(element))
                {
                    element.Style = oldStyleStore[element];
                }
            }
        }

        private static void AddStyleToStore(FrameworkElement element)
        {
            Debug.Assert(element != null, "parameter 'element' cannot be null");
            if (!oldStyleStore.ContainsKey(element))
            {
                if (element.ReadLocalValue(FrameworkElement.StyleProperty) == DependencyProperty.UnsetValue)
                {
                    Style style = element.TryFindResource(element.GetType()) as Style;
                    oldStyleStore.Add(element, style);
                }
                else
                {
                    oldStyleStore.Add(element, element.Style);
                }
            }
        }
    }
}

From the above code, you can see that I've defined a inheritable attached DependencyProperty so when this attached DP is applied in the parent element, all the children elements within its containing scope will has this attached DP inherited, and undo their implicit styles individually. The cloning of Style property using CreateStyleClone helper method is really necessary since when a style is explicitly set, and you want to turn it off, you should first cache the explicit style in the oldStyleStore, and then set the Style to null, the style within the oldStyleStore dictionary will also be nullified if you don't make a copy of the original style.

The following XAML shows how to use the StyleManager class:

<Window x:Class="UndoImplicitStyles.Window1"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="Undo Implicit Styles" Height="300" Width="300"
       xmlns:local="clr-namespace:Sheva.Windows.Components">
   <Window.Resources>
       <Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
           <Setter Property="FontFamily" Value="{DynamicResource {x:Static SystemFonts.MessageFontFamilyKey}}" />
           <Setter Property="FontSize" Value="{DynamicResource {x:Static SystemFonts.MessageFontSizeKey}}" />
           <Setter Property="FontStyle" Value="{DynamicResource {x:Static SystemFonts.MessageFontStyleKey}}" />
           <Setter Property="FontWeight" Value="{DynamicResource {x:Static SystemFonts.MessageFontWeightKey}}" />
           <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
           <Setter Property="HorizontalContentAlignment" Value="Center" />
           <Setter Property="VerticalContentAlignment" Value="Center" />
           <Setter Property="ClipToBounds" Value="True" />
           <Setter Property="Padding" Value="2" />
           <Setter Property="Margin" Value="10" />
           <Setter Property="Height" Value="30" />
           <Setter Property="Template">
               <Setter.Value>
                   <ControlTemplate TargetType="{x:Type Button}">
                       <Grid>
                           <Rectangle x:Name="GelBackground" Opacity="1" RadiusX="4" RadiusY="4" Stroke="Black" StrokeThickness="1">
                               <Rectangle.Fill>
                                   <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                       <LinearGradientBrush.GradientStops>
                                           <GradientStop Offset="0" Color="White" />
                                           <GradientStop Offset="1" Color="#99ccff" />
                                       </LinearGradientBrush.GradientStops>
                                   </LinearGradientBrush>
                               </Rectangle.Fill>
                           </Rectangle>
                           <Rectangle x:Name="GelShine" Margin="2,2,2,0" VerticalAlignment="Top" RadiusX="6" RadiusY="6" Opacity="0" Stroke="Transparent" Height="15px">
                               <Rectangle.Fill>
                                   <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                                       <LinearGradientBrush.GradientStops>
                                           <GradientStop Offset="0" Color="#ccffffff" />
                                           <GradientStop Offset="1" Color="Transparent" />
                                       </LinearGradientBrush.GradientStops>
                                   </LinearGradientBrush>
                               </Rectangle.Fill>
                           </Rectangle>
                           <ContentPresenter x:Name="ContentSite" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
                       </Grid>
                       <ControlTemplate.Triggers>
                           <Trigger Property="IsMouseOver" Value="true">
                               <Setter Property="Rectangle.Fill" Value="#99ccff" TargetName="GelBackground" />
                               <Setter Property="Rectangle.Opacity" Value="1" TargetName="GelShine" />
                           </Trigger>
                       </ControlTemplate.Triggers>
                   </ControlTemplate>
               </Setter.Value>
           </Setter>
       </Style>
   </Window.Resources>
   <StackPanel>
       <Button Content="Click Me" />
       <CheckBox Content="Undo Implicit Style" Name="checkBox"/>
       <StackPanel local:StyleManager.IsDefaultStyleEnabled="{Binding ElementName=checkBox, Path=IsChecked}">
           <Button Content="Click Me" />
           <StackPanel>
               <Button Content="Click Me" />
               <StackPanel>
                   <Button Content="Click Me" />
               </StackPanel>
           </StackPanel>
       </StackPanel>
       <Button Content="Click Me" />
   </StackPanel>
</
Window>

When you run above XAML code, you will find that when checking and unchekding the CheckBox, the style of Buttons within the containing StackPanel parent will by toggled between default system style and the implicit style set in the resource.

Disclaimer: The above solution just gives you a way to tackle the problem of undoing implicit styles, and it's not an elegant or performant way of doing things, I just come up with it here for completeness rather than as a recommendation. if you want to have a portion of your element tree to just pick up the system default styles, I strongly recommend you to use the approach I demonstrated in my previous article, since presumably this approach is also taken by the Expression Blend.

Attachment: UndoImplicitStyles.zip

How To Enumerate All The Bindings Set On A Specfied DependencyObject?

| 1 comments

Dr WPF one of the most active WPF MSDN forum participant just posts a reply on MSDN forum on how to enumerate all the binding objects set on a specified DependencyObject, this guy who seems to have the whole WPF SDK imprinted into his brilliant mind really knows something about WPF:)

I just refactored his code a little bit, and made into my own WPF component/control library. kudos, Dr WPF:)

using System;
using System.Windows;
using System.Windows.Data;
using System.ComponentModel;
using System.Collections.Generic;

namespace Sheva.Windows.Components
{
    public static class DependencyPropertyHelper
    {
        public static IList<DependencyProperty> GetAttachedProperties(Object element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            List<DependencyProperty> attachedProperties = new List<DependencyProperty>();

            foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(element,
                new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues |
                                                                             PropertyFilterOptions.UnsetValues |
                                                                             PropertyFilterOptions.Valid) }))
            {
                DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(pd);
                if (dpd != null && dpd.IsAttached)
                {
                    attachedProperties.Add(dpd.DependencyProperty);
                }
            }

            return attachedProperties;
        }

        public static IList<DependencyProperty> GetProperties(Object element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            List<DependencyProperty> properties = new List<DependencyProperty>();

            foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(element,
                new Attribute[] { new PropertyFilterAttribute(PropertyFilterOptions.SetValues | 
                                                                             PropertyFilterOptions.UnsetValues |
                                                                             PropertyFilterOptions.Valid) }))
            {
                DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(pd);
                if (dpd != null)
                {
                    properties.Add(dpd.DependencyProperty);
                }
            }

            return properties;
        }

        public static IEnumerable<Binding> EnumerateBindings(DependencyObject element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            LocalValueEnumerator lve = element.GetLocalValueEnumerator();

            while (lve.MoveNext())
            {
                LocalValueEntry entry = lve.Current;

                if (BindingOperations.IsDataBound(element, entry.Property))
                {
                    Binding binding = (entry.Value as BindingExpression).ParentBinding;
                    yield return binding;
                }
            }
        }
    }
}

ObservableObject - Is This The Proper Implementation of INotifyPropertyChanged?

| 0 comments

I've not written any blog articles on WPF for a long time, and at the same time, I missed a lot of cool blog articles written by any WPF'ers too. So recently I spent some time reading those blogs published one or two months ago, and really got a lot of new and cool tricks and tips on WPF.

One of the article I always enjoy reading is Josh Smith's blog post on a base class which implements INotifyPropertyChanged and the those awesome comments following this post. Designing a base class for general data binding purpose is always a challenge, in particular, if you take into consideration things like multi-threading, and the various business scenarios and changing customer needs. I think that's why Microsoft doesn't come up with a default implementation of INotifyPropertyChanged class which developers can subclass to design their business entities. The following is my take on this challenge after reading Josh Smith's original article and those comments following it:

using System;
using System.Threading;
using System.Reflection;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.Generic;

namespace Sheva.Windows.Data
{
    /// <summary>
    /// This class acts as the base class for any data entity which provides notifications whenever any of its property is changed.
    /// </summary>
    public abstract class ObservableObject : INotifyPropertyChanged
    {
        #region Data & Constructors

        private const Int32 notificationDisabled = 0;
        private const Int32 notificationEnabled = 1;

        private const String Error_Msg = "{0} is not a public property of {1}";
        private const String Error_SuspendNotification = "Nested SuspendNotification is not supported";
        private const String Error_ResumeNotification = "ResumeNotification without first calling SuspendNotification is not supported";
        private Int32 objectState = notificationEnabled;

        protected ObservableObject()
        {
        }

        #endregion

        #region
Public Members

        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Suspend the property changed notification.
        /// </summary>
        /// <remarks>
        /// After this call, the property changed notification is disabled.
        /// </remarks>
        public void SuspendNotification()
        {
            if (Interlocked.CompareExchange(ref objectState, notificationDisabled, notificationEnabled) != notificationEnabled)
            {
                throw new InvalidOperationException(Error_SuspendNotification);
            }
        }

        /// <summary>
        /// Resume the property changed notification.
        /// </summary>
        /// <remarks>
        /// After this call, the property changed notification is re-enabled.
        /// </remarks>
        public void ResumeNotification()
        {
            if (Interlocked.CompareExchange(ref objectState, notificationEnabled, notificationDisabled) != notificationDisabled)
            {
                throw new InvalidOperationException(Error_SuspendNotification);
            }
        }

        public Boolean IsNotificationEnabled
        {
            get { return Thread.VolatileRead(ref objectState) == 1; }
        }

        #endregion

        #region
Protected Members

        ///<summary>
        /// Invoked whenever the value of any property is changed.
        ///</summary>
        ///<remarks>
        /// Derived classes can override this method to include any logic after the property is changed.
        ///</remarks>
        /// <param name="propertyName">
        /// The name of the property which was changed.
        /// </param>
        protected virtual void OnPropertyChanged(String propertyName)
        {
            // Do nothing
        }

        ///<summary>
        ///Raise the property changed event.
        ///</summary>
        /// <param name="propertyName">
        /// The name of the property which was changed.
        /// </param>
        protected void RaisePropertyChangedEvent(String propertyName)
        {
            VerifyProperty(propertyName);
            if (Thread.VolatileRead(ref objectState) == notificationDisabled)
            {
                return;
            }

            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                // Raise the PropertyChanged event.
                handler(this, new PropertyChangedEventArgs(propertyName));
            }

            OnPropertyChanged(propertyName);
        }

        #endregion

        #region
Private Helpers

        [Conditional("DEBUG")]
        private void VerifyProperty(String propertyName)
        {
            Type type = GetType();
            PropertyInfo propertyInfo = type.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);

            Debug.Assert(propertyInfo != null, String.Format(Error_Msg, propertyName, type.FullName));
        }

        #endregion
    }
}

Note that I don't cache the PropertyChangedEventArgs objects as Josh Smith did, properly his cache scheme can improve this performance a little bit, but I think in any interactive UI programming, that kinda performance gain doesn't make any signficant difference. as a bonus, I add two pair methods called SuspendNotification and ResumeNotification, this two methods can be pretty useful when you first initialize the business objects, at this instance, you don't want the UI or the change notification subscribers to be notified about the property change.

I've attached a sample project of how to use ObservableObject base class, you can download it from here:

Attachment: ObservableObjectDemo.zip

The Tricks with TreeViewItem Expansion (Revisited)

| 1 comments

Last year, I posted a blog article talking about how to expand TreeViewItems, unfortunately, the download link to the source code of that article was broken, and what's more, I lost the original source code myself, so in today's post, I want to revisit this topic with the core piece of code which actually does the trick.

using System;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace Sheva.Windows.Controls
{
    public static class TreeViewHelper
    {
        public static void ExpanAll(TreeView treeView)
        {
            ExpandSubContainers(treeView);
        }

        private static void ExpandSubContainers(ItemsControl parentContainer)
        {
            foreach (Object item in parentContainer.Items)
            {
                TreeViewItem currentContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
                if (currentContainer != null && currentContainer.Items.Count > 0)
                {
                    currentContainer.IsExpanded = true;
                    if (currentContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
                    {
                        currentContainer.ItemContainerGenerator.StatusChanged += delegate
                        {
                            ExpandSubContainers(currentContainer);
                        };
                    }
                    else
                    {
                        ExpandSubContainers(currentContainer);
                    }
                }
            }
        }
    }
}

The key caveat of expanding TreeViewItems is to make sure that the container for the current TreeViewItem has been generated, so you can safely expand it to show all its sub items, that's why I make recursive call when the status of current TreeViewItem's ItemContainerGenerator is set to GeneratorStatus.ContainersGenerated.

Delegate.BeginInvoke vs ThreadPool.QueueUserWorkItem

| 3 comments

Today, I come across Chris Brumme's blog on TransparentProxy, I really enjoy his blog, in particular those dedicated to the inner-working of CLR, one of the surprising sentence I read from his TransparentProxy article is that:

One surprising fact is that this is also why Delegate.BeginInvoke / EndInvoke are so slow compared to equivalent techniques like ThreadPool.QueueUserWorkItem (or UnsafeQueueUserWorkItem if you understand the security implications and want to be really efficient). The codepath for BeginInvoke / EndInvoke quickly turns into the common Message processing code of the general remoting pathway.

   I have to say I get pretty stunned when reading this, so I write a little piece of code to verify his allegation:

using System;
using System.Threading;
using System.Diagnostics;

namespace DelegatePerformance
{
    class Program
    {
        static void Main(string[] args)
        {
            ThreadStart threadStart = new ThreadStart(() => { });
            WaitCallback waitCallback = new WaitCallback(a => { });
            Stopwatch stopWatch = new Stopwatch();

            stopWatch.Start();
            for (int i = 0; i < 10000; i++)
            {
                threadStart.BeginInvoke(null, null);
            }
            stopWatch.Stop();
            Console.WriteLine("Delegate.BeinInvoke(): {0}",stopWatch.ElapsedTicks.ToString());

            stopWatch.Reset();
            stopWatch.Start();
            for (int i = 0; i < 10000; i++)
            {
                System.Threading.ThreadPool.QueueUserWorkItem(waitCallback);
            }
            stopWatch.Stop();
            Console.WriteLine("ThreadPool.QueueUserWorkItem(): {0}", stopWatch.ElapsedTicks.ToString());
        }
    }
}

And the following output is what I get when running above programme:

Delegate.BeinInvoke(): 591441
ThreadPool.QueueUserWorkItem(): 91958

From the output, there is no brainer that Delegate.BeginInvoke is way much slower than the ThreadPool.QueueUserWorkItem call.


 

Dash Animation

| 2 comments

I seldom use WPF's animation capabilities, mostly because I believe the overuse of animations doesn't do any good to user experience, but animtions can be cool if you do it and use it appropriately, this bit of XAML snippet shown below just comes into my imagination, it doesn't serve any practical purpose, but is fun to play with:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         
Title="Dash Animation">
    <
Viewbox Margin="30">
        <
Rectangle
           Name="ellipse"
           Width="600"
           Height="600"
           Fill="Green"
           Stroke="Red"
           StrokeThickness="120"
           StrokeDashArray="0,2"
           StrokeDashOffset="0"
           StrokeDashCap="Round">
            <
Rectangle.Triggers>
                <
EventTrigger RoutedEvent="FrameworkElement.Loaded">
                    <
BeginStoryboard>
                        <
Storyboard>
                            <
DoubleAnimation
                               To="20"
                               Duration="0:0:5"
                               RepeatBehavior="Forever"
                               By="2"
                               Storyboard.TargetProperty="StrokeDashOffset" />
                        </
Storyboard>
                    </
BeginStoryboard>
                </
EventTrigger>
            </
Rectangle.Triggers>
        </
Rectangle>
    </
Viewbox>
</
Page>

Focus Navigation Using Enter Key

| 0 comments

Moving focus between UI elements using Enter key is probably one of the controversial features of user interaction design, personally I think it has its own usage, and is a great complement to tab navigation, here is the two techniques of implementing enter navigation I learnt from WPF MSDN forum, both of the two methods need you to first hook up to interesting controls' KeyDown event, imagine you have such piece of XAML:

<Window x:Class="EnterNavigationDemo.Window1"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="Login" Width="300" Height="200" ResizeMode="NoResize"
    >
  <
Window.Resources>
    <
Style TargetType="{x:Type Button}">
      <
Setter Property="Width" Value="60"/>
      <
Setter Property="Margin" Value="4"/>
    </
Style>
    <
Style TargetType="{x:Type TextBox}">
      <
Setter Property="Width" Value="160"/>
      <
Setter Property="Margin" Value="4"/>
    </
Style>
    <
Style TargetType="{x:Type PasswordBox}">
      <
Setter Property="Width" Value="160"/>
      <
Setter Property="Margin" Value="4"/>
    </
Style>
    <
Style TargetType="{x:Type Label}">
      <
Setter Property="Margin" Value="4"/>
    </
Style>
  </
Window.Resources>
  <
Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Name="documentRoot">
    <
Grid.RowDefinitions>
      <
RowDefinition/>
      <
RowDefinition/>
      <
RowDefinition/>
      <
RowDefinition/>
      <
RowDefinition/>
    </
Grid.RowDefinitions>
    <
Label Target="{Binding ElementName=txtDomainName}" Content="_Domain:" Grid.Column="0" Grid.Row="0"/>
    <
TextBox Name="txtDomainName" Grid.Column="1" Grid.Row="0"/>
    <
Label Target="{Binding ElementName=txtUserName}" Content="_User:" Grid.Column="0" Grid.Row="1"/>
    <
TextBox Name="txtUser" Grid.Column="1" Grid.Row="1"/>
    <
Label Target="{Binding ElementName=txtPasswrod}" Content="_Passwrod:" Grid.Column="0" Grid.Row="2"/>
    <
PasswordBox Name="txtPasswrod" Grid.Column="1" Grid.Row="2"/>
    <
StackPanel Grid.Row="3" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Center">
      <
Button Content="_Login" Name="loginButton" IsDefault="True"/>
      <
Button Content="_Cancel" Name="cancelButton" IsCancel="True"/>
    </
StackPanel>
  </
Grid>
</
Window>

In the code behind file, you can achieve enter navigation using the following methods:

public partial class Window1 : System.Windows.Window
{
    public Window1()
    {
        InitializeComponent();
        this.documentRoot.AddHandler(TextBox.KeyDownEvent, new KeyEventHandler(documentRoot_KeyDown));
        this.loginButton.Click += new RoutedEventHandler(loginButton_Click);
        this.cancelButton.Click += new RoutedEventHandler(cancelButton_Click);
    }

    private void cancelButton_Click(Object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Cancel button is clicked");
    }

    private void loginButton_Click(Object sender, RoutedEventArgs e)
    {
        MessageBox.Show("Login button is clicked");
    }

    private void documentRoot_KeyDown(Object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Return)
        {
            UIElement focusedElement = Keyboard.FocusedElement as UIElement;
            if (focusedElement != null)
            {
                focusedElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
            }
            e.Handled = true;
        }
    }

}

you can also achieve this by modifying the key down event handler to add the following code:

private void documentRoot_KeyDown(Object sender, KeyEventArgs e)
{
    if (e.Key == Key.Return)
    {
        e.Handled = true;
        KeyEventArgs args = new KeyEventArgs(Keyboard.PrimaryDevice, Keyboard.PrimaryDevice.ActiveSource, 0, Key.Tab);
        args.RoutedEvent = Keyboard.KeyDownEvent;
        InputManager.Current.ProcessInput(args);
    }
}

You can see that I add two buttons on the window, my initial thought is that I want enter navigate between the three TextBoxes, but I still want to tab navigate to the buttons, and the above code finally ends up working in the way I desire.