Sandrino's Blog

Life around .NET

New site/blog: http://sharepointserver2010.be

leave a comment

May I introduce our new site/blog: http://sharepointserver2010.be

As you can read on the first blog post this site will be all about SS2010.
You’ll have access to all documents, videos, downloads, … available for SS2010.

Hope to see you soon!

Written by Sandrino

November 19th, 2009 at 11:23 pm

Posted in SharePoint

Tagged with ,

Solving issue in WCF: ‘There are too many pending secure conversations on the server’

leave a comment

For an internet facing service I noticed the following issue: There are too many pending secure conversations on the server
That’s one that slipped through the unit tests…

Solution:

<behaviors>

  <serviceBehaviors>

    <behavior name=serviceBehavior>

      <serviceThrottling maxConcurrentCalls=1000 maxConcurrentSessions=1000 maxConcurrentInstances=1000 />

    </behavior>

  </serviceBehaviors>

</behaviors>

Just modify the limits of your service behavior.

Important: Don’t just put any high number there. These limits should be adapted to your needs.
Read why in the following blog post: http://kennyw.com/indigo/150

Written by Sandrino

September 21st, 2009 at 12:35 pm

Posted in C#, WCF

Tagged with , ,

WCF and fixing client/host time issues (MaxClockSkew) quickly

leave a comment

I have noticed that some of my WCF clients suddenly had issues connecting.
These were the exceptions:

  • The security timestamp is stale because its expiration time is in the past.
  • An error occurred when verifying security for the message

The problem was that the server time had a difference of +/- 10 min. with the clients that had problems.
Since these clients are not part of the domain at all we had a problem, since WCF only allows a 5 min. difference.

This could “easely” be solved with:

  • LocalClientSettings.MaxClockSkew
  • LocalServiceSettings.MaxClockSkew

But this is only supported by custom bindings.
Custom binding doesn’t support properties like readerQuotas … so this creates a new problem.
You could for example add tcpTransport in your custom binding but here again not all the properties are available.
And I’m actually not a fan of having to rewrite all my bindings.

That’s why I liked the following solution (note, this is only when you host your service in an application/service):

ServiceHost service = new ServiceHost(typeof(Calculator));

Binding currentBinding = service.Description.Endpoints[0].Binding;

With this code we get our current binding (presuming the endpoints and bindings are in the App.config).
Then we just have to update the current binding with a custom binding:

// Set the maximum difference in minutes

int maxDifference = 300;

 

// Create a custom binding based on an existing binding

CustomBinding myCustomBinding = new CustomBinding(currentBinding);

 

// Set the maxClockSkew

var security = myCustomBinding.Elements.Find<SymmetricSecurityBindingElement>();

security.LocalClientSettings.MaxClockSkew = TimeSpan.FromMinutes(maxDifference);

security.LocalServiceSettings.MaxClockSkew = TimeSpan.FromMinutes(maxDifference);

 

// Set the maxClockSkew

var secureTokenParams = (SecureConversationSecurityTokenParameters)security.ProtectionTokenParameters;

var bootstrap = secureTokenParams.BootstrapSecurityBindingElement;

bootstrap.LocalClientSettings.MaxClockSkew = TimeSpan.FromMinutes(maxDifference);

bootstrap.LocalServiceSettings.MaxClockSkew = TimeSpan.FromMinutes(maxDifference);

 

// Update the binding of the endpoint

service.Description.Endpoints[0].Binding = myCustomBinding;

The only thing we had to do is create a custom binding based on an existing binding.
After that we adapted the maxClockSkew on the required objects and then injected the new binding back to the endpoint.

If you have multiple endpoints you should apply this on all your endpoints.

Written by Sandrino

September 3rd, 2009 at 2:03 pm

A list of all available DevExpress skins and how to apply/install them

leave a comment

The DevExpress support site shows how to install some skins.
But what are all the available skins (for example in the new release DevExpress 9.2)?

Available skins (these are the correct names to use in your code)

DevExpress.BonusSkins.v9.2

  1. Coffee
  2. Liquid Sky
  3. London Liquid Sky
  4. Glass Oceans
  5. Stardust
  6. Xmas 2008 Blue
  7. Valentine
  8. McSkin
  9. Summer 2008
  10. Pumpkin
  11. Dark Side
  12. Springtime
  13. Darkroom
  14. Foggy
  15. High Contrast
  16. Seven
  17. Seven Classic
  18. Sharp
  19. Sharp Plus

DevExpress.OfficeSkins.v9.2

  1. Office 2007 Blue
  2. Office 2007 Black
  3. Office 2007 Silver
  4. Office 2007 Green
  5. Office 2007 Pink

Installing the skins

First be sure to add a reference to DevExpress.BonusSkins.v9.2 and/or DevExpress.OfficeSkins.v9.2.
After that you can just do the following:

DefaultLookAndFeel defaultSkin = new DefaultLookAndFeel();

defaultSkin.LookAndFeel.SetSkinStyle(“Seven”);

The name of the skin  you want to load should be passed as a string.
After you’ve done that your whole application will be using this skin (except if you have set UseDefaultLookAndFeel to false).

Written by Sandrino

September 3rd, 2009 at 12:55 pm

Almost-automatic INotifyPropertyChanged, automatic IsDirty, automatic ChangeTracking

leave a comment

WPF… it looks like I need to build workarounds for everything I’m trying to do.
Prior to WPF I rarely needed INotifyPropertyChanged.

But since we need it why not make the best of it.
Since I want a base class for all my future objects in all my future projects I want something super clean.

  • Implement INotifyPropertyChanged in the best way possible (refactory friendly and optimal performance)
  • Automatically set the IsDirty flag
  • Automatic change tracking to be able to log what has been changed (like in SharePoint when you get alerts, I love that!)
  • The change tracking should also be exported as text so I can write it to SQL

Of course I don’t have to tell you that the code must always insure a minimal amount of work.
And it should be refactor friendly etc… I want perfect code.

The code

    /// <summary>

    /// This object automatically implements notify property changed

    /// </summary>

    public class NotifyPropertyChangeObject : INotifyPropertyChanged

    {

        /// <summary>

        /// Track changes or not.

        /// If we’re working with DTOs and we fill up the DTO in the DAL we should not be tracking changes.

        /// </summary>

        private bool trackChanges = false;

 

        /// <summary>

        /// Changes to the object

        /// </summary>

        public Dictionary<string, object> Changes { get; private set; }

 

        /// <summary>

        /// Is the object dirty or not?

        /// </summary>

        public bool IsDirty

        {

            get { return Changes.Count > 0; }

            set { ; }

        }

 

        /// <summary>

        /// Event required for INotifyPropertyChanged

        /// </summary>

        public event PropertyChangedEventHandler PropertyChanged;

 

        /// <summary>

        /// This constructor will initialize the change tracking

        /// </summary>

        public NotifyPropertyChangeObject()

        {

            // Change tracking default

            trackChanges = true;

 

            // New change tracking dictionary

            Changes = new Dictionary<string, object>();

        }

 

        /// <summary>

        /// Reset the object to non-dirty

        /// </summary>

        public void Reset()

        {

            Changes.Clear();

        }

 

        /// <summary>

        /// Start tracking changes

        /// </summary>

        public void StartTracking()

        {

            trackChanges = true;

        }

 

        /// <summary>

        /// Stop tracking changes

        /// </summary>

        public void StopTracking()

        {

            trackChanges = false;

        }

 

        /// <summary>

        /// Change the property if required and throw event

        /// </summary>

        /// <param name=”variable”></param>

        /// <param name=”property”></param>

        /// <param name=”value”></param>

        public void ApplyPropertyChange<T, F>(ref F field, Expression<Func<T, object>> property, F value)

        {

            // Only do this if the value changes

            if (field == null || !field.Equals(value))

            {

                // Get the property

                var propertyExpression = GetMemberExpression(property);

                if (propertyExpression == null)

                    throw new InvalidOperationException(“You must specify a property”);

 

                // Property name

                string propertyName = propertyExpression.Member.Name;

 

                // Set the value

                field = value;

 

                // If change tracking is enabled, we can track the changes…

                if (trackChanges)

                {

                    // Change tracking

                    Changes[propertyName] = value;

 

                    // Notify change

                    NotifyPropertyChanged(propertyName);

                }

            }

        }

 

        /// <summary>

        /// Get member expression

        /// </summary>

        /// <typeparam name=”T”></typeparam>

        /// <param name=”expression”></param>

        /// <returns></returns>

        public MemberExpression GetMemberExpression<T>(Expression<Func<T, object>> expression)

        {

            // Default expression

            MemberExpression memberExpression = null;

 

            // Convert

            if (expression.Body.NodeType == ExpressionType.Convert)

            {

                var body = (UnaryExpression)expression.Body;

                memberExpression = body.Operand as MemberExpression;

            }

            // Member access

            else if (expression.Body.NodeType == ExpressionType.MemberAccess)

            {

                memberExpression = expression.Body as MemberExpression;

            }

 

            // Not a member access

            if (memberExpression == null)

                throw new ArgumentException(“Not a member access”, “expression”);

 

            // Return the member expression

            return memberExpression;

        }

 

        /// <summary>

        /// The property has changed

        /// </summary>

        /// <param name=”propertyName”></param>

        private void NotifyPropertyChanged(string propertyName)

        {

            if (PropertyChanged != null)

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

        }

 

        /// <summary>

        /// Convert the changes to an XML string

        /// </summary>

        /// <returns></returns>

        public string ChangesToXml()

        {

            // Prepare base objects

            XDeclaration declaration = new XDeclaration(“1.0″, Encoding.UTF8.HeaderName, String.Empty);

            XElement root = new XElement(“Changes”);

 

            // Create document

            XDocument document = new XDocument(declaration, root);

 

            // Add changes to the document

            // TODO: If it’s an object, maybe do some other things

            foreach (KeyValuePair<string, object> change in Changes)

                root.Add(new XElement(change.Key, change.Value));

 

            // Get the XML

            return document.Document.ToString();

        }

    }

Example

   public class Person : NotifyPropertyChangeObject

    {

        private string firstName;

        private string lastName;

        private int age;

 

        /// <summary>

        /// FirstName

        /// </summary>

        [DefaultValue("")]

        public string FirstName

        {

            get { return firstName; }

            set { ApplyPropertyChange<Person, string>(ref firstName, o => o.FirstName, value); }

        }

 

        /// <summary>

        /// LastName

        /// </summary>

        [DefaultValue("")]

        public string LastName

        {

            get { return lastName; }

            set { ApplyPropertyChange<Person, string>(ref lastName, o => o.LastName, value); }

        }

 

        /// <summary>

        /// Age

        /// </summary>

        [DefaultValue(0)]

        public int Age

        {

            get { return age; }

            set { ApplyPropertyChange<Person, int>(ref age, o => o.Age, value); }

        }

    }

That’s it…

A little word about the code

  • Our class Person inherits from NotifyPropertyChangeObject
  • When we change the value of a property the magic happens
  • We forward the private variable, the (refactor friendly) property, and the new value
  • After that we check if the ‘new’ value matches the old one. If this is the case nothing should happen.
  • If the values do not match, we want to change the value and notify that a change has happened
  • In the meanwhile we also add that change to the Changes dictionary
  • It’s safe to presume that if the dictionary contains changes, the object is dirty

Finally there are the methods Reset, StartTracking, StopTracking.
These have to do with the change tracking.

If I’m filling up a DTO in my DAL I don’t want it to be marked as dirty.
So before I start I call StopTracking, and when I’m done I call StartTracking.

Later on if I save my object I want it to be clean (not dirty), so I call the Reset method.

Finally you could call ChangesToXml to get a string representation of all the changes.
This could the be written to SQL or so…

Example application

At the end of the article you can download a working project.
I’ve also added an example for the cases where you cannot inherit from NotifyPropertyChangeObject.
When you’re working with Entity Framework or LinqToSql for example…

Download: Labs.NotifyProperty.zip

Written by Sandrino

August 26th, 2009 at 11:26 pm

Filtering the WPF DataGrid automatically via the header (inline filtering)

one comment

Problem

Before switching to WPF we used WinForms + DevExpress.
For reasons like performance, time, … I liked DevExpress.

But now with WPF it feels strage to use commercial controls.
I feel like the sky is the limit and I could do whatever we want…

…until I wanted to filter the DataGrid.

I want to achieve something similar that was used in the DevExpress XtraGrid.
That filter was so nice!

Customizing the WPF DataGrid

Ok, I always want the best for my customers so lets do something nicer than the default thingies.
I don’t know what UX article it was from but I read that controls should not pollute the UI.
They should only be visible when needed. And that what we’re going to build.

Normal state

 

Filtering (mouse over)

 

Mouse out (after the filter is applied)

The grid will only show the filter textbox when hovering the header.
Once the filter is applied (automatically) the header text itself will update automatically.

Getting started

We’ll need to do the following:

  • Create a lookless grid control that implements this filtering functionality (FilterDataGrid)
  • Create a nice style for the header of the grid
  • Create a converter that will format our header text

Creating the lookless control

Here is the code: 

    /// <summary>

    /// A grid that makes inline filtering possible.

    /// </summary>

    public class FilteringDataGrid : DataGrid

    {

        /// <summary>

        /// This dictionary will have a list of all applied filters

        /// </summary>

        private Dictionary<string, string> columnFilters;

 

        /// <summary>

        /// Cache with properties for better performance

        /// </summary>

        private Dictionary<string, PropertyInfo> propertyCache;

 

        /// <summary>

        /// Case sensitive filtering

        /// </summary>

        public static DependencyProperty IsFilteringCaseSensitiveProperty =

             DependencyProperty.Register(“IsFilteringCaseSensitive”, typeof(bool), typeof(FilteringDataGrid), new PropertyMetadata(true));

 

        /// <summary>

        /// Case sensitive filtering

        /// </summary>

        public bool IsFilteringCaseSensitive

        {

            get { return (bool)(GetValue(IsFilteringCaseSensitiveProperty)); }

            set { SetValue(IsFilteringCaseSensitiveProperty, value); }

        }

 

        /// <summary>

        /// Register for all text changed events

        /// </summary>

        public FilteringDataGrid()

        {

            // Initialize lists

            columnFilters = new Dictionary<string, string>();

            propertyCache = new Dictionary<string, PropertyInfo>();

 

            // Add a handler for all text changes

            AddHandler(TextBox.TextChangedEvent, new TextChangedEventHandler(OnTextChanged), true);

 

            // Datacontext changed, so clear the cache

            DataContextChanged += new DependencyPropertyChangedEventHandler(FilteringDataGrid_DataContextChanged);

        }

 

        /// <summary>

        /// Clear the property cache if the datacontext changes.

        /// This could indicate that an other type of object is bound.

        /// </summary>

        /// <param name=”sender”></param>

        /// <param name=”e”></param>

        private void FilteringDataGrid_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)

        {

            propertyCache.Clear();

        }

 

        /// <summary>

        /// When a text changes, it might be required to filter

        /// </summary>

        /// <param name=”sender”></param>

        /// <param name=”e”></param>

        private void OnTextChanged(object sender, TextChangedEventArgs e)

        {

            // Get the textbox

            TextBox filterTextBox = e.OriginalSource as TextBox;

 

            // Get the header of the textbox

            DataGridColumnHeader header = TryFindParent<DataGridColumnHeader>(filterTextBox);

            if (header != null)

            {

                UpdateFilter(filterTextBox, header);

                ApplyFilters();

            }

        }

 

        /// <summary>

        /// Update the internal filter

        /// </summary>

        /// <param name=”textBox”></param>

        /// <param name=”header”></param>

        private void UpdateFilter(TextBox textBox, DataGridColumnHeader header)

        {

            // Try to get the property bound to the column.

            // This should be stored as datacontext.

            string columnBinding = header.DataContext != null ? header.DataContext.ToString() : “”;

 

            // Set the filter

            if (!String.IsNullOrEmpty(columnBinding))

                columnFilters[columnBinding] = textBox.Text;

        }

 

        /// <summary>

        /// Apply the filters

        /// </summary>

        /// <param name=”border”></param>

        private void ApplyFilters()

        {

            // Get the view

            ICollectionView view = CollectionViewSource.GetDefaultView(ItemsSource);

            if (view != null)

            {

                // Create a filter

                view.Filter = delegate(object item)

                {

                    // Show the current object

                    bool show = true;

 

                    // Loop filters

                    foreach (KeyValuePair<string, string> filter in columnFilters)

                    {

                        object property = GetPropertyValue(item, filter.Key);

                        if (property != null)

                        {

                            // Check if the current column contains a filter

                            bool containsFilter = false;

                            if (IsFilteringCaseSensitive)

                                containsFilter = property.ToString().Contains(filter.Value);

                            else

                                containsFilter = property.ToString().ToLower().Contains(filter.Value.ToLower());

 

                            // Do the necessary things if the filter is not correct

                            if (!containsFilter)

                            {

                                show = false;

                                break;

                            }

                        }

                    }

 

                    // Return if it’s visible or not

                    return show;

                };

            }

        }

 

        /// <summary>

        /// Get the value of a property

        /// </summary>

        /// <param name=”item”></param>

        /// <param name=”property”></param>

        /// <returns></returns>

        private object GetPropertyValue(object item, string property)

        {

            // No value

            object value = null;

 

            // Get property  from cache

            PropertyInfo pi = null;

            if (propertyCache.ContainsKey(property))

                pi = propertyCache[property];

            else

            {

                pi = item.GetType().GetProperty(property);

                propertyCache.Add(property, pi);

            }

 

            // If we have a valid property, get the value

            if (pi != null)

                value = pi.GetValue(item, null);

 

            // Done

            return value;

        }

 

        /// <summary>

        /// Finds a parent of a given item on the visual tree.

        /// </summary>

        /// <typeparam name=”T”>The type of the queried item.</typeparam>

        /// <param name=”child”>A direct or indirect child of the queried item.</param>

        /// <returns>The first parent item that matches the submitted

        /// type parameter. If not matching item can be found, a null reference is being returned.</returns>

        public static T TryFindParent<T>(DependencyObject child)

          where T : DependencyObject

        {

            //get parent item

            DependencyObject parentObject = GetParentObject(child);

 

            //we’ve reached the end of the tree

            if (parentObject == null) return null;

 

            //check if the parent matches the type we’re looking for

            T parent = parentObject as T;

            if (parent != null)

            {

                return parent;

            }

            else

            {

                //use recursion to proceed with next level

                return TryFindParent<T>(parentObject);

            }

        }

 

        /// <summary>

        /// This method is an alternative to WPF’s

        /// <see cref=”VisualTreeHelper.GetParent”/> method, which also

        /// supports content elements. Do note, that for content element,

        /// this method falls back to the logical tree of the element.

        /// </summary>

        /// <param name=”child”>The item to be processed.</param>

        /// <returns>The submitted item’s parent, if available. Otherwise null.</returns>

        public static DependencyObject GetParentObject(DependencyObject child)

        {

            if (child == null) return null;

            ContentElement contentElement = child as ContentElement;

 

            if (contentElement != null)

            {

                DependencyObject parent = ContentOperations.GetParent(contentElement);

                if (parent != null) return parent;

 

                FrameworkContentElement fce = contentElement as FrameworkContentElement;

                return fce != null ? fce.Parent : null;

            }

 

            // If it’s not a ContentElement, rely on VisualTreeHelper

            return VisualTreeHelper.GetParent(child);

        }

    }

First I want to thank Philipp Sumi for his snippet for finding ancestors of dependency objects.
I have used his 2 methods TryFindParent and GetParentObject that you can find at the end of the class.

But let’s get to work now, this is how it works:

  • The FilteringDataGrid exposes 1 DependencyProperty: IsFilteringCaseSensitive.
    This is just a flag to do case sensitive checks or not on the filtering.
  • When the grid is initialized we also initialize 2 dictionaries. More about this later.
    The most important thing happening in the constructor is binding to all TextChanged events.
  • Once some text changes in any TextBox in the grid, this event will happen.
    Thats why in the method OnTextChanged we only want to process TextBoxes that are present in the DataGridColumnHeader.
  • If this TextBox is in the DataGridColumnHeader we can process.
    By using the DataContext on the header we’ll find the name of the property that is bound to the current column.
    That is where our 2 lists come in handy.
  • First we’ll use the columnsFilter. This is a Dictionary that will keep track of all the current properties with all their current filters.
    This is important to know because one could want to filter on multiple columns at the same time.
  • After we updated the columnsFilter Dictionary we’ll want to apply the whole filter to all the rows.
    This is done using the ICollectionView interface.
  • This is where the propertyCache comes in handy. We’re actually using reflection to get the property of the object using the property name.
    After that we’ll get the value of the current object for that property and we’ll check if it contains the value from our filter.
    But since reflection is so heavy I want to use some form of cache. If we know the object type we could store the property that matches a certain name.
    Doing that we won’t always need to do item.GetType().GetProperty().
  • And once the filtering is done for each object (including case sensitive checks or not) we’re done.
  • Just a last remark.
    If the DataContext changes (from List<Country> to List<City> for example) we’ll want to clear the propertyCache.
    Because if we store the property “Name” of Country, and later on we want to use that property to get the value of a City object we’ll get exceptions.

Well well, we got us a nice filter control.
But this controll will not show any filter in the header. We still need to apply some styling.

You can style however you want, the only requirement is that you put a TextBox in the DataGridColumnHeader.
You might ask yourself why I’m not using things like PART_filterControl or so but the thing is we don’t have access to the actualy DataGridColumnHeader.
We only have access to its style from the DataGrid itself.

Styling the FilterDataGrid

First some XAML…

<local:HeaderFilterConverter x:Key=headerConverter/>

 

<Style TargetType={x:Type my:DataGridColumnHeader}>

  <Setter Property=VerticalContentAlignment Value=Center/>

  <Setter Property=Template>

    <Setter.Value>

      <ControlTemplate TargetType={x:Type my:DataGridColumnHeader}>

        <ControlTemplate.Resources>

          <Storyboard x:Key=ShowFilterControl>

            <ObjectAnimationUsingKeyFrames BeginTime=00:00:00 Storyboard.TargetName=filterTextBox Storyboard.TargetProperty=(UIElement.Visibility)>

              <DiscreteObjectKeyFrame KeyTime=00:00:00 Value={x:Static Visibility.Visible}/>

              <DiscreteObjectKeyFrame KeyTime=00:00:00.5000000 Value={x:Static Visibility.Visible}/>

            </ObjectAnimationUsingKeyFrames>

            <ColorAnimationUsingKeyFrames BeginTime=00:00:00 Storyboard.TargetName=filterTextBox Storyboard.TargetProperty=(Panel.Background).(SolidColorBrush.Color)>

              <SplineColorKeyFrame KeyTime=00:00:00 Value=Transparent/>

              <SplineColorKeyFrame KeyTime=00:00:00.5000000 Value=White/>

            </ColorAnimationUsingKeyFrames>

          </Storyboard>

          <Storyboard x:Key=HideFilterControl>

            <ObjectAnimationUsingKeyFrames BeginTime=00:00:00 Storyboard.TargetName=filterTextBox Storyboard.TargetProperty=(UIElement.Visibility)>

              <DiscreteObjectKeyFrame KeyTime=00:00:00.4000000 Value={x:Static Visibility.Collapsed}/>

            </ObjectAnimationUsingKeyFrames>

            <ColorAnimationUsingKeyFrames BeginTime=00:00:00 Storyboard.TargetName=filterTextBox Storyboard.TargetProperty=(UIElement.OpacityMask).(SolidColorBrush.Color)>

              <SplineColorKeyFrame KeyTime=00:00:00 Value=Black/>

              <SplineColorKeyFrame KeyTime=00:00:00.4000000 Value=#00000000/>

            </ColorAnimationUsingKeyFrames>

          </Storyboard>

        </ControlTemplate.Resources>

        <my:DataGridHeaderBorder x:Name=dataGridHeaderBorder Margin=0 VerticalAlignment=Top Height=31 IsClickable={TemplateBinding CanUserSort} IsHovered={TemplateBinding IsMouseOver} IsPressed={TemplateBinding IsPressed} SeparatorBrush={TemplateBinding SeparatorBrush} SeparatorVisibility={TemplateBinding SeparatorVisibility} SortDirection={TemplateBinding SortDirection} Background={TemplateBinding Background} BorderBrush={TemplateBinding BorderBrush} BorderThickness={TemplateBinding BorderThickness} Padding={TemplateBinding Padding} Grid.ColumnSpan=1>

          <Grid x:Name=grid Width=Auto Height=Auto RenderTransformOrigin=0.5,0.5>

            <Grid.RenderTransform>

              <TransformGroup>

                <ScaleTransform/>

                <SkewTransform/>

                <RotateTransform/>

                <TranslateTransform/>

              </TransformGroup>

            </Grid.RenderTransform>

            <Grid.ColumnDefinitions>

              <ColumnDefinition Width=*/>

            </Grid.ColumnDefinitions>

            <ContentPresenter x:Name=contentPresenter

                    HorizontalAlignment={TemplateBinding HorizontalContentAlignment}

                    VerticalAlignment={TemplateBinding VerticalContentAlignment}

                    SnapsToDevicePixels={TemplateBinding SnapsToDevicePixels}

                    ContentStringFormat={TemplateBinding ContentStringFormat}

                    ContentTemplate={TemplateBinding ContentTemplate}>

              <ContentPresenter.Content>

                <MultiBinding Converter={StaticResource headerConverter}>

                  <MultiBinding.Bindings>

                    <Binding ElementName=filterTextBox Path=Text />

                    <Binding RelativeSource={RelativeSource TemplatedParent} Path=Content />

                  </MultiBinding.Bindings>

                </MultiBinding>

              </ContentPresenter.Content>

            </ContentPresenter>

            <TextBox x:Name=filterTextBox HorizontalAlignment=Right MinWidth=25 Height=Auto OpacityMask=Black Visibility=Collapsed Text=“” TextWrapping=Wrap Grid.Column=0 Grid.ColumnSpan=1/>

          </Grid>

        </my:DataGridHeaderBorder>

        <ControlTemplate.Triggers>

          <Trigger Property=IsMouseOver Value=True>

            <Trigger.EnterActions>

              <BeginStoryboard x:Name=ShowFilterControl_BeginStoryboard Storyboard={StaticResource ShowFilterControl}/>

              <StopStoryboard BeginStoryboardName=HideFilterControl_BeginShowFilterControl/>

            </Trigger.EnterActions>

            <Trigger.ExitActions>

              <BeginStoryboard x:Name=HideFilterControl_BeginShowFilterControl Storyboard={StaticResource HideFilterControl}/>

              <StopStoryboard BeginStoryboardName=ShowFilterControl_BeginStoryboard/>

            </Trigger.ExitActions>

          </Trigger>

        </ControlTemplate.Triggers>

      </ControlTemplate>

    </Setter.Value>

  </Setter>

</Style>

Ok that’s a big one.
Let me explain in a few words what we’re doing.

The thing is we have to modify the style of the header.
We actually modify the DataGridHeaderBorder, this one contains the ContentPresenter.

Well, the ContentPresenter is wrapped in a grid with 2 rows.
Row 0 contains the ContentPresenter and Row 1 contains the filterTextBox.

And if you pay close attention you’ll see that the content of the ContentPresenter contains a multi binding.
This is set to the name of the column itself and the filter. Well see about this in the next section.

And finally in the styling of our control we have some triggers that will cause a nice fade-in/fade-out effect on the filter TextBox.
If you enter the DataGridColumnHeader with your mouse the filterTextBox will appear after 0.5 sec. If you mouse out, it will disappear after 0.5 sec.

Styling the header even more

The final thing we want to do is show the status of the current filters in the headers.
Because when we move away with our mouse the filterTextBox is gone and we want to know if the grid is filtered or not.

That’s why we want the header to show if a filter is enabled.
Like this:

Things we’ll have to do:

  • Bind the filter text to the header content
  • Bind the name/header of the column to the header content
  • Apply some formatting to the displayed text (bold)

For this I’ve created a HeaderFilterConverter. This class implements the IMultiValueConverter interface.
This means you can input multiple values and return 1 value.

What we’ll do is, we’ll pass the header/name of the column (eg: Name) and the current filter (eg: San) and we’ll return a TextBlock.
This TextBlock should look like this “Name (Filter: San)”. If there is no text at all it should only display the header/column text.

For this will use an interesting technique where we’ll create some XAML code and convert it to an actual UI element.
An example of this dynamic XAML can also be found on: http://msdn.microsoft.com/en-us/library/dd894487(VS.95).aspx

    /// <summary>

    /// This converter will:

    ///  – Take the header

    ///  – Take the filtered word (if any)

    ///  – Add ‘(Filter: (bold)x(/bold))’ to the header

    /// </summary>

    public class HeaderFilterConverter : IMultiValueConverter

    {

        /// <summary>

        /// Create a nice looking header

        /// </summary>

        /// <param name=”values”></param>

        /// <param name=”targetType”></param>

        /// <param name=”parameter”></param>

        /// <param name=”culture”></param>

        /// <returns></returns>

        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            // Get values

            string filter = values[0] as string;

            string headerText = values[1] as string;

 

            // Generate header text

            string text = “{0}{3}” + headerText + ” {4}”;

            if (!String.IsNullOrEmpty(filter))

                text += “(Filter: {2}” + values[0] + “{4})”;

            text += “{1}”;

 

            // Escape special XML characters like <>&’

            text = new System.Xml.Linq.XText(text).ToString();

 

            // Format the text

            text = String.Format(text,

             @”<TextBlock xmlns=’http://schemas.microsoft.com/winfx/2006/xaml/presentation’>”,

             “</TextBlock>”, “<Run FontWeight=’bold’ Text=’”, “<Run Text=’”, @”‘/>”);

 

            // Convert to stream

            MemoryStream stream = new MemoryStream(ASCIIEncoding.UTF8.GetBytes(text));

 

            // Convert to object

            TextBlock block = (TextBlock)System.Windows.Markup.XamlReader.Load(stream);

            return block;

        }

 

        /// <summary>

        /// Not required

        /// </summary>

        /// <param name=”value”></param>

        /// <param name=”targetTypes”></param>

        /// <param name=”parameter”></param>

        /// <param name=”culture”></param>

        /// <returns></returns>

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)

        {

            throw new NotImplementedException();

        }

    }

Todo

Take converters into account.
Because it could be that the data in the grid does not match the data the user actually sees.

Downloadable sample

I’ve also prepared a fully working downloadable sample.
In this sample you’ll have a grid with 1000 random items where you can see the filter in action.

It also includes all the code from this guide.

Download Labs.Filtering.zip

Written by Sandrino

August 25th, 2009 at 9:37 pm

Set the default value of a dependency property

leave a comment

While creating a new control I wanted to set the value of a bool DependencyProperty to true.
I hoped to use [DefaultValue] on the property that invokes the DependencyProperty but this dit not work.

And it’s not abnormal that this didn’t work.
WPF does not go through the getters/setters, it talks to the DependencyProperty directly.

So after searching a little bit I found the following:

public static DependencyProperty IsFilteringCaseSensitiveProperty =
     DependencyProperty.Register(”IsFilteringCaseSensitive”, typeof(bool), typeof(FilteringDataGrid), new PropertyMetadata(
true));

You have to add the default value in the PropertyMetadata of your dependency property.

Written by Sandrino

August 25th, 2009 at 1:57 pm

Automatically validating business entities in WPF using custom binding and attributes.

2 comments

I like WPF.
I like MVVM.
I like non-repetitive code.
I don’t like how WPF validation fits in this story.

Validation implementation 1: Validation rules in XAML

<TextBox Name=textSandrino>

  <Binding Path=Name>

    <Binding.ValidationRules>

      <MustBeANumberValidationRule />

    </Binding.ValidationRules>

  </Binding>

</TextBox>

Jeah nice… not.

This has several downsides:

  • Your business logic leaks to the UI. Very bad!
  • A validation rule inherits from System.Windows.Controls.ValidationRule
    Even if I could create some validation rules in the BLL, this would still be based on a UI object. Not clean.
  • What about existing validation code if I’m migrating?
    Do I have to rewrite everything to fit the ValidationRule logic? Crazy…
  • Also, this code is hard to manage.

Validation implementation 2: Exceptions in the setters

public class User

{

    private int _age;

    public int Age

    {

        get { return _age; }

        set

        {

            if (value < 21)

                throw new ArgumentException(“Kid!”);

            _age = value;

        }

    }

}

<TextBox Name=textSandrino>

  <Binding Path=Age>

    <Binding.ValidationRules>

      <ExceptionValidationRule />

    </Binding.ValidationRules>

  </Binding>

</TextBox>

I’m almost sure that getters and setters should not throw Exceptions (did I read it in Framework Design Guidelines?).
Personally I’m ok with the fact that methods throw Exceptions. But setters? This should only set values.
With a minimum amount of processing logic (like INotifyPropertyChanged).

An other downside of this is that a big part of your business logic will be in the setter.
That’s not clean enough for me.

Validation implementation 3: IDataErrorInfo

public class User : IDataErrorInfo

{

    private string _name;

    public string Name

    {

        get { return _name; }

        set { _name = value; }

    }

 

    public string Error

    {

        get

        {

            return this[string.Empty];

        }

    }

 

    public string this[string propertyName]

    {

        get

        {

            string result = string.Empty;

            if (propertyName == “Name”)

            {

                if (string.IsNullOrEmpty(this.Name))

                    result = “Name cannot be empty!”;

            }

            return result;

        }

    }

}

Are you kidding me?

  • Based on the property name? That’s so 90’s, not refactor friendly at all.
  • How will this integrate with my current validation logic (if I’m migrating from WinForms projects for example)?
  • This code looks dirty. I want clean code.

I have seen that there has been an attempt by Josh Smith to implement this in MVVM. Sorry Josh I like your work but this is even worse.
I can’t live with the fact that I should implement my validation in the Model and in the ViewModel in such a way.
Writing this type of validation once is even too much! Imagine writing this twice.

A cleaner solution

Ok. This is what I would call clean.

public class Person

{

    [TextValidation(MinLength=5)]

    public string Name { get; set; }

 

    [NumberValidation]

    public string Age { get; set; }

}

What this sollution should provide:

  • Centralize all the validation logic in 1 place
  • Be able to use this on the client side (WPF UI) but also on the server side (BLL)
  • No repetitive code
  • Nothing string based

We will be using the BindingDecoratorBase from Philipp Sumi.
This class is far more better than the native BindingBase in .NET. Great job!

Writing our own validation logic

interface IValidationRule

{

    void Validate(object value, out bool isValid, out string errorMessage);

}

Not much to say about this interface.
This will be the base of our validation logic.

Creating the attributes

public class NumberValidationAttribute : Attribute, IValidationRule

{

    public NumberValidationAttribute()

    {

 

    }

 

    public void Validate(object value, out bool isValid, out string errorMessage)

    {

        double result = 0;

        isValid = double.TryParse(value.ToString(), out result);

        errorMessage = “”;

 

        if (!isValid)

            errorMessage = value + ” is not a valid number”;

    }

}

 

public class TextValidationAttribute : Attribute, IValidationRule

{

    public int MinLength { get; set; }

 

    public TextValidationAttribute()

    {

 

    }

 

    public void Validate(object value, out bool isValid, out string errorMessage)

    {

        isValid = false;

        errorMessage = “”;

 

        if (value != null && value.ToString().Length >= MinLength)

            isValid = true;

 

        if (!isValid)

            errorMessage = value + ” is not equal to or longer than “ + MinLength;

    }

}

Nothing magic here either.
These 2 attribute classes will make it possible to decorate our model properties with rules.

But it also makes it possible to configure the validation attributes (see MinLength in TextValidationAttribute).

Using the validation attributes

public class Person

{

    [TextValidation(MinLength=5)]

    public string Name { get; set; }

 

    [NumberValidation]

    public string Age { get; set; }

 

    [NumberValidation]

    [TextValidation(MinLength=2)]

    public string Money { get; set; }

}

How clean is this?
We can:

  • Apply the validation in a very clean way.
  • Only write the validation attribute once and reuse it multiple times.
  • Configure the validation attribute in a clean way!
  • Use more than one validation attribute per property.

I like!

But how can we integrate our custom validation attributes with WPF?

Integrating with WPF

You should first take a look at Philipp’s article.
With his BindingDecoratorBase we’ll add some custom binding to our solution.

But first we’ll create a WPF ValidationRule that can use our own validation attributes.

class GenericValidationRule : ValidationRule

{

    private IValidationRule ValidationRule;

 

    public GenericValidationRule(IValidationRule validationRule)

    {

        this.ValidationRule = validationRule;

        this.ValidatesOnTargetUpdated = true;

    }

 

    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)

    {

        bool isValid = false;

        string errorMessage = “”;

 

        ValidationRule.Validate(value, out isValid, out errorMessage); 

        ValidationResult result = new ValidationResult(isValid, errorMessage);

        return result;

    }

}

Ok just a small word about this class.
If you take a close look this WPF ValidationRule will absorb our own IValidationRule (that is used by the attributes).
It will use the Validate method from the interface and return a WPF compatible response.

Also have you seen ValidatesOnTargetUpdated?
This is a nice property. This will cause the control to validate immediately when it’s data bound.
This means even before the user adds some input.

Now that we have this rule let’s bind our IValidationRules with WPF controls automatically!

/// <summary>

/// Binding that will automatically implement the validation

/// </summary>

public class ValidationBinding : BindingDecoratorBase

{

    public ValidationBinding()

        : base()

    {

        Binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

    }

 

    /// <summary>

    /// This method is being invoked during initialization.

    /// </summary>

    /// <param name=”provider”>Provides access to the bound items.</param>

    /// <returns>The binding expression that is created by the base class.</returns>

    public override object ProvideValue(IServiceProvider provider)

    {

        // Get the binding expression

        object bindingExpression = base.ProvideValue(provider);

 

        // Bound items

        DependencyObject targetObject;

        DependencyProperty targetProperty;

 

        // Try to get the bound items

        if (TryGetTargetItems(provider, out targetObject, out targetProperty))

        {

            if (targetObject is FrameworkElement)

            {

                // Get the element and implement datacontext changes

                FrameworkElement element = targetObject as FrameworkElement;

                element.DataContextChanged += new DependencyPropertyChangedEventHandler(element_DataContextChanged);

 

                // Set the template

                ControlTemplate controlTemplate = element.TryFindResource(“validationTemplate”) as ControlTemplate;

                if (controlTemplate != null)

                    Validation.SetErrorTemplate(element, controlTemplate);

            }

        }

 

        // Go on with the flow

        return bindingExpression;

    }

 

    /// <summary>

    /// Datacontext of the control has changed

    /// </summary>

    /// <param name=”sender”></param>

    /// <param name=”e”></param>

    private void element_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)

    {

        object datacontext = e.NewValue;

        if (datacontext != null)

        {

            PropertyInfo property = datacontext.GetType().GetProperty(Binding.Path.Path);

            if (property != null)

            {

                IEnumerable<object> attributes = property.GetCustomAttributes(true).Where(o => o is IValidationRule);

                foreach (IValidationRule validationRule in attributes)

                    ValidationRules.Add(new GenericValidationRule(validationRule));

            }

        }

    }

}

Ok let me just run through the code:

  • Inherit from Philipp’s class
  • Validation should happen when the property changes (instant validation)
  • ProvideValue will help us detect the control that received the binding.
    On that control we’ll watch out when the datacontext changes

Now, once the datacontext is set on the control we’ll get started.
Using Binding.Path.Path we know what property we bound to the data context.

Imagine I’m setting Person as datacontext of the Window. Then the DataContextChanged will also launch on the TextBox for example.
And if the binding of that TextBox is set to a property of that class we can get the attributes!

This is what happens in element_DataContextChanged. We get the property that matches the one of the data context.
Once we have that property we  try to find the attributes we are looking for and we add these as validation rules to the control.

Also note that I automatically set the validation template of the control.
This again reduces the ugly repetitive code.  This could also be implemented with a style.

Implementing it

There isn’t much to do.

public class Person

{

    [TextValidation(MinLength=5)]

    public string Name { get; set; }

 

    [NumberValidation]

    public string Age { get; set; }

 

    [NumberValidation]

    [TextValidation(MinLength=2)]

    public string Money { get; set; }

}

 

<TextBox Height=23 Margin=38,34,0,0 Name=textBox1 Text={local:ValidationBinding Path=Name} VerticalAlignment=Top HorizontalAlignment=Left Width=170 />

That’s it!

The only small downside is that if you’re using MVVM you have to add the attributes both in your Model and ViewModel.
But there are also ways around that.

Download: Working sample

Written by Sandrino

August 21st, 2009 at 7:23 pm

Posted in WPF

SelectedValuesListBox – A ListBox that implements more than one selected value

leave a comment

My main occupation is writing data driven applications and recently I have moved from Windows Forms to WPF.
Since WPF provides a great databinding mechanism I want to use it for 100%.

In my case I had the following problem: imagine that the end user can manage a list of Languages.
He would also have a list of patients and for each of these patients he would select one or more languages.

Example:

Sandrino: Dutch, French, Italian, English
John: English
Bernard: French
William: Dutch, French

This would require the following classes:

    public class Language

    {

        public Guid ID { get; set; }

        public string Name { get; set; }

    }

 

    public class Patient

    {

        public string Name { get; set; }

        public List<Guid> LanguageIDs { get; set; }

 

        public Patient(string name)

        {

            Name = name;

            LanguageIDs = new List<Guid>();

        }

    }

Now let’s say I bind a ListBox with a List.
Here comes the problem, how will I set the selection on the ListBox using databinding?
Since I’m using a list of Guid in the Patient I cannot set the SelectedItems of the ListBox.

Half solution..

The solution would be to have a list of Languages in the patient.
But since I’m using this in a client/server environment this will have performance issue.
Imagine that the user gets a list of 1000 Patients and for each patient 2 Languages need to be downloaded.
This is not acceptable.

I want to be able to download the list of languages once and bind it to a ListBox.
After that I want to bind a list of Guids to the ListBox and poof, the selection should happen automatically.

ListBox and SelectedValuePath

The original ListBox almost solved my problem.
By setting the SelectedValuePath like this:

<ListBox ItemsSource={Binding Path=Languages} SelectedValuePath=ID SelectionMode=Multiple />

This made it possible to do ListBox.SelectedValue and use it in code or through binding.
The problem here is that I only have access to 1 selected item.

Again this is not acceptable since the requirement is that for each patient a user could select multiple languages.

Enters… SelectedValuesListBox
That’s why I created a new control based on a ListBox.
It exposes a new DependencyProperty: SelectedValues.

This makes the following possible:

  1. Bind ListBox.ItemsSource to a list of Languages
  2. Set the SelectedValuePath to ID
  3. Bind the ListBox.SelectedValues to a List
  4. All the items in the ListBox that have an ID in the SelectedValues list will be selected

    public class SelectedValuesListBox : ListBox

    {

        /// <summary>

        /// Use the monitor to prevent stack overflow exceptions

        /// </summary>

        private bool monitor = true;

 

        /// <summary>

        /// The dependency property for selected values

        /// </summary>

        public static readonly DependencyProperty SelectedValuesProperty =

            DependencyProperty.RegisterAttached(“SelectedValues”, typeof(IList),

            typeof(SelectedValuesListBox), new PropertyMetadata(OnValuesChanged));

 

        /// <summary>

        /// Property for the control

        /// </summary>

        public IList SelectedValues

        {

            get { return (IList)GetValue(SelectedValuesProperty); }

            set { SetValue(SelectedValuesProperty, value); }

        }

 

        /// <summary>

        /// When a list is bound to the control, tell the control to set all items as selected.

        /// </summary>

        /// <param name=”dependencyObject”></param>

        /// <param name=”e”></param>

        private static void OnValuesChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)

        {

            // Set the selections in the control the first time the list is bound

            SelectedValuesListBox multi = dependencyObject as SelectedValuesListBox;

            multi.SetSelected(e.NewValue as IList);

 

            // For each change in the bound list, change the selection in the control

            if (e.NewValue is INotifyPropertyChanged)

                (e.NewValue as INotifyPropertyChanged).PropertyChanged +=

                  (dependencyObject as SelectedValuesListBox).MultiList_PropertyChanged;

        }

 

        /// <summary>

        /// This constructor will make sure the selection changes in the control are reflected in the bound list

        /// </summary>

        public SelectedValuesListBox()

        {

            SelectionChanged += new SelectionChangedEventHandler(MultiList_SelectionChanged);

        }

 

        /// <summary>

        /// This event will send the selection changes from the control to the bound list

        /// </summary>

        /// <param name=”sender”></param>

        /// <param name=”e”></param>

        private void MultiList_SelectionChanged(object sender, SelectionChangedEventArgs e)

        {

            if (monitor && !String.IsNullOrEmpty(SelectedValuePath))

            {

                try

                {

                    monitor = false;

 

                    // Reset the selected values property

                    SelectedValues.Clear();

 

                    // Loop each selected item

                    // Add the value to the list based on the selected value path

                    foreach (object item in SelectedItems)

                    {

                        PropertyInfo property = item.GetType().GetProperty(SelectedValuePath);

                        if (property != null)

                            SelectedValues.Add(property.GetValue(item, null));

                    }

                }

                catch

                {

                    throw;

                }

                finally

                {

                    monitor = true;

                }

            }

        }

 

        /// <summary>

        /// There was a change in the list that was bound.

        /// Change the selection of the items in the control!

        /// </summary>

        /// <param name=”sender”></param>

        /// <param name=”e”></param>

        protected void MultiList_PropertyChanged(object sender, PropertyChangedEventArgs e)

        {

            SetSelected(sender as IList);

        }

 

        /// <summary>

        /// Set the selection in the control based on a bound list

        /// </summary>

        /// <param name=”list”></param>

        public void SetSelected(IList list)

        {

            if (monitor && !String.IsNullOrEmpty(SelectedValuePath) && list != null && list.Count > 0)

            {

                try

                {

                    monitor = false;

 

                    // Loop each item

                    foreach (object item in Items)

                    {

                        // Get the property based on the selected value path

                        PropertyInfo property = item.GetType().GetProperty(SelectedValuePath);

                        if (property != null)

                        {

                            // Match the value from the bound list to an item in the control

                            if (list.Contains(property.GetValue(item, null)))

                                SelectedItems.Add(item);

                        }

                    }

                }

                catch

                {

                    throw;

                }

                finally

                {

                    monitor = true;

                }

            }

        }

    }

First we create a control that inherits from ListBox.

Now we have to make the SelectedValues functionality available to the control.
We first create a DependencyProperty, that we name SelectedValuesProperty (best practices naming). This is also required for binding to work correctly.

After that we implement the SelectedValues property that actually sets and gets values from the DependencyProperty.

Using the callback OnValuesChanged we can trace when a list gets bound to the SelectedValues property. If this happens we will go through all values in the list (for example Guid) and find all items in the ListBox (for example Languages) that have the same Guid set on the SelectedValuePath (for example ID).

If the list we bound to SelectedValues implements the INotifyPropertyChanged interface (probably an ObservableCollection) we also want to be aware that there are changes in the list. By handeling the PropertyChanged event we can update the ListBox if something in the list changes.

And finally, if someone changes the selection in the ListBox this will be handled by the SelectionChangeEvent.
This is required to update the SelectedValues property and thus updating the list that is bound to this property.

Using the control

<local:SelectedValuesListBox

SelectedValues={Binding Path=Me.Languages, Mode=TwoWay} ItemsSource={Binding Path=Languages}

DisplayMemberPath=Name SelectedValuePath=ID x:Name=listLanguages SelectionMode=Extended>

</local:SelectedValuesListBox>

The SelectedValuePath is the property you want to have matched with the SelectedValues.

Sample code

In the attachment you can find a sample project implementing this control.
This sample is based on MVVM since this is how I stumbled upon the problem.

Download: SelectedValuesListBox.zip

Written by Sandrino

August 21st, 2009 at 6:16 pm

Posted in WPF

Tagged with , , , , ,

Using Expander to create an Outlook-like pane (like Todo)

leave a comment

For one of our projects I have to create a pane in the application that acts like the Todo pane in Outlook.
This would be visible at one side of the application (Todo is on the right) with a vertical header.
And of course it’s also possible to collapse the pane.

It’s possible to set the ExpandDirection of the expander, but this doesn’t change anything to the header.
In let’s say we want the pane on the left.

We start with setting the ExpanderDirection to left.
Here we have 2 remarks:

  • I think that the arrow button should be reversed.
    It should point to the right when we can expand and it should point to the left when we can collapse
  • The text is horizontal so it’s taking too much space

We can solve this by modifying the template for Expander.

    <Style x:Key=ExpanderLeftHeaderStyle TargetType={x:Type ToggleButton}>

      <Setter Property=Template>

        <Setter.Value>

          <ControlTemplate TargetType={x:Type ToggleButton}>

            <Border Padding={TemplateBinding Padding}>

              <Grid SnapsToDevicePixels=False Background=Transparent ClipToBounds=False>

                <Grid.LayoutTransform>

                    <RotateTransform Angle=90/>

                </Grid.LayoutTransform>

                <Grid.RowDefinitions>

                  <RowDefinition Height=Auto/>

                  <RowDefinition Height=*/>

                </Grid.RowDefinitions>

                <Grid.ColumnDefinitions>

                  <ColumnDefinition Width=25 />

                  <ColumnDefinition Width=250 />

                </Grid.ColumnDefinitions>

                <Grid Grid.Column=0>

                  <Ellipse Grid.Column=0 x:Name=circle Stroke=DarkGray

                     HorizontalAlignment=Center VerticalAlignment=Center

                     Width=19 Height=19/>

                  <Path Grid.Column=0 x:Name=arrow Stroke=#666 

                    StrokeThickness=2 HorizontalAlignment=Center

                    VerticalAlignment=Center SnapsToDevicePixels=false

                    Data=M 1,1.5 L 4.5,5 L 8,1.5>

                    <Path.LayoutTransform>

                      <RotateTransform Angle=180></RotateTransform>

                    </Path.LayoutTransform>

                  </Path>

                </Grid>

                <ContentPresenter x:Name=content SnapsToDevicePixels=True

                 Grid.Column=1 RecognizesAccessKey=True/>

              </Grid>

            </Border>

            <ControlTemplate.Triggers>

              <Trigger Property=IsChecked Value=true>

                <Setter Property=Data TargetName=arrow Value=M 1,4.5  L 4.5,1  L 8,4.5/>

              </Trigger>

              <Trigger Property=IsMouseOver Value=true>

                <Setter Property=Stroke TargetName=circle Value=#FF3C7FB1/>

                <Setter Property=Stroke TargetName=arrow Value=#222/>

              </Trigger>

              <Trigger Property=IsPressed Value=true>

                <Setter Property=Stroke TargetName=circle Value=#FF526C7B/>

                <Setter Property=StrokeThickness TargetName=circle Value=1.5/>

                <Setter Property=Stroke TargetName=arrow Value=#FF003366/>

              </Trigger>

            </ControlTemplate.Triggers>

          </ControlTemplate>

        </Setter.Value>

      </Setter>

    </Style>

In this example we first apply a layout transform of 90° to the complete header.
This will rotate the header from horizontal to vertical.

Because of this layout transform you’ll see that the arrow does not match our logic anymore.
Add a rotate transform of 180° to the arrow to also solve this issue.

Result:

expander_left

You can download the project here.

Written by Sandrino

August 19th, 2009 at 2:02 am

Posted in WPF

Tagged with , , , , , ,