Zhou's profileSheva's TechSpacePhotosBlogLists Tools Help

Blog


    4/5/2008

    WPF, Terminal Services & Bitmap Remoting

    Everybody who has been playing around with WPF for a reasonable period of time might have known that WPF Application Processing is actually split up into two threads, they are UI thread which runs the dispatcher loop, and processes any GUI, keyboard/mouse/ink input, animation, layout and user initiated messages/events, and there is a hidden unmanaged background thread which is responsible for composing the graphical content (both in hardware or software if fallback is needed), and presenting it to frame buffer for display.

    The whole architecture of WPF is built around this two threads model, and it has some of the benefits as articulated by Chris Anderson in the two-year long Channel video. One of the benefits Chris mentioned is that with the two threads model, in terminal services scenario, we can have the UI thread running in the TS server, and have the composition thread (or render thread in other nomenclature) running at the TS client console. And the communication between the two threads are handed over to the TS/RDP protocol. This can enable one interesting scenario called primitive remoting or vector remoting. And since UI thread is running at the TS server side, and it maintains the visual tree and the composition is at the TS client side and it keeps up a composition tree, so in order to keep the client screen up to date, the UI thread will send the edits to the composition tree over the TS/RDP protocol in a highly compressed manner, this not only saves the network bandwidth, because only the edits need to be remoted, but also speeds up the client processing. This type of server/client communication also holds true in the standalone WPF application, the difference is that the two threads are running at the same Win32 process, and the inter thread communication mechanism is used instead of the TS/RDP wire. To enable primitive remoting, both the TS server and TS client should run under Windows Vista and with desktop composition is enabled, this requirement tremendously narrows down the scenario in which the primitive remoting could be leveraged.

    The upcoming .NET Framework 3.5 SP1 release would change all of this, in particular, bitmap remoting with sub region update support will always be used even if the TS server and the TS client are equipped to support primitive remoting, primitive remoting can be as good or bad as bitmap remoting, and the scenario in which primitive remoting is enabled is quite rare, because most existing Windows servers such as Windows 2000 or Windows 2003 server families don't support primitive remoting by default. So for any developer who want your WPF applications to run reasonably well under TS scenario and running under TS is an important scenario for your applications, you need to take implication of bitmap remoting into consideration beforehand, there are a couple of ways from which you can improve the performance of your WPF application in TS scenario:

    • Considering using as little animations as possible in your application, and if animations are indispensable, you could try reducing the DesiredFrameRate (The default value is 6O FPS) of each animations you need to use, usually 25~30 FPS should be enough, and if you don't need to high fidelity animation experience, you can use a even lower frame rate.
    • Considering using solid color brushes instead of gradient or tile brushes.
    • Try reducing the number of "hidden redraws" your application needs to perform, "hidden redraws" will be overlaid by its most foreground redraws, but they will still be remoted which waste network bandwidth. You can visualize the "hidden redraws" using the Perforator (part of WPF Performance Suite) with "Show dirty-regions update overlay" CheckBox checked.
    1/3/2008

    How to Reference Named Elements Within CellTemplate?

    Getting reference to the named elements within data template is a commonly requested feature of WPF. In the current version of WPF, there is no intuitive and straightforward way to do it, you need to either traverse the visual tree to compare the Name property of each FrameworkElements within the tree, or alternatively you can first off grab the reference to the ContentPresenter within the ControlTemplate, and call DataTemplate.FindName() passing in the ContentPresenter reference as the "templatedParent" argument. Accessing named elements within CellTemplate complicates the things a bit since each Cell will have different instantiation of CellTemplate, the following helper class gives a possible solution to this problem (I also posted it in this WPF MSDN forum thread):

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

    namespace Sheva.Windows.Component
    {
        public static class ListViewHelper
      
    {
            /// <summary>
            ///
    Finds an element that has the provided identifier name within the specified cell template identified by the cellColumn and cellRow
          
    /// </summary>
            /// <param name="listView">
    the referenced ListView control</param>
            /// <param name="cellColumn">
    the index of column from which the CellTemplate will be retrieved</param>
            /// <param name="cellRow">
    the index of the row from which the specified ListViewItem will be retrieved</param>
            /// <param name="name">
    The name of the requested element.</param>
            /// <returns>
    The requested element. This can be null reference if no matching element was found.</returns>
          
    public static FrameworkElement FindNameFromCellTemplate(ListView listView, Int32 cellColumn, Int32 cellRow, Stringname)
            {
                if(listView == null)
                {
                    throw newArgumentNullException("listView");
                }

                if(!listView.IsLoaded)
                {
                    throw new InvalidOperationException("ListView is not yet loaded");
                }

                if (cellRow >= listView.Items.Count || cellRow < 0)
                {
                    throw new ArgumentOutOfRangeException("row");
                }

                GridView gridView = listView.View as GridView;
                if (gridView == null)
                {
                    return null;
                }

                if (cellColumn >= gridView.Columns.Count || cellColumn < 0)
                {
                    throw new ArgumentOutOfRangeException("column");
                }

                ListViewItem item = listView.ItemContainerGenerator.ContainerFromItem(listView.Items[cellRow]) as ListViewItem;
                if (item != null)
                {
                    if (!item.IsLoaded)
                    {
                        item.ApplyTemplate();
                    }
                    GridViewRowPresenter rowPresenter = GetFrameworkElementByName<GridViewRowPresenter>(item);

                    if(rowPresenter != null)
                    {
                        ContentPresenter templatedParent = VisualTreeHelper.GetChild(rowPresenter, cellColumn) as ContentPresenter;
                        DataTemplate dataTemplate = gridView.Columns[cellColumn].CellTemplate;
                        if(dataTemplate != null && templatedParent != null)
                        {
                            return dataTemplate.FindName(name, templatedParent) as FrameworkElement;
                        }
                    }
                }

                return null;
            }

            private static T GetFrameworkElementByName<T>(FrameworkElement referenceElement) where T : FrameworkElement
          
    {
                FrameworkElementchild = null;
                for(Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceElement); i++)
                {
                    child = VisualTreeHelper.GetChild(referenceElement, i) as FrameworkElement;
                    if(child != null && child.GetType() == typeof(T))
                    {
                        break;
                    }
                    else if(child != null)
                    {
                        child = GetFrameworkElementByName<T>(child);
                        if(child != null && child.GetType() == typeof(T))
                        {
                            break;
                        }
                    }
                }
                returnchild asT;
            }
        }
    }

    This actually requires a bit of coding, hopefully future version of WPF in particular the ListView control could has a much better content model to make it more flexible and extensible than it is right now.

    11/25/2007

    DataErrorValidationRule - New Way To Invalidate Data In WPF

    The WPF 3.5 has introduced a new data validation API aka DataErrorValidationRule, which you can specify on the Binding object, if the data source implements the IDataErrorInfo interface. This new feature enables some of the scenario the previous Custom ValidationRule cannot enable.

    One of the scenario the custom ValidationRule cannot support is to enable data binding on the properties of the Custom ValidationRule class. Because ValidationRule is not a DependencyOject, you cannot define dependency properties on it to enable data binding. And because ValidationRule is not a part of the element tree, any Binding expression which relies on RelativeSource or ElementName or similar things that needs to walk up the element tree to find the binding source cannot be evaluated successfully. Some community members such as Josh Smith has come up with a hackery to workaround this limitation using the trick he calls "Virutal Branches". Or our beloved Dr. WPF's ObjectReference custom Markup Extension as he "bloated" in this MSDN WPF thread.

    DataErrorValidationRule drives those "dirty tricks" to obsolete. Because You don't need to bind some values to custom ValidationRule object as validation input parameters. Because when implementing IDataErrorInfo, you do the validation logic at the source object. The following is an example of how to leverage the DataErrorValidationRule API to suppport the type of scenario "Virtual Branches" is trying to enable.

    First off, the data source class should implement IDataErrorInfo or INotifyPropertyChanged if you want to enable two way data binding as follows:

    public class Person : IDataErrorInfo, INotifyPropertyChanged
    {
        private int age;
        private int min = 0;
        private int max = 150;

        public int Age
        {
            get { return age; }
            set
            {
                age = value;
                RaisePropertyChanged("Age");
            }
        }

        public string Error
        {
            get
            {
                return null;
            }
        }

        public int Min
        {
            get
            {
                return min;
            }
            set
            {
                min = value;
                RaisePropertyChanged("Min");
            }
        }

        public int Max
        {
            get
            {
                return max;
            }
            set
            {
                max = value;
                RaisePropertyChanged("Max");
            }
        }

        public string this[string name]
        {
            get
            {
                string result = null;

                if (name == "Age")
                {
                    if (this.age < this.Min || this.age > this.Max)
                    {
                        result = String.Format("Age must not be less than {0} or greater than {1}.", this.Min, this.Max);
                    }
                }
                return result;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    You can see that Min and Max properties needs to be specified by the user, so we need to bind those two properties to corresponding UI elements as following XAML snippet shows:

    <Window x:Class="BusinessLayerValidation.Window1"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
           Title="WPF IDataErrorInfo Sample"
           Width="450" Height="170"
           xmlns:src="clr-namespace:BusinessLayerValidation">

      <
    Window.Resources>
        <
    src:Person x:Key="data"/>
        <
    Style x:Key="textBoxInError" TargetType="TextBox">
          <
    Style.Triggers>
            <
    Trigger Property="Validation.HasError" Value="true">
              <
    Setter Property="ToolTip"
                     Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                            Path=(Validation.Errors)[0].ErrorContent}
    "/>
            </
    Trigger>
          </
    Style.Triggers>
        </
    Style>
      </
    Window.Resources>

      <
    StackPanel Margin="20" DataContext="{Binding Source={StaticResource data}}">
        <
    StackPanel Orientation="Horizontal">
          <
    TextBlock Width="60">
            Min:(<TextBlock Text="{Binding Path=Value, ElementName=minSlider}"/>)
          </TextBlock>
          <
    Slider Margin="10, 0, 0, 0"
                  Name="minSlider"
                  Width="300"
                  Orientation="Horizontal"
                  IsSnapToTickEnabled="True"
                  HorizontalAlignment="Right"
                  TickPlacement="BottomRight"
                  AutoToolTipPlacement="BottomRight"
                  Value="{Binding Path=Min, Mode=TwoWay}"
                  Minimum="0"
                  Maximum="150"
                  TickFrequency="10"/>
        </
    StackPanel>
        <
    StackPanel Orientation="Horizontal">
          <
    TextBlock Width="60">
            Max:(<TextBlock Text="{Binding Path=Value, ElementName=maxSlider}"/>)
          </TextBlock>
          <
    Slider Margin="10, 0, 0, 0"
                  Name="maxSlider"
                  Width="300"
                  Orientation="Horizontal"
                  IsSnapToTickEnabled="True"
                  HorizontalAlignment="Right"
                  TickPlacement="BottomRight"
                  AutoToolTipPlacement="BottomRight"
                  Value="{Binding Path=Max, Mode=TwoWay}"
                  Minimum="0"
                  Maximum="150"
                  TickFrequency="10"/>
        </
    StackPanel>
        <
    TextBlock>Enter your age:</TextBlock>
        <
    TextBox Style="{StaticResource textBoxInError}" Name="textBox">
          <
    TextBox.Text>
            <
    Binding Path="Age"
                    ValidatesOnDataErrors="True"
                    UpdateSourceTrigger="PropertyChanged">
              <
    Binding.ValidationRules>
                <
    ExceptionValidationRule/>
              </
    Binding.ValidationRules>
            </
    Binding>
          </
    TextBox.Text>
        </
    TextBox>
      </
    StackPanel>
    </
    Window>

    You can see from the XAML shown above that DataErrorValidationRule actually provide a greater flexibility when validating data. For a detailed introduction to DataErrorValidationRule, and its role in the WPF data validation model, you can refer to this WPF SDK blog article.

    For completeness, I've attached full sample project here for further reference.

    Attachment: WPFDataValidation.zip

    How To Search Text In WPF FlowDocument?

    This blog article is a reply to the recent WPF MSDN forum thread on how to efficiently search text in FlowDocument. The thread starter needs to have the same performance as the search feature in Visual Studio text editor. I don't know how Visual Studio IDE implements the search feature, but in terms of search in FlowDocument, because FlowDocument enables much richer content model, It's presumably much harder to achieve the same search performance as Visual Studio text editor.

    I have to say that the code I posted in that thread apparently has a serious performance flaw, it introduces a lot of unnecessary iterations. After digging into this issue at the weekend, I finally come up with a method which can achieve the perceived performance, and I think this should be enough at most circumstance. Based on this method, I mocked up a sample code which shows how to perform find and replace feature in FlowDocument, because find and replace is a common feature every text editing tool should provide, this might help others who need this similar feature. The following shows the core code which perform the search:

    /// <summary>
    ///
    Find the corresponding<see cref="TextRange"/> instance
    /// representing the input string given a specified text pointer position.
    /// </summary>
    ///
    <param name="position">the current text position</param>
    ///
    <param name="textToFind">input text</param>
    ///
    <param name="findOptions">the search option</param>
    ///
    <returns>An<see cref="TextRange"/> instance represeneting the matching string withing the text container.</returns>
    public TextRange GetTextRangeFromPosition(ref TextPointer position, String input, FindOptions findOptions)
    {
        Boolean matchCase = (findOptions & FindOptions.MatchCase) == FindOptions.MatchCase;
        Boolean matchWholeWord = (findOptions & FindOptions.MatchWholeWord) == FindOptions.MatchWholeWord;

        TextRange textRange = null;

        while (position != null)
        {
            if (position.CompareTo(inputDocument.ContentEnd) == 0)
            {
                break;
            }

            if (position.GetPointerContext(LogicalDirection.Forward) == TextPointerContext.Text)
            {
                String textRun = position.GetTextInRun(LogicalDirection.Forward);
                StringComparison stringComparison = matchCase ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase;
                Int32 indexInRun = textRun.IndexOf(input, stringComparison);

                if (indexInRun >= 0)
                {
                    position = position.GetPositionAtOffset(indexInRun);
                    TextPointer nextPointer = position.GetPositionAtOffset(input.Length);
                    textRange = new TextRange(position, nextPointer);

                    if (matchWholeWord)
                    {
                        if (IsWholeWord(textRange)) // Test if the "textRange" represents a word.
                        {
                            // If a WholeWord match is found, directly terminate the loop.
                            break;
                        }
                        else
                        {
                            // If a WholeWord match is not found, go to next recursion to find it.
                            position = position.GetPositionAtOffset(input.Length);
                            return GetTextRangeFromPosition(ref position, input, findOptions);
                        }
                    }
                    else
                    {
                        // If a none-WholeWord match is found, directly terminate the loop.
                        position = position.GetPositionAtOffset(input.Length);
                        break;
                    }
                }
                else
                {
                    // If a match is not found, go over to the next context position after the "textRun".
                    position = position.GetPositionAtOffset(textRun.Length);
                }
            }
            else
            {
                //If the current position doesn't represent a text context position, go to the next context position.
                // This can effectively ignore the formatting or embedded element symbols.
                position = position.GetNextContextPosition(LogicalDirection.Forward);
            }
        }

        return textRange;
    }

    The code above is part of my FindAndReplaceManager helper class implementation, you can refer to the attachment for the complete source code. The code should be pretty straightforward as I've commentted it. The FindAndReplaceManager can support search options such as FindOptions.MatchCase and FindOptions.MatchWholeWord, aka two commonly used search options. For simplicity, I don't implement reverse search, since this should be really straightforward, instead of using LogicalDirection.Forward, you could use LogicalDirection.Backward.

    As I've said, the FindAndReplaceManager should be able to achieve perceived performance at most situation, if you need hard best performance. You'd better choose a more sophisticated search algorithm instead of the bare-bones "start-to-end" search algorithm as is illustrated in the code above.

    Another alternative you could choose is the internal undocumented search API provided by WPF. The System.Windows.Documents.TextFindEngine class has a static "Find" method, this method is widely used in build-in document readers and viewers such as FlowDocumentReader, FlowDocumentPageViewer, and FlowDocumentScrollViewer. Because TextFindEngine has a much better understanding of the underlying document content structure, it should provide the hard performance benefit you expect. The following helper method shows how to use this method:

    using System;
    using System.Windows;
    using System.Reflection;
    using System.Globalization;
    using System.Windows.Documents;

    namespace Sheva.Windows.Documents
    {
        [Flags]
        public enum FindFlags
        {
            FindInReverse = 2,
            FindWholeWordsOnly = 4,
            MatchAlefHamza = 0x20,
            MatchCase = 1,
            MatchDiacritics = 8,
            MatchKashida = 0x10,
            None = 0
        }

        public static class DocumentHelper
        {
            private static MethodInfo findMethod = null;

            public static TextRange FindText(TextPointer findContainerStartPosition,TextPointer findContainerEndPosition, String input, FindFlags flags, CultureInfo cultureInfo)
            {
                TextRange textRange = null;
                if (findContainerStartPosition.CompareTo(findContainerEndPosition) < 0)
                {
                    try
                    {
                        if (findMethod == null)
                        {
                            findMethod = typeof(FrameworkElement).Assembly.GetType("System.Windows.Documents.TextFindEngine").
                                   GetMethod("Find", BindingFlags.Static | BindingFlags.Public);
                        }
                        Object result = findMethod.Invoke(null, new Object[] { findContainerStartPosition,
                        findContainerEndPosition,
                        input, flags, CultureInfo.CurrentCulture });
                        textRange = result as TextRange;
                    }
                    catch (ApplicationException)
                    {
                        textRange = null;
                    }
                }

                return textRange;
            }
        }
    }

    Because TextFindEngine.Find() is a non-public API, we should use a bit of reflection code to call it. If you are working on pesonal project, feel free to use it as an alternative, but never ever use this method in production code.

    WPF should provide a much better built-in public API to perform search operation in FlowDocument. I don't know what type of future plan WPF team has, but from my educated guess, WPF should have a much better support on this in the near future.

    Attachment: SearchInFlowDocumentDemo.zip

    10/23/2007

    How To Globally Specify FontFamily In WPF?

       This question is asked about seven months ago in WPF MSDN forum, From the begining, I think this should be obvious, just specify the TextElement.FontFamily in the Application level, the font shall be applied to the entire UI elements within the app:

    <Application TextElement.FontFamily="Constantia"/>

       But the thing is not that so straightforward, since Application is not a DependencyObject, you cannot specify an attached property on it, then how to do this trick?

       Just recently, I come up with an approach, since nearly every WPF UI control hosts TextBlock inside (either in data template or control template) to display text (except FlowDocument, FlowDocument has a different mechanism to render text), I can specify a Style within Application.Resources for TextBlock, then the style shall be applied to each TextBlocks within the app:

    <Application x:Class="GlobalFontSettings.App"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       StartupUri="Window1.xaml">
      <
    Application.Resources>
        <
    Style TargetType="{x:Type TextBlock}">
          <
    Setter Property="TextElement.FontFamily" Value="Constantia"/>
        </
    Style>
      </
    Application.Resources>
    </
    Application>

         I've written a little test app for it, and it works like a charm, hope this can help others who also want to implement the similar thing in WPF.

    10/17/2007

    Another Way to Undo Implicit Styles (Revisited)

        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

    10/16/2007

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

        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;
                    }
                }
            }
        }
    }

    10/6/2007

    ObservableObject - Is This The Proper Implementation of INotifyPropertyChanged?

       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

    8/7/2007

    The Tricks with TreeViewItem Expansion (Revisited)

       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.

    4/2/2007

    Dash Animation

       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>

    4/1/2007

    Focus Navigation Using Enter Key

       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.

    3/27/2007

    WTF of WPF [Part One]- Templating & Styling

       Have you ever been bugged by WPF's strange behaviour for a long time and cannot stop asking:"WTF?".  Douglas Stockwell just met one of those confusing aspects of WPF, I just generalize his confusion about WPF's templating and styling into the following XAML repro code:

    <Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <
    Page.Resources>
            <
    Style TargetType="{x:Type TextBlock}">
                <
    Setter Property="TextElement.Foreground" Value="Cyan"/>
            </
    Style>
            <
    Style TargetType="{x:Type ContentPresenter}">
                <
    Setter Property="TextElement.Foreground" Value="Cyan"/>
            </
    Style>
            <
    Style TargetType="{x:Type  AccessText}">
                <
    Setter Property="TextElement.Foreground" Value="Cyan"/>
            </
    Style>
            <
    Style TargetType="{x:Type Rectangle}">
                <
    Setter Property="Fill" Value="Cyan"/>
            </
    Style>
            <
    Style TargetType="{x:Type Image}">
                <
    Setter Property="Source" Value="C:\WINDOWS\Web\Wallpaper\Home.jpg"/>
            </
    Style>
            <
    Style TargetType="{x:Type MediaElement}">
                <
    Setter Property="Source" Value="C:\WINDOWS\clock.avi"/>
            </
    Style>
            <
    Style TargetType="{x:Type InkCanvas}">
                <
    Setter Property="Background" Value="Cyan"/>
            </
    Style>
            <
    Style TargetType="{x:Type Border}">
                <
    Setter Property="BorderThickness" Value="10"/>
                <
    Setter Property="BorderBrush" Value="Cyan"/>
            </
    Style>
            <
    Style TargetType="{x:Type StackPanel}">
                <
    Setter Property="Background" Value="Green"/>
            </
    Style>
            <
    Style TargetType="{x:Type Control}">
                <
    Setter Property="Template">
                    <
    Setter.Value>
                        <
    ControlTemplate>
                            <
    Label>Inside Control</Label>
                        </
    ControlTemplate>
                    </
    Setter.Value>
                </
    Setter>
            </
    Style>
            <
    ControlTemplate x:Key="template">
                <!--
    WTF, StackPanel, TextBlock, AccessText, ContentPresenter, InkCanvas,  Image, Rectangle,
                  MediaElement etc cannot pick up their respective implicit styles here,
                  one takeway from this empircal discovery is that Elements directly derived from FramworkElement cannot work in this scenario.
    -->
                <
    StackPanel>
                    <
    TextBlock Name="tb">inside TextBlock</TextBlock>
                    <
    AccessText>inside AccessText</AccessText>
                    <
    ContentPresenter>
                        <
    ContentPresenter.Content>
                        inside ContentPresenter
                        </ContentPresenter.Content>
                    </
    ContentPresenter>
                    <
    InkCanvas/>
                    <
    Image/>
                    <
    Rectangle Width="40" Height="40"/>
                    <
    MediaElement/>
                    <!--
    WTF, Border can pick up implicit style here.-->
                    <
    Border Width="40" Height="40"/>
                    <!--
    WTF, Control can pick up implicit style here, since Border and Control can work here, our previous hypothesis breaks.-->
                    <
    Control/>
                </
    StackPanel>
            </
    ControlTemplate>
        </
    Page.Resources>
        <
    Control Template="{StaticResource template}"/>
    </
    Page>

       When you have above XAML code runnning in XAMLPad, you will see that StackPanel((or other Panel derivatives), TextBlock, AccessText, ContentPresenter, InkCanvas, Image, Rectangle(or other Shape derivatives), MediaElement et al cannot pick up the implicit styles, well, this gives us an empirical understanding that elements directly derived from FrameworkElement cannot pick up implicit styles when placed inside FrameworkTemplate(DataTemplate, ControlTemplate, ItemsPanelTemplate), but how about Border and Control, those two privileged guys works quite different from their siblings, Anyway, Control derivatives(including Control itself) all work pretty well in this scenario. so question comes, why those aforementioned elements aren't created equal? Can we perceive this aspect of WPF as another example of breaking the principle of consistency.

       Edit: Thanks for Doug's great find, if you look at the implementation of System.Windows.FrameworkElement.FindImplicitStyleResource() method from reflector, you will find that if the elements aren't Controls or Control derivatives, style lookup is limited within the templated parent boundary(In my example, the templated parent is Control), and as to the Border, the Border within the template still cannot pick up its implict style, but since Page's template has Border defined there, and in the Page case, Page is the templated parent, so the implicit style in Page's resources is within the search boundary for Border, so the Border can pick up the style there, to put it simple: the resolution of non-Control element's implicit style are limited within its templated parent boundary.

        But still, my confusion remains, why the logic of style lookup works the way it is now, why grant Controls more privileges than other equal citizens of WPF?

    3/25/2007

    DataBinding & Multi-Threading In WPF [Part Two]

       In my last post, I talked about a solution to overcome the current WPF limitation to enable multi-threaded data binding.

       But there are one of drawback which I introduced in that solution. In that version of BindingCollection, I just simply marshal the collection changed notification back to UI thread, this sounds innocuous, but sometimes, this is not what we really expect, imagine that I have a bunch of listeners listening to BindingCollection.CollectionChanged events, some of them are free-threaded objects, but some of them would be single-threaded, actually CollectionView is the primary listener to this event. free threaded listeners can run on any threads, they are not bound to UI threads, so we shouldn't force them to run in the UI threads, but single-threaded listeners(DispatcherObjects etc) are actually bound to UI threads, so we should let them running inside UI threads as we previously did, to put it simple, we should treat free-threaded listeners and single-threaded listeners differently, so here comes another refined implementation of this collection, this time I call it BindableCollection, because I find that the name of BindingCollection is a bit misleading indeed, It could imply "a collection of Binding" which apparently is not what I want to do with it.

    using System;
    using System.Threading;
    using System.ComponentModel;
    using System.Collections.Generic;
    using System.Windows.Threading;
    using System.Collections.Specialized;
    using System.Collections.ObjectModel;

    namespace Sheva.Windows.Data
    {
        /// <summary>
        /// An ObservableCollection&lt;T&gt; enhanced with capability of free threading.
        /// </summary>
        [Serializable]
        public class BindableCollection<T> : ObservableCollection<T>
        {
            /// <summary>
            /// Initializes a new instance of the<see cref="BindingCollection&lt;T&gt;">BindingCollection</see>.
            /// </summary>
            public BindableCollection() : base() {}

            /// <summary>
            /// Initializes a new instance of the<see cref="BindingCollection&lt;T&gt;">BindingCollection</see>
            /// class that contains elements copied from the specified List&lt;T&gt;.
            /// </summary>
            /// <param name="list">The list from which the elements are copied.</param>
            /// <exception cref="System.ArgumentNullException">The list parameter cannot be null.</exception>
            public BindableCollection(List<T> list) : base(list) {}

            /// <summary>
            /// Initializes a new instance of the<see cref="BindingCollection&lt;T&gt;">BindingCollection</see>
            /// class that contains elements copied from the specified IEnumerable&lt;T&gt;.
            /// </summary>
            /// <param name="list">The list from which the elements are copied.</param>
            /// <exception cref="System.ArgumentNullException">The list parameter cannot be null.</exception>
            public BindableCollection(IEnumerable<T> list)
            {
                if (list == null) throw new ArgumentOutOfRangeException("The list parameter cannot be null.");
                foreach (T item in list)
                {
                    this.Items.Add(item);
                }
            }

            /// <summary>
            /// Occurs when an item is added, removed, changed, moved, or the entire list is refreshed.
            /// </summary>
            public override event NotifyCollectionChangedEventHandler CollectionChanged;

            protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
            {
                if (this.CollectionChanged != null)
                {
                    using (IDisposable disposable = this.BlockReentrancy())
                    {
                        foreach (Delegate del in this.CollectionChanged.GetInvocationList())
                        {
                            NotifyCollectionChangedEventHandler handler = (NotifyCollectionChangedEventHandler)del;
                            DispatcherObject dispatcherInvoker = del.Target as DispatcherObject;
                            ISynchronizeInvoke syncInvoker = del.Target as ISynchronizeInvoke;
                            if (dispatcherInvoker != null)
                            {
                                // We are running inside DispatcherSynchronizationContext,
                                // so we should invoke the event handler in the correct dispatcher.
                                dispatcherInvoker.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(delegate
                                {
                                    handler(this, e);
                                }));
                            }
                            else if (syncInvoker != null)
                            {
                                // We are running inside WindowsFormsSynchronizationContext,
                                // so we should invoke the event handler in the correct context.
                                syncInvoker.Invoke(del, new Object[] { this, e });
                            }
                            else
                            {
                                // We are running in free threaded context, so just directly invoke the event handler.
                                handler(this, e);
                            }
                        }
                    }
                }
            }
        }
    }

      I've update the test sample to include this new version of BindableCollection, you can download it from here.

    attachment:MultiThreadingInWPF.zip


     

    3/24/2007

    DataBinding & Multi-Threading In WPF [Part One]

       One of the scenario I think the current version of WPF hasn't done enough is the multi-threaded data binding. Imagine your UI is data bound to a data source collection whose items are modified(added, removed, cleared etc) from another thread rather than the UI thread. you will get the following exception:

      This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.

       Actually, generic data source collections are actually free-threaded(I mean free-threaded not thread safe), but WPF's implementations of CollectionView(such as BindingListCollectionView for ADO.NET data source and ListCollectionView for general purpose data collections) are actually single-threaded, If you look at the base class of CollectionView, you can see that It directly derives from DispatcherObject, this tells us that CollectionViews are actually Disptacher affinitized, because Dispatchers are tied to the thread which creates them, so CollectionViews end up tying to the their spawning threads. This type of restriction makes multi-threaded data binding a bit more cumbersome and complicated. Beatriz Costa who's working on WPF's data binding feature wrote up a great article ages ago talking about how to do multi-threaded data binding using current version of WPF. She sincerely admit the drawbacks and limitations of current WPF data binding mechanism when it comes to multi-threading. And she came up with an approach to make this type of binding work properly. But one of the things I don't like about her solution is to marshal all the collection modification operations back to the UI thread. I don't think this type of technique can fully leverage the capabilities of worker threads, and much of the benefits inherent with multi-threading are depreciated. How about if the operation you post to UI thread takes a long time to complete, the UI thread will get busy processing this operation, and your UI gets frozen for quite a bit. In this post, I will show another way of doing multi-threaded data binding, and my solution will make as best use of worker threads as possible, enter BindingCollection.

       The following code illustrates the implementation of BindingCollection:

    using System;
    using System.Threading;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Collections.ObjectModel;
    using System.Runtime.CompilerServices;

    namespace Sheva.Windows.Data
    {
        /// <summary>
        /// An ObservableCollection&lt;T&gt; enhanced with cross threads marshaling.
        /// </summary>
        [Serializable]
        public class BindingCollection<T> : ObservableCollection<T>
        {
            private SynchronizationContext creationSyncContext;
            private Thread creationThread;

            /// <summary>
            /// Initializes a new instance of the<see cref="BindingCollection&lt;T&gt;">BindingCollection</see>.
            /// </summary>
            public BindingCollection() : base()
            {
                InstallSynchronizationContext();
            }

            /// <summary>
            /// Initializes a new instance of the<see cref="BindingCollection&lt;T&gt;">BindingCollection</see>
            /// class that contains elements copied from the specified list.
            /// </summary>
            /// <param name="list">The list from which the elements are copied.</param>
            /// <exception cref="System.ArgumentNullException">The list parameter cannot be null.</exception>
            public BindingCollection(List<T> list) : base(list)
            {
                InstallSynchronizationContext();
            }

            protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
            {
                if (this.creationThread == Thread.CurrentThread)
                {
                    // We are running in the same thread, so just directly fire the collection changed notification.
                    this.OnCollectionChangedInternal(e);
                }
                else if (this.creationSyncContext.GetType() == typeof(SynchronizationContext))
                {
                    // We are running in free threaded context, also directly fire the collection changed notification.
                    this.OnCollectionChangedInternal(e);
                }
                else
                {
                    // We are running in WindowsFormsSynchronizationContext or DispatcherSynchronizationContext,
                    // so we should marshal the collection changed notification back to the creation thread.
                    // Note that we should use the blocking SynchronizationContext.Send operation to marshal the call,
                    // because SynchronizationContext.Post will fail under heavy usage scenario.
                    this.creationSyncContext.Send(new SendOrPostCallback(delegate
                    {
                        this.OnCollectionChangedInternal(e);
                    }), null);
                }
            }

            private void InstallSynchronizationContext()
            {
                this.creationSyncContext = SynchronizationContext.Current;
                this.creationThread = Thread.CurrentThread;
            }

            internal void OnCollectionChangedInternal(NotifyCollectionChangedEventArgs e)
            {
                base.OnCollectionChanged(e);
            }
        }
    }

       Note that the reason I use SynchronizationContext rather than Dispatcher here is that I want this collection to be usable in different presentation technologies such as Windows Forms, WPF, and ASP.NET(You might think I am too of frameworkitis, actually I am). I really hope that the logic I create in the OnCollectionChanged method can make into the future implementation of CollectionViews, so that we don't need to worry about the threading problem illustrated above. I've created a simple WPF application to demonstrate how to use this collection, here is the vs project for this little app.

      You can make this test more demanding by pressing the "Add Items" and "Remove Items" buttons multiple times to create more threads for testing.

    3/18/2007

    Preserving Whitespaces Between XAML Tags

    Filipe Fortes just blogs about Collapsed Spaces Around <Run /> issue in WPF, and his workaround is placing the text containing white spaces into the Text attribute, actually this is not a nice way of doing this, and you can actually twick the XAML parser to respect white spaces between XAML tags using the following trick:

    <Pagexmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <
    TextBlock>
    <
    Run xml:space="preserve">Hello XAML !</Run>
    </
    TextBlock>
    </
    Page>
    For complete explanation on xml:space, you can refer to msdn documentation on Whitespace Processing in XAML.

    3/3/2007

    Application.DoEvents In WPF Revisited

        A long time ago, I blogged about how to implement the Application.DoEvents logic in WPF, Windows Forms has this method for the very beginning, but WPF doesn't build this logic into the framework, I think WPF team made a good decision here, because as I've mentioned in my original blog article, nested message pump is evil. But at some rare circumstances, DoEvents can solve some of the problems such as how to expand TreeViewItems as I've blogged ages ago.

       Recently, I just find another way to implement the DoEvents method, and this version of DoEvents only requires a single line of code:

    using System;
    using System.Threading;
    using System.Windows;
    using System.Windows.Threading;

    namespace Sheva.Windows
    {
        /// <summary>
        /// Designates a Windows Presentation Foundation application with added functionalities.
        /// </summary>
        public class WpfApplication : Application
        {
            /// <summary>
            /// Processes all messages currently in the message queue.
            /// </summary>
            /// <remarks>
            /// This method can potentially cause code re-entrancy problem, so use it with great care.
            /// </remarks>
            public static void DoEvents()
            {
                Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { }));
            }
        }
    }

       Actually, Dispatcher.Invoke will do proper nested pumping for you when the priority is anything than DispatcherPriority.Send.

    2/25/2007

    Visual Level Programming vs Logical Level Programming

       Josh Smith recently posts a great article on codeproject on how to write a custom control to enable spell checking suggestions in WPF, and his article is well written and pretty concise indeed, but he's got a problem when doing adorner layer programming, as he asked in this MSDN forum post. Since I also got the similar problem. So I digged in and with the great help of Ian Griffiths, I got the problem sorted out.  

       The problem both Josh and I encounter is something to do with DP value inheritance on those visuals drawn in the adorner layer using visual layer programming technique, first off I don't agree with Josh's comments about the visual tree and adorner layer. Actually those visuals drawn in the adorner layer are also part of the visual tree if you programme it in the proper way, the reason the inherited DP values are "invisible" to the adorning visuals should be something to do with how we add those visuals in the adorner layer as Ian Griffiths suggests. The following code is a simplified version of the custom Adorner which can host a single UIElement properly:

    public class UIElementAdorner : Adorner
    {
        private UIElement child;
        public UIElementAdorner(UIElement element, UIElement adornedElement)
            : base(adornedElement)
        {
            this.child = element;
            base.AddLogicalChild(element);
            base.AddVisualChild(element);
        }

        protected override Size MeasureOverride(Size constraint)
        {
            this.child.Measure(constraint);
            return child.DesiredSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            this.child.Arrange(new Rect(finalSize));
            return finalSize;
        }

        protected override Int32 VisualChildrenCount
        {
            get { return 1; }
        }

        protected override Visual GetVisualChild(Int32 index)
        {
            return this.child;
        }

        protected override IEnumerator LogicalChildren
        {
            get
            {
                ArrayList list = new ArrayList();
                list.Add(this.child);
                return (IEnumerator)list.GetEnumerator();
            }
        }
    }

       The above code demonstrates both on how to define visual tree using visual layer programming and how to define the logical tree. When defining the visual tree, you should override GetVisualChild method and VisualChildrenCount property to enable proper indexing on the hosted visuals, optionally you can make the call to Visual.AddVisualChild method to establish the child-parent relationship for the visual tree of the custom element. this call is mandatory if you are hosting single visual, and you want to enable dependency property inheritance and UI interaction on this visual. If you are hosting multiple visuals, you can use the VisualCollection to establish the children-parent relationship. When defining the logical tree, you should override the LogicalChilden property, and if you are adding single element, you should call the FrameworkElement.AddLogicalChild method, and  if you are adding multiple elements, you can use UIElementCollection to store the logical children.

    So If we use UIElementCollection, the above code can be re-written as follows:

    public class UIElementAdorner : Adorner
    {
        private UIElementCollection children;
        public UIElementAdorner(UIElement element, UIElement adornedElement) : base(adornedElement)
        {
            children = new UIElementCollection(this, this);
            children.Add(element);
        }

        protected override Size MeasureOverride(Size constraint)
        {
            this.children[0].Measure(constraint);
            return this.children[0].DesiredSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            this.children[0].Arrange(new Rect(finalSize));
            return finalSize;
        }

        protected override Int32 VisualChildrenCount
        {
            get { return 1; }
        }

        protected override Visual GetVisualChild(Int32 index)
        {
            return this.children[0];
        }

        protected override IEnumerator LogicalChildren
        {
            get
            {
                return (IEnumerator)children.GetEnumerator();
            }
        }
    }

      If you use VisualCollection instead, the code can be mutated like this:

    public class UIElementAdorner : Adorner
    {
        private UIElement child;
        private VisualCollection visuals;
        public UIElementAdorner(UIElement element, UIElement adornedElement)
            : base(adornedElement)
        {
            visuals = new VisualCollection(this);
            this.child = element;
            visuals.Add(element);
            base.AddLogicalChild(element);
            //base.AddVisualChild(element);
        }

        protected override Size MeasureOverride(Size constraint)
        {
            this.child.Measure(constraint);
            return child.DesiredSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            this.child.Arrange(new Rect(finalSize));
            return finalSize;
        }

        protected override Int32 VisualChildrenCount
        {
            get { return 1; }
        }

        protected override Visual GetVisualChild(Int32 index)
        {
            return this.child;
        }

        protected override IEnumerator LogicalChildren
        {
            get
            {
                ArrayList list = new ArrayList();
                list.Add(this.child);
                return (IEnumerator)list.GetEnumerator();
            }
        }
    }

       As the final words, since UIElementCollection enables both visual tree level and logical tree level programming, It's more appropriate when defining new content model for you cutsom control, because ContentControl, ItemsControl, HeaderedContentControl, and HeaderedItemsControl all don't fit into your peculiar need. But if you want to programme at the visual level to gain better performance, VisualCollection is more appropriate. And also note that if you just hosing single element, VisualCollection and UIElementCollection all seem a bit overkilled, just mind you, a collection object which contains a single object takes much more memory than the single object itself, so when hosting single element, the first code sample makes more sense.

      Last but not least, my greatest thanks is given to Ian Griffiths for his wonderful tips on Visual.AddVisualChild method and overriding LogicalChildren property.

    2/24/2007

    How To Host Top-Level HWNDs In WPF

        Someone asks in MSDN forum on how to host top-level form in WPF, To be pedantic, the System.Windows.Window in WPF is "big" top-level hwnd indeed, and you cannot put another top-level hwnd into this hwnd, this is not a WPF limitation, this is actually a limitation imposed by Win32, but Win32 does allow you to add child hwnds, you can work around this limitation by "transforming" the top-level hwnd into a child hwnd, I've created a custom control which can do this trick:

    using System;
    using System.Windows;
    using System.Windows.Interop;
    using System.Windows.Forms.Integration;
    using System.Runtime.InteropServices;

    namespace Sheva.Windows.Interop
    {
        [System.Windows.Markup.ContentProperty("Child")]
        public class FormHost : HwndHost
        {
            private System.Windows.Forms.Form child;

            public event EventHandler<ChildChangedEventArgs> ChildChanged;

            public System.Windows.Forms.Form Child
            {
                get { return child; }
                set
                {
                    HwndSource ps = PresentationSource.FromVisual(this) as HwndSource;
                    if (ps != null && ps.Handle != IntPtr.Zero)
                        throw new InvalidOperationException("Cannot set the Child property after the layout is done.");
                    System.Windows.Forms.Form oldChild = child;
                    child = value;
                    OnChildChanged(oldChild);
                }
            }

            public Boolean ShowCaption
            {
                get
                {
                    CheckChildValidity();
                    return (GetWindowStyle(Child.Handle) & WindowStyles.WS_BORDER) == WindowStyles.WS_CAPTION;
                }
                set
                {
                    if (child == null)
                    {
                        this.ChildChanged += delegate
                        {
                            if (value)
                            {
                                SetWindowStyle(Child.Handle, GetWindowStyle(Child.Handle) | WindowStyles.WS_CAPTION);
                            }
                            else
                            {
                                SetWindowStyle(Child.Handle, GetWindowStyle(Child.Handle) & ~WindowStyles.WS_CAPTION);
                            }
                        };
                    }
                    else
                    {
                        if (value)
                        {
                            SetWindowStyle(Child.Handle, GetWindowStyle(Child.Handle) | WindowStyles.WS_CAPTION);
                        }
                        else
                        {
                            SetWindowStyle(Child.Handle, GetWindowStyle(Child.Handle) & ~WindowStyles.WS_CAPTION);
                        }
                    }
                }
            }

            protected override HandleRef BuildWindowCore(HandleRef hwndParent)
            {
                CheckChildVadility();
                HandleRef childHwnd = new HandleRef(Child, child.Handle);
                SetWindowStyle(childHwnd.Handle, WindowStyles.WS_CHILD | GetWindowStyle(childHwnd.Handle));
                WindowsFormsHost.EnableWindowsFormsInterop();
                System.Windows.Forms.Application.EnableVisualStyles();
                SetParent(childHwnd.Handle, hwndParent.Handle);
                return childHwnd;
            }

            protected override void DestroyWindowCore(HandleRef hwnd)
            {
                child.Dispose();
            }

            protected void OnChildChanged(System.Windows.Forms.Form oldChild)
            {
                if (this.ChildChanged != null)
                {
                    this.ChildChanged(this, new ChildChangedEventArgs(oldChild));
                }
            }

            private void CheckChildValidity()
            {
                if (child == null || child.Handle == IntPtr.Zero)
                {
                    throw new ArgumentNullException("child form cannot be null");
                }
            }

            public static readonly Int32 GWL_STYLE = -16;
            public static readonly UInt32 WS_CHILD = 0x40000000;

            [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
            internal static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent);
            [DllImport("user32.dll")]
            internal static extern WindowStyles GetWindowLong(IntPtr hWnd, Int32 nIndex);
            [DllImport("user32.dll")]
            internal static extern UInt32 SetWindowLong(IntPtr hWnd, Int32 nIndex, UInt32 dwNewLong);

            internal WindowStyles GetWindowStyle(IntPtr hWnd)
            {
                return (WindowStyles)GetWindowLong(hWnd, GWL_STYLE);
            }

            internal void SetWindowStyle(IntPtr hWnd, WindowStyles windowStyle)
            {
                SetWindowLong(hWnd, GWL_STYLE, (UInt32)windowStyle);
            }
        }

        [Flags]
        internal enum WindowStyles : uint
        {
            WS_OVERLAPPED = 0x00000000,
            WS_POPUP = 0x80000000,
            WS_CHILD = 0x40000000,
            WS_MINIMIZE = 0x20000000,
            WS_VISIBLE = 0x10000000,
            WS_DISABLED = 0x08000000,
            WS_CLIPSIBLINGS = 0x04000000,
            WS_CLIPCHILDREN = 0x02000000,
            WS_MAXIMIZE = 0x01000000,
            WS_BORDER = 0x00800000,
            WS_DLGFRAME = 0x00400000,
            WS_VSCROLL = 0x00200000,
            WS_HSCROLL = 0x00100000,
            WS_SYSMENU = 0x00080000,
            WS_THICKFRAME = 0x00040000,
            WS_GROUP = 0x00020000,
            WS_TABSTOP = 0x00010000,

            WS_MINIMIZEBOX = 0x00020000,
            WS_MAXIMIZEBOX = 0x00010000,

            WS_CAPTION = WS_BORDER | WS_DLGFRAME,
            WS_TILED = WS_OVERLAPPED,
            WS_ICONIC = WS_MINIMIZE,
            WS_SIZEBOX = WS_THICKFRAME,
            WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW,

            WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
            WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU,
            WS_CHILDWINDOW = WS_CHILD,
        }
    }

       The above code also demonstrates how to make custom HwndHost in WPF, note that in the setter of Child property, I've checked to see if the layout is already done, this is important because BuildWindowCore method will be called during the layout pass, and it's meaningless to set the Child property after BuildWindowCore is made. and also note that I make a call to WindowsFormsHost.EnableWindowsFormsInterop() method, this is also pretty important if you want the hosted Form object to properly get notified with the keyboard related messages, because Windows Forms and WPF have different way of implementing message pumping. If you want to know all the details related to how the message loop interop between Windows Forms and WPF works, you can read the WPF documentation article: Windows Forms and WPF Interoperability Input Architecture.

      The usage of FormHost in XAML is pretty straightforward as the following XAML snippet domonstrates:

    <Window x:Class="TestCode.HostingFormDemo"
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
       xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
       
    xmlns:cc="clr-namespace:Sheva.Windows.Interop"
       
    Title="Hosting Form In WPF"
        >
        <
    cc:FormHost ShowCaption="False">
          <
    wf:Form/>
        </
    cc:FormHost>
    </
    Window>

    2/5/2007

    Another Way To Listen To DP Value Change

      Ben Constable just blogs about another elegant way to listen to dependency property change, the trick here is using DependencyPropertyDescriptor, imagine that you have a Label called myLabel, and you want to get notified when it's ContentProperty is changed, then you can do something like the following:

    DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(Label.ContentProperty, typeof(Label));
    if (dpd != null)
    {
        dpd.AddValueChanged(myLabel, delegate
        {
            // Add your logic here to respond to value change.
        });
    }

      The other ways in WPF that you can use to listen to property change is to subclass an existing Control, and override the metadata of a dependency property whose property change you want to listen to, when you do so, you need to specify a PropertyChangedCallback which a delegate to your property change event handler, or instead of using PropertyChangedCallback, you can override the OnPropertyChanged method, and place your logic there, as a bonus, you can get additional information things like which property is changed by examining the value of passed-in DependencyPropertyChangedEventArgs argument's Property property.

    1/27/2007

    XamlHack: A Better XAMLPad

       XamlHack is a little XAML authoring tool which I use in my daily WPF life. whenever I want to author some small pieces of XAML code, I use this tool instead of Visual Studio, becauese this tool is more lightweight and easier to work with when writing some trivial XAML testing code, and the following is some of the features which this version of XamlHack supports:

    1. Basic text editing features & syntax highlighting for XAML

        Writing a text editor which directly supports syntax highlight is really difficult, fortunately SharpDevelop project already has a decent text editing control, so without reinventing the wheel, I use this control instead, so I can get syntax highlighting, line numbering, folding and unfolding etc at free. I also add the search and replace capability which is essential to any text editor.

    2. XAML preview with element tree viewing capability using "Element Tree Explorer"   

       In the XAML preview window(aka the "Design" tab shown below), You can examine the definition of the visual tree and logical tree of the root element. I think this feature is very important, since the element tree is a key player in a lot of WPF features for instance resource lookup, property inheritance and routed events etc. By default, the "Element Tree Explorer" is turened off, you can turn it on by checking it from the "View" menu.

    3. Examine the default value of DPs using "Property Explorer"

        The ability to examine the default value of dependency properties is a vital feature, in particular, when you want to know the default XAML definition for a control's ControlTemplate and Style property, XamlHack has this feature built in, so you don't need to go a long way to use Expression Blend to find out such kind of info, and another plus of this tool is that it enables you to add third party assemblies for examination.

    4. Convert BAML to XAML using "Resource Explorer"

       I use reflector quite a lot to "reverse engineer" the code written by other poeple, reflector can let you decompile the IL code, but not BAML code, so in order to "reverse engineer" BAML code, I create this tool (at the time of this writing, Reflector already has this feature.)

    5. Last but not least, peculiar error warning.

        When your XAML code contains some errors, XamlHack will show you a hilarious error warning message in "Design" tab, and when you click the message, it will turn you back to your xaml code, and directly highlight the source of error for you.

       If XamlHack and the features it provides make some sense to you, you can download it here and have a try on it, any feedback and suggestions are greatly appreciated.

       Attachment: XamlHack.zip