Navigation Support for Paperstack Control

Since PaperStack control supports user drilling down , it also need to support stepping out. This paradigm obviously cries for some sort of navigation support. WPF support navigation paradigm and so I decided to implement the navigation support for the control. There are some unique problems that I came across.

  1. Control may or may not be hosted inside navigation container so control needs to be smart enough to act accordingly
  2. Additive Filtering behavior of  ListCollectionView means we need to keep track of all the filter applied to get to current state and reapply them one by one to restore the journal entry
  3. Control is not at the root of the content so IProvideCustomContentState so control needs to use the Navigating Event to store the custom content state
  4. Un hook from navigating event at the right time other control instance wont be GCed since container will hold on to the reference
  5. Page can multiple controls of the same type, that means each journal entry needs to know which control it was associated too. This is done by using Application properties bag. Storing a reference on the properties bag can also lead to memory leak just like navigating event handler. That is why we need to use weak reference.
  6. There can be some other control storing some other content state. That means content state object of PaperStack control needs to wrap contentstate stored in the event args so that it is not lost.

Before we get into those individual items. this is the only change I made to page.xaml of application. It has a text box and a hyperlink to make sure control not only supports navigation when there is no top level navigation but it also restores its state when user navigates away from the page containing the control and hits back.

<TextBlock Grid.Row=”1″>
                           <Hyperlink NavigateUri=”Page2.xaml”> Navigate to Page2</Hyperlink>
                  </TextBlock>
                  <TextBox Grid.Row=”2″></TextBox>

<ccl:PaperStack Name=”stack” ItemsSource=”{Binding Source={StaticResource feedcollection}}” Grid.Row=”4″ DrilledDown=”OnDrilledDown”/>

1) Navigate or Not

Control has a member ns to store the reference to the NavigationService. OnLoaded event, control tries to get handle to the NavigationService.. If the control is not in the navigation supported container then, value will be null. Value of ns is used everywhere internally to decide if the control should execute navigation related code. I think in my code I only try to get reference once. Ideally I should have tried to get reference every time I try to use it because application might tweak the tree after control is loaded.

void PaperStack_Loaded(object sender, RoutedEventArgs e)
{
            ns = NavigationService.GetNavigationService(this);
            if (ns != null)
            {
                        ns.Navigating += new NavigatingCancelEventHandler(ns_Navigating);
            }
}

2) Keep Track of Filtering and Grouping

Keep track of grouping was straight forward because in my control at any given time there is only one grouping applied but filtering was a different deal all together. Especially because filtering has additive behavior as I explained in the last post. That means for any given view I have to keep track of existing grouping but all the filters applied so far.

//Check if the navigation service is null
//if not null, control will start journaling the navigation state
if (ns != null)
{
          //Create a new journal entry object
          PaperStackJournalEntry pje = new PaperStackJournalEntry();

          //add Filter Values and Filter Properties to journal entry
          for (int i = 0; i < _filterdvalues.Count; i++)
          {
                    pje.FilterCollection.Add(_filterdvalues[i].ToString());
                    pje.FilterPropertyCollection.Add(_filterproperties[i].ToString());
          }

          //add current grouping property
          pje.Group = _currentgroupingproperty;

          //store the control guid
          pje.MyControlGuid = MyGuid;

          //Call AddBackEntry to add the journal entry in the journal
          ns.AddBackEntry(pje);
}

 

3) Navigating Event Instead of IProvideCustomContentState

Since the Control can live anywhere in the content, IProvideCustomContentState based solution would not work.  NavigatingCancelEventArgs provides access to ContentStateToSave property that allows NavigatingEventHandler to specify other information that needed to be stored. This is very useful mechanism when you can not use IProvideCustomContentState interface.

/// <summary>
/// Since Control is not at the root of the content for
/// a navigation container, typical use of IProvideCustomContentState
/// implemention wont work here. So to be able to set the custom content
/// state, we will use navigating event. Event args for that event gives
/// access to content state property.
/// </summary>
/// <param name=”sender”></param>
/// <param name=”e”></param>
void ns_Navigating(object sender, NavigatingCancelEventArgs e)
{
          //gets the datasource for the control
          ListCollectionView cs = this.ItemsSource as ListCollectionView;
          //creates a new journal entry
          PaperStackJournalEntry pje = new PaperStackJournalEntry();
          //journal the guid of the control
          pje.MyControlGuid = MyGuid;
          //journal collection of filters applied
          //to the datasource
          pje.FilterCollection = _filterdvalues;
          //journal collection of properties to which
          //those filter values are applied
          pje.FilterPropertyCollection = _filterproperties;
          //journal the grouping criteria
          pje.Group = (cs.GroupDescriptions[0] as           PropertyGroupDescription).PropertyName;

          //This is very interesting since there might
          //some other controls who might be trying to do
          //the same thing, we just cant overwrite customstate
          //so we need to wrap around existing custom state
          //so that it is not lost.
          pje.OtherContentState = e.ContentStateToSave;
          //sets the content state of navigating event args
          //to journal entry
          e.ContentStateToSave = pje;
}

 

4) Unhooking from Navigating Event

Control hooks into navigating event to store the custom state if it is hosted inside a navigation container. This means control wont be GC’ed since Navigation container will hold on to the reference of the control even after control is unloaded. So to work around this, control needs to unhook from the Navigating event so that control can be disposed easily.

void PaperStack_Unloaded(object sender, RoutedEventArgs e)
{
          if (ns != null)
                    ns.Navigating -= new NavigatingCancelEventHandler(ns_Navigating);
}

5) Who is my Owner?

There can be multiple controls of the same type all on same page. How does a journal entry know which control it was associated with. For that I use my own cooked up scheme. Every control gets a guid  when the instance is created and then I use Application object’s property bag to map guid to reference to the control. When the journal entry is created, control guid is part of the content state that is stored. So when there is time to restore the content state, replay method gets the handle to control using the application properties bag and uses that reference to restore the content state. There are two main problems with above approach…

  1. Control gets a new guid on instance creation that means user navigates away from the page containing control and comes back, control gets a new guid. To work around this problem I use journalble Dependency Property. Since this Dependency property is journalble it gets restored when users comes back to the page containing control. I hook into property change notification callback to update the property bag with correct guid.
  2. Since I used Application properties to map guid to reference handle to the control, that means when user navigates away from the page containing control, control would not get GC’ed. To get around this problem I use WeakReference instead of directly using the control reference.

//Change the guid mechanism to add DP and weak references
private Guid _myguid;

//Declare Dependency Property for GUID
public static readonly DependencyProperty MyGuidProperty;

//CLR Accessor for the property
public Guid MyGuid
{
         set { SetValue(MyGuidProperty, value); }
         get { return (Guid)GetValue(MyGuidProperty); }
}

/// <summary>
/// Static Constructor
/// </summary>
static PaperStack()
{
         //This OverrideMetadata call tells the system that this element
         //wants to provide a style that is different than its base class.
         //This style is defined in themes\generic.xaml
         DefaultStyleKeyProperty.OverrideMetadata(typeof(PaperStack), new          FrameworkPropertyMetadata(typeof(PaperStack)));

         //register the dependency property
         //Create Metadata object
         FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
         //Make the dependency property journalable.
         metadata.Journal = true;
         //specify the call for property changed notification
         metadata.PropertyChangedCallback += new PropertyChangedCallback         (PaperStack.OnMyGuidPropertyChanged);
         //register the property
         MyGuidProperty = DependencyProperty.Register(“MyGuid”, typeof(Guid),          typeof(PaperStack), metadata);

}

/// <summary>
/// Property change noification handler.
/// This gets called when GUID property changes.
/// There are two conditions when guid property changes.
/// 1) in Constructor when control gets instantiated
/// 2) journaling scenariop where navigation system restores the property
/// It adds the value using weakreference so that app does not leak memory
/// by keeping control alive as a result of holding on to its reference
/// </summary>
/// <param name=”obj”></param>
/// <param name=”args”></param>
static void OnMyGuidPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
         //gets the handle to the control
         PaperStack ps = obj as PaperStack;
         //removes the old entry for the guid if it exists
         Application.Current.Properties.Remove(((Guid)args.OldValue).ToString());
         //add entry for the new guid
         Application.Current.Properties[ps.MyGuid.ToString()] = new WeakReference(ps);
}

Other Content State

In a given page there might be other controls that trying to store their own custom states to the journal. That means I cant just assign ContentStateToSave property to PaperStackContentState because then application will lose information that somebody else might have stored. At the same time, PaperStack does not know anything about other controls to predict what the content state might be. so solution to that is to wrap the existing content state in the PaperStackContentState and when Navigation framework calls Replay on PaperStackContentState, it restore its own content state and then calls Replay on wrapped ContentState. This model will only work if all other controls are being nice citizens and doing the similar things and not overriding each other’s content state.

Code in Navigating Event handler for the control

//This is very interesting since there might
//some other controls who might be trying to do
//the same thing, we just cant overwrite customstate
//so we need to wrap around existing custom state
//so that it is not lost.
pje.OtherContentState = e.ContentStateToSave;
//sets the content state of navigating event args
//to journal entry
e.ContentStateToSave = pje;

PaperStackContentState definition

//Other custom content state that might be part of navigating event
private CustomContentState _othercs = null;

/// <summary>
/// Property get/set for Other content state
/// </summary>
public CustomContentState OtherContentState
{
         get { return _othercs; }
         set { _othercs = value; }
}

PaperStackContentState Replay Method

//When a user navigates back through navigation history to a piece of content
//that has an associated CustomContentState object, Replay is called;
//you override Replay to provide an implementation that reapplies state to the
//current content.
public override void Replay(NavigationService navigationService, NavigationMode mode)
{
         //gets the access to the control using application property bag
         PaperStack ps = (Application.Current.Properties[MyControlGuid.ToString()] as WeakReference).Target as PaperStack;

         //update the control’s propertye
         ps.FilterdValues = FilterCollection;
         ps.FilterProperties = FilterPropertyCollection;

         //get the datasource of the object to apply the journaled data
         ListCollectionView cs = ps.ItemsSource as ListCollectionView;
         //undo all grouping
         cs.GroupDescriptions.Clear();
         //add a grouping based on journaled value
         PropertyGroupDescription temp = new PropertyGroupDescription(Group);
         cs.GroupDescriptions.Add(temp);

         //undo all existing filters
         cs.Filter = null;
         if (FilterCollection != null)
         {
                  //apply one filter at a time since filtering is additive
                  for (int i = 0; i < FilterCollection.Count; i++)
                  {
                           _curfiltervalue = FilterCollection[i].ToString();
                           _curfilterproperty = FilterPropertyCollection[i].ToString();
                           cs.Filter = new Predicate<object>(FilterOutOtherGroups);
                  }
         }
         //if there was other content state stored the
         //we will call reply on the content state
         if(this.OtherContentState != null)
                  this.OtherContentState.Replay(navigationService, mode);
}

 

Restoring PaperStackContentState from Journal

This class stores the the custom state of the control. For my control, I need to store grouping and filtering separately. for showing the data with same grouping is pretty straight forward because all I needed to do was to store the current grouping value in the journal entry.

//get the datasource of the object to apply the journaled data
ListCollectionView cs = ps.ItemsSource as ListCollectionView;
//undo all grouping
cs.GroupDescriptions.Clear();
//add a grouping based on journaled value
PropertyGroupDescription temp = new PropertyGroupDescription(Group);
cs.GroupDescriptions.Add(temp);

 But for filtering, I needed to keep track of all the filter applied to data source till the current view to generate the current view. Since filters on the data source is not a collection, I have to actually create a collection that keeps track of all the filters applied so far after clearing all the existing filters.

//undo all existing filters
cs.Filter = null;
if (FilterCollection != null)
{
          //apply one filter at a time since filtering is additive
          for (int i = 0; i < FilterCollection.Count; i++)
         {
                    _curfiltervalue = FilterCollection[i].ToString();
                    _curfilterproperty = FilterPropertyCollection[i].ToString();
                    cs.Filter = new Predicate<object>(FilterOutOtherGroups);
         }
 }

//////////////////////////////////////////////////////////////////////////
Full Code
//////////////////////////////////////////////////////////////////////////

**************************************************************************
Updated PaperStack
**************************************************************************

namespace MyControls
{

          //PaperStack control inheriting from ItemsControl.
          public class PaperStack : System.Windows.Controls.ItemsControl
          {

           //Routed command that control uses to describe that user wants to
           // drill down on the current view. In the default group style, button will
          // send this command that
          //this control will catch to rearrange the it’s own view.
          public static RoutedCommand DrillDownCmd = new RoutedCommand();

          //Current grouping criteria E.g Foldername
          private string _currentgroupingproperty = null;

          //data value of the group selected by user by clicking 
          //E.g. FolderName = “Friends”
          private string _selectedgroup = null;

          //when user clicks on a button Let’s say FolderName=”Friends” then
          //Friends becomes the filtered value for the next navigation
          private ArrayList _filterdvalues = null;

          //when user clicks on a button Let’s say FolderName=”Friends” then
          //folderName becomes the filtered property for the next navigation
          //(Condition for filtering will be Foldername=”Frends”)
          private ArrayList _filterproperties = null;

          /// <summary>
          /// These are internals because did not application settings these value
          /// but Journal entry needs to
          /// </summary>
          internal ArrayList FilterdValues
          {
                    set { _filterdvalues = value; }
                    get { return _filterdvalues; }
          }

          /// <summary>
          /// These are internals because did not application settings these value
          /// but Journal entry needs to
          /// </summary>
          internal ArrayList FilterProperties
          {
                    set { _filterproperties = value; }
                    get { return _filterproperties; }
          }

          // Create a custom routed event by first registering a RoutedEventID
          // This event uses the bubbling routing strategy
          public static readonly RoutedEvent DrilledDownEvent = EventManager.RegisterRoutedEvent(“DrilledDown”, RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(PaperStack));

          //Reference to container’s Navigation Service
          private NavigationService ns = null;

          //Change the guid mechanism to add DP and weak references
          private Guid _myguid;

          //Declare Dependency Property for GUID
          public static readonly DependencyProperty MyGuidProperty;

          //CLR Accessor for the property
          public Guid MyGuid
          {
                    set { SetValue(MyGuidProperty, value); }
                    get { return (Guid)GetValue(MyGuidProperty); }
          }

          /// <summary>
          /// Static Constructor
          /// </summary>
          static PaperStack()
          {
                    //This OverrideMetadata call tells the system that this element
                    //wants to provide a style that is different than its base class.
                    //This style is defined in themes\generic.xaml
                    DefaultStyleKeyProperty.OverrideMetadata(typeof(PaperStack), new FrameworkPropertyMetadata(typeof(PaperStack)));

                    //register the dependency property
                    //Create Metadata object
                    FrameworkPropertyMetadata metadata = new FrameworkPropertyMetadata();
                    //Make the dependency property journalable.
                    metadata.Journal = true;
                    //specify the call for property changed notification
                    metadata.PropertyChangedCallback += new PropertyChangedCallback(PaperStack.OnMyGuidPropertyChanged);
                    //register the property
                    MyGuidProperty = DependencyProperty.Register(“MyGuid”, typeof(Guid), typeof(PaperStack), metadata);
          }

          /// <summary>
          /// Instance constructor…
          /// 1) Initilizes the arraylist to store the filter values
          /// </summary>
          public PaperStack()
          {
                    //Intialize the Filterd Value and Filter Properties lists
                    _filterdvalues = new ArrayList();
                    _filterproperties = new ArrayList();

                    //set the guid property
                    MyGuid = Guid.NewGuid();

                    //hook into loaded and unloaded event of self so that
                    //those events can be used to hook/unhook into
                    //the navigating event
                    this.Loaded += new RoutedEventHandler(PaperStack_Loaded);
                    this.Unloaded += new RoutedEventHandler(PaperStack_Unloaded);
          }

          /// <summary>
          /// Provide CLR Accessor for events
          /// </summary>
          public event RoutedEventHandler DrilledDown
          {
                    add { AddHandler(DrilledDownEvent, value); }
                    remove { RemoveHandler(DrilledDownEvent, value); }
          }

          /// <summary>
          /// Method raises Drilled Down event that Application can use
          /// </summary>
          void RaiseDrilledDownEvent()
          {
                    RoutedEventArgs newEventArgs = new RoutedEventArgsv(PaperStack.DrilledDownEvent);
                    RaiseEvent(newEventArgs);
          }

          /// <summary>
          /// Override to EndInit to specify a group style if it is not already specified
          /// This is because the same model that works for ItemsStyle does not work
          ///for group style In other words, control does not fall back and look inside
          ///generic.xaml for grouping style
          /// </summary>
          public override void EndInit()
          {
                    //calling ItemsControl.EndInit
                    base.EndInit();

                    //Check if the group style has been specified by application developer
                    //if the group style is specified then dont apply the default style
                    //if the group style is not specified then applyc the default style.
                    if (this.GroupStyle.Count == 0)
                    {
                              //This code actually loads the resource dictionary embedded
                              // inside the assembly using pack:// syntax. It merges the loaded
                              //dictionary with controls dictionary.
                              ResourceDictionary r = new ResourceDictionary();
                              r.Source = new Uri                                        (“pack://application:,,,/MyControls;component/groupstyle.xaml”,                               UriKind.RelativeOrAbsolute);
                              this.Resources.MergedDictionaries.Add(r);
                              this.GroupStyle.Add(this.Resources[“groupstyle”] as GroupStyle);
                    }

                    // Creates new command binding to bind a command to routed command
                    // handler
                    CommandBinding cmdbnd = new CommandBinding(PaperStack.DrillDownCmd, DrillDownExecuted);

                    //adds the binding to control so that control can get those commands
                    this.CommandBindings.Add(cmdbnd);
          }

          /// <summary>
          /// Routed Command Handler. Ccommand handler does two things…
          /// 1) After user Clicks on a particular group,
          /// this handler needs to filter out all other items except the group selected
          /// 2) This handler needs to raise an event that allows application to specify
          /// the next criteria for grouping
          /// </summary>
          /// <param name=”sender”></param>
          /// <param name=”e”></param>
          private void DrillDownExecuted(object sender, ExecutedRoutedEventArgs e)
          {
                    //gets the data source for the control
                    ListCollectionView cs = this.ItemsSource as ListCollectionView;
                    //gets the grouping property E.g. FolderName
                    _currentgroupingproperty = (cs.GroupDescriptions[0] as                     PropertyGroupDescription).PropertyName;

                    //Gets the selected group
                    if ((e.Parameter as CollectionViewGroup).Name != null)
                              _selectedgroup = (e.Parameter as CollectionViewGroup).Name.ToString();
                    else
                              _selectedgroup = null;

                    //Check if the navigation service is null
                    //if not null, control will start journaling the navigation state
                    if (ns != null)
                    {
                              //Create a new journal entry object
                              PaperStackJournalEntry pje = new PaperStackJournalEntry();

                              //add Filter Values and Filter Properties to journal entry
                              for (int i = 0; i < _filterdvalues.Count; i++)
                              {
                                        pje.FilterCollection.Add(_filterdvalues[i].ToString());
                                        pje.FilterPropertyCollection.Add(_filterproperties[i].ToString());
                              }

                              //add current grouping property
                              pje.Group = _currentgroupingproperty;

                              //store the control guid
                              pje.MyControlGuid = MyGuid;

                              //Call AddBackEntry to add the journal entry in the journal
                              ns.AddBackEntry(pje);
                    }

                    //Adds the current group property and selected group for filter
                    // E.g. if user clicks on foldername=friends
                    //then all items except FolderName=friends need to be filtered out
                    _filterdvalues.Add(_selectedgroup);
                    _filterproperties.Add(_currentgroupingproperty);

                    //Adds the filter delegate
                    cs.Filter = new Predicate<object>(FilterOutOtherGroups);

                    //After filtering is done, raises the Drill down even
                    //to allow applications to specify the next level of grouping
                    RaiseDrilledDownEvent();

          }

          /// <summary>
          /// Filtering Delegate. This gets called for each item to decide
          /// if the items should be filtered out. It uses reflection to get the
          /// value of grouping property (foldername) for each of the item and
          /// checks if it is equal to selected group (friends).
          /// If it matches then it returns true (item should not be filtered)
          /// If it does not match then it returns false (item should be filtered)
          /// </summary>
          /// <param name=”item”></param>
          /// <returns></returns>
          public bool FilterOutOtherGroups(object item)
          {
                    string _filtervalue = null;
                    Type itemtype = item.GetType();
                    PropertyInfo pinfo = itemtype.GetProperty(_currentgroupingproperty);
                    if (pinfo.GetValue(item, null) != null)
                    {
                              _filtervalue = pinfo.GetValue(item, null).ToString();
                              if (_filtervalue != null)
                              {
                                        if (_filtervalue.Equals(_selectedgroup))
                                                  return true;
                                        else
                                                  return false;
                              }
                              else
                                        return false;
                    }
                    else
                              return false;
          }

          /// <summary>
          /// Property change noification handler.
          /// This gets called when GUID property changes.
          /// There are two conditions when guid property changes.
          /// 1) in Constructor when control gets instantiated
          /// 2) journaling scenariop where navigation system restores the property
          /// It adds the value using weakreference so that app does not leak memory
          /// by keeping control alive as a result of holding on to its reference
          /// </summary>
          /// <param name=”obj”></param>
          /// <param name=”args”></param>
          static void OnMyGuidPropertyChanged(DependencyObject obj,           DependencyPropertyChangedEventArgs args)
          {
                    //gets the handle to the control
                    PaperStack ps = obj as PaperStack;
                    //removes the old entry for the guid if it exists
                    Application.Current.Properties.Remove(((Guid)args.OldValue).ToString());
                    //add entry for the new guid
                    Application.Current.Properties[ps.MyGuid.ToString()] = new WeakReference(ps);
          }

          /// <summary>
          /// On Unloaded Event, control unhooks itself
          /// from the Navigating event
          /// </summary>
          /// <param name=”sender”></param>
          /// <param name=”e”></param>
          void PaperStack_Unloaded(object sender, RoutedEventArgs e)
          {
                    if (ns != null)
                              ns.Navigating -= new NavigatingCancelEventHandler(ns_Navigating);
          }

          /// <summary>
          /// On Loaded Event, control hooks itself
          /// to the Navigating event
          /// </summary>
          /// <param name=”sender”></param>
          /// <param name=”e”></param>
          void PaperStack_Loaded(object sender, RoutedEventArgs e)
          {
                    ns = NavigationService.GetNavigationService(this);
                    if (ns != null)
                              ns.Navigating += new NavigatingCancelEventHandler(ns_Navigating);
          }

          /// <summary>
          /// Since Control is not at the root of the content for
          /// a navigation container, typical use of IProvideCustomContentState
          /// implemention wont work here. So to be able to set the custom content
          /// state, we will use navigating event. Event args for that event gives
          /// access to content state property.
          /// </summary>
          /// <param name=”sender”></param>
          /// <param name=”e”></param>
          void ns_Navigating(object sender, NavigatingCancelEventArgs e)
          {
                    //gets the datasource for the control
                    ListCollectionView cs = this.ItemsSource as ListCollectionView;
                    //creates a new journal entry
                    PaperStackJournalEntry pje = new PaperStackJournalEntry();
                    //journal the guid of the control
                    pje.MyControlGuid = MyGuid;
                    //journal collection of filters applied
                    //to the datasource
                    pje.FilterCollection = _filterdvalues;
                    //journal collection of properties to which
                    //those filter values are applied
                    pje.FilterPropertyCollection = _filterproperties;
                    //journal the grouping criteria
                    pje.Group = (cs.GroupDescriptions[0] as                     PropertyGroupDescription).PropertyName;

                    //This is very interesting since there might
                    //some other controls who might be trying to do
                    //the same thing, we just cant overwrite customstate
                    //so we need to wrap around existing custom state
                    //so that it is not lost.
                    pje.OtherContentState = e.ContentStateToSave;
                    //sets the content state of navigating event args
                    //to journal entry
                    e.ContentStateToSave = pje;
          }
          }
}

 

**************************************************************************
PaperStack Journal Entry
**************************************************************************

namespace MyControls
{
           //A class that derives from CustomContentState must be serializable,
           //which means it must either be augmented with SerializableAttribute
           //or implement ISerializable
           [Serializable]
           class PaperStackJournalEntry : CustomContentState
           {
                      //value of the filtered property. For FolderName=”Friends”
                      //Friends is the filter value
                      private ArrayList _filtercollection = null;

                      //value of the filtered property name. For FolderName=”Friends”
                      //FolderName is the property
                      private ArrayList _filterpropertycollection = null;

                      //value for grouping
                      private string _group = null;

                      //internal variables to be used for filtering
                      private string _curfiltervalue = null;
                      private string _curfilterproperty = null;

                      //Guid of the control the journal entry is associated
                      private Guid _mycontrolguid;

                      //Other custom content state that might be part of navigating event
                      private CustomContentState _othercs = null;

                      /// <summary>
                      /// Instance constructor
                      /// </summary>
                      public PaperStackJournalEntry()
                      {
                                 //initializes collection that will be used to store filter information
                                 _filtercollection = new ArrayList();
                                 _filterpropertycollection = new ArrayList();
                      }

                      /// <summary>
                      /// Property get/set for Other content state
                      /// </summary>
                      public CustomContentState OtherContentState
                      {
                                 get { return _othercs; }
                                 set { _othercs = value; }
                      }

                      /// <summary>
                      /// Property get/set for Filtercollection
                      /// </summary>
                      public ArrayList FilterCollection
                      {
                                 get { return _filtercollection; }
                                 set { _filtercollection = value; }
                      }

                      /// <summary>
                      /// Property get/set for Filter Properites
                      /// </summary>
                      public ArrayList FilterPropertyCollection
                      {
                                 get { return _filterpropertycollection; }
                                 set { _filterpropertycollection = value; }
                      }

                      /// <summary>
                      /// Property get/set for guid of the control
                      /// </summary>
                      public Guid MyControlGuid
                      {
                                 get { return _mycontrolguid; }
                                 set { _mycontrolguid = value; }
                      }

                      /// <summary>
                      /// Property get set for the grouping
                      /// </summary>
                      public String Group
                      {
                                 get { return _group; }
                                 set { _group = value; }
                      }

                      //The name that appears in the back and forward drop down navigation
                     //  buttons,displayed from NavigationWindow, Frame, or Microsoft
                      // Internet Explorer 7 Beta 3 navigation chrome is automatically
                      // generated, unless you return your own by overriding
                      // JournalEntryName  property.
                      public override string JournalEntryName
                      {
                                 get{ return “Drill Down”;}
                      }

                      //When a user navigates back through navigation history to a piece of
                      // content that has an associated CustomContentState object, Replay is
                      // called; you override Replay to provide an implementation that
                      // reapplies state to the current content.
                      public override void Replay(NavigationService navigationService,            NavigationMode mode)
                      {
                                 //gets the access to the control using application property bag
                                 PaperStack ps = (Application.Current.Properties[MyControlGuid.ToString()] as WeakReference).Target as PaperStack;

                                 //update the control’s propertye
                                 ps.FilterdValues = FilterCollection;
                                 ps.FilterProperties = FilterPropertyCollection;

                                 //get the datasource of the object to apply the journaled data
                                 ListCollectionView cs = ps.ItemsSource as ListCollectionView;
                                 //undo all grouping
                                 cs.GroupDescriptions.Clear();
                                 //add a grouping based on journaled value
                                 PropertyGroupDescription temp = new PropertyGroupDescription(Group);
                                 cs.GroupDescriptions.Add(temp);

                                 //undo all existing filters
                                 cs.Filter = null;
                                 if (FilterCollection != null)
                                 {
                                            //apply one filter at a time since filtering is additive
                                            for (int i = 0; i < FilterCollection.Count; i++)
                                            {
                                                       _curfiltervalue = FilterCollection[i].ToString();
                                                       _curfilterproperty = FilterPropertyCollection[i].ToString();
                                                       cs.Filter = new Predicate<object>(FilterOutOtherGroups);
                                            }
                                 }
                      //if there was other content state stored the
                      //we will call reply on the content state
                      if(this.OtherContentState != null)
                                 this.OtherContentState.Replay(navigationService, mode);
           }

           /// <summary>
           /// Filtering Delegate. This gets called for each item to decide
           /// if the items should be filtered out. It uses reflection to get the
           /// value of grouping property (foldername) for each of the item and
           /// checks if it is equal to selected group (friends).
           /// If it matches then it returns true (item should not be filtered)
           /// If it does not match then it returns false (item should be filtered)
           /// </summary>
           /// <param name=”item”></param>
           /// <returns></returns>
           public bool FilterOutOtherGroups(object item)
           {
                      string _filtervalue = null;
                      Type itemtype = item.GetType();
                      PropertyInfo pinfo = itemtype.GetProperty(_curfilterproperty);
                      if (pinfo.GetValue(item, null) != null)
                      {
                                 _filtervalue = pinfo.GetValue(item, null).ToString();
                                 if (_filtervalue != null)
                                 {
                                            if (_filtervalue.Equals(_curfiltervalue))
                                                       return true;
                                            else
                                                       return false;
                                 }
                                 else
                                            return false;
                      }
                      else
                                 return false;
           }
           }
}

 

**************************************************************************
Page.xaml
**************************************************************************

<Page x:Class=”WindowsApplication1.Page1″
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:ccl=”clr-namespace:MyControls;assembly=MyControls”
xmlns:feedns=”clr-namespace:IERSSInterop;assembly=IERSSInterop”
Title=”WindowsApplication1″>
         <Page.Resources>
                  <feedns:FeedCollection x:Key=”stack”/>
                  <CollectionViewSource Source=”{StaticResource stack}” x:Key=”feedcollection”/>
         </Page.Resources>
         <Grid>
                  <Grid.ColumnDefinitions>
                           <ColumnDefinition />
                  </Grid.ColumnDefinitions>
                  <Grid.RowDefinitions>
                           <RowDefinition Height=”30″/>
                           <RowDefinition Height=”30″/>
                           <RowDefinition Height=”30″/>
                           <RowDefinition />
                  </Grid.RowDefinitions>
                  <ComboBox SelectedIndex=”0″ SelectionChanged=”OnSelectionChanged” Grid.Row=”0″>
                           <ComboBoxItem Content=”FolderName”></ComboBoxItem>
                           <ComboBoxItem Content=”PubDate”></ComboBoxItem>
                           <ComboBoxItem Content=”Author”></ComboBoxItem>
                           <ComboBoxItem Content=”FeedName”></ComboBoxItem>
                  </ComboBox>
                  <TextBlock Grid.Row=”1″>
                           <Hyperlink NavigateUri=”Page2.xaml”> Navigate to Page2</Hyperlink>
                  </TextBlock>
                  <TextBox Grid.Row=”2″></TextBox>
<ccl:PaperStack Name=”stack” ItemsSource=”{Binding Source={StaticResource feedcollection}}” Grid.Row=”4″ DrilledDown=”OnDrilledDown”/>
         </Grid>
</Page>

Advertisements

Grouping Control: PaperStack

I wanted to build a control that takes a data source in the form of observable collection and then visualizes it in the form of drill down user paradigm. It is similar what windows explorer does for drilling down in file system.

Since IE 7’s new RSS features allows you to categorize your feeds into folder, I thought I would use that as a data source. I definitely don’t want to talk in detail about RSS interop but what I did was pretty straight. I created a managed wrapper (using VS add reference functionality to COM DLL) and then created another wrapper class that walks the hierarchy of the feeds and creates and observable collection that can be used. That is why I have put that code in the end.

I wanted to Application XAML to look pretty straight forward. User defines a datasource and defines a control (PaperStack) then binds the datasource to that source and rest should just follow.

So here is what the startup page of my application looks like…

<Page x:Class=”WindowsApplication1.Page1″
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:ccl=”clr-namespace:MyControls;assembly=MyControls”
xmlns:feedns=”clr-namespace:IERSSInterop;assembly=IERSSInterop”
Title=”WindowsApplication1″>
       <Page.Resources>
              <feedns:FeedCollection x:Key=”stack”/>
              <CollectionViewSource Source=”{StaticResource stack}”
x:Key=”feedcollection”/>
       </Page.Resources>
       <Grid>
       <Grid.ColumnDefinitions>
              <ColumnDefinition />
       </Grid.ColumnDefinitions>
       <Grid.RowDefinitions>
              <RowDefinition Height=”30″/>
              <RowDefinition Height=”30″/>
              </Grid.RowDefinitions>
             <ComboBox SelectedIndex=”0″ SelectionChanged=”OnSelectionChanged” Grid.Row=”0″>
                    <ComboBoxItem Content=”FolderName”></ComboBoxItem>
                     <ComboBoxItem Content=”PubDate”></ComboBoxItem>
                     <ComboBoxItem Content=”Author”></ComboBoxItem>
                     <ComboBoxItem Content=”FeedName”></ComboBoxItem>
              </ComboBox>
              <ccl:PaperStack Name=”stack” ItemsSource=”{Binding Source={StaticResource feedcollection}}” Grid.Row=”4″ DrilledDown=”OnDrilledDown”/>
       </Grid>
</Page>

Basic Behavior

So the basic behavior I wanted to support is this…

  1. Applications has the data source
  2. Application databinds the data source to this control
  3. Control allows application to set the grouping
  4. It displays the datasource grouped by application specified grouping
  5. User clicks on the group
  6. Control filters all the other items but those included in the group
  7. Application specifies next set of grouping

Since this is control is supposed to show a collection of items, it was obvious that I had to use ItemsControl. Using ItemsControl I got the most of the function. I even discovered that ItemsControl supports grouping and filtering of data through ListCollectionView class.

ControlTemplate and Groupstyle

Control Template of the paperstack look likes. Application can always override the control templates through styling but if application does not then control falls back to Generic.xaml in the themes directory. So default control template for the paperstack control is defined in the Generic.xaml in the themes directory under control root project directory.

<ResourceDictionary
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:local=”clr-namespace:MyControls”
>
       <Style TargetType=”{x:Type local:PaperStack}”>
              <Setter Property=”Template”>
                     <Setter.Value>
                            <ControlTemplate TargetType=”{x:Type local:PaperStack}”>
                                   <Border Background=”{TemplateBinding Background}”
BorderBrush=”{TemplateBinding BorderBrush}”
BorderThickness=”{TemplateBinding BorderThickness}”>
                                          <ScrollViewer VerticalScrollBarVisibility= “Visible”>
                                                 <WrapPanel Orientation=”Horizontal” IsItemsHost=”True”/>
                                          </ScrollViewer>
                                   </Border>
                            </ControlTemplate>
                     </Setter.Value>
              </Setter>
       </Style>
</ResourceDictionary>

GroupStyle appeared to be entirely different kind of concept. GroupStyle is a collection and you can specify multiple types of group styles. But Groupstyles don’t work with generic.xaml. You can not specify a groupstyle in the generic.xaml and expect the fall back to work. So I had to write a custom code for making sure if application does not specify a group style then it falls back to default group style. I used EndInit override to load the default groupstyle if application has not specified one.

/// <summary>
/// Override to EndInit to specify a group style if it is not already specified
/// This is because the same model that works for ItemsStyle does not work
/// for group style In other words, control does not fall back and look inside
/// generic.xaml for grouping style
/// </summary>
public override void EndInit()
{
            //calling ItemsControl.EndInit
            base.EndInit();

            //Check if the group style has been specified by application developer
            //if the group style is specified then dont apply the default style
            //if the group style is not specified then applyc the default style.
            if (this.GroupStyle.Count == 0)
            {
                        //This code actually loads the resource dictionary embedded
                        // inside the assembly
                        //using pack:// syntax. It merges the loaded dictionary with
                       // controls dictionary.
                        ResourceDictionary r = new ResourceDictionary();
                        r.Source = new Uri(“pack://application:,,,/MyControls;component/groupstyle.xaml”, UriKind.RelativeOrAbsolute);
                        this.Resources.MergedDictionaries.Add(r);
                        this.GroupStyle.Add(this.Resources[“groupstyle”] as GroupStyle);
            }

            // Creates new command binding to bind a command to routed
            // command handler
            CommandBinding cmdbnd = new CommandBinding(PaperStack.DrillDownCmd, DrillDownExecuted);

            //adds the binding to control so that control can get those commands
            this.CommandBindings.Add(cmdbnd);

}

Here is the groupstyle.xaml that gets loaded above…

<ResourceDictionary
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:local=”clr-namespace:MyControls”
>
            <GroupStyle x:Key=”groupstyle”>
                        <GroupStyle.ContainerStyle>
                                    <Style TargetType=”{x:Type GroupItem}”>
                                                <Setter Property=”Margin” Value=”0,0,0,5″/>
                                                <Setter Property=”Template”>
                                                            <Setter.Value>
                                                                        <ControlTemplate TargetType=”{x:Type GroupItem}”>
                                                                                    <Grid >
                                                                                                <Grid.ColumnDefinitions>
                                                                                                            <ColumnDefinition/>
                                                                                                </Grid.ColumnDefinitions>
                                                                                                <Grid.RowDefinitions>
                                                                                                            <RowDefinition/>
                                                                                                </Grid.RowDefinitions>
                                                                                                <Rectangle Stroke=”#FF000000″ Fill=”sc#1, 0.886873066, 0.8459564, 0.261662871″ HorizontalAlignment=”Left” VerticalAlignment=”Top” Margin=”7,6,0,0″ Width=”205″ Height=”199″ x:Name=”Rectangle”/>
                                                                                                <Rectangle Stroke=”#FF000000″ Fill=”sc#1, 0.8657835, 0.882726, 0.353273958″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” Margin=”15,14,29,25″ Width=”Auto” Height=”Auto” x:Name=”Rectangle1″/>
                                                                                                <Rectangle Stroke=”#FF000000″ Fill=”sc#1, 0.864997566, 0.882446051, 0.352562249″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” Margin=”23,23,22,17″ Width=”Auto” Height=”Auto” x:Name=”Rectangle2″/>
                                                                                                <StackPanel Margin=”25,24,23,17″ x:Name=”StackPanel”>
                                                                                                            <TextBlock FontSize=”18″ FontFamily=”Segoe Print” Width=”188″ Height=”136″ x:Name=”txtTitle” TextWrapping=”Wrap” Text=”{Binding Path=Name}”/>
                                                                                                            <TextBlock FontSize=”14″ FontFamily=”Segoe Print” HorizontalAlignment=”Center” Width=”192″ Height=”57″ x:Name=”txtCount” TextWrapping=”Wrap” Text=”{Binding Path=ItemCount}”/>
                                                                                                </StackPanel>
                                                                                                <Button Opacity=”0.1″ Margin=”0,0,18,13″ Background=”{x:Null}” x:Name=”Button” Content=”btnFilter” Foreground=”{x:Null}” BorderBrush=”{x:Null}” Command=”{x:Static local:PaperStack.DrillDownCmd}” CommandParameter=”{Binding}”/>
                                                                                    </Grid>
                                                                        </ControlTemplate>
                                                            </Setter.Value>
                                                </Setter>
                                    </Style>
                        </GroupStyle.ContainerStyle>
            </GroupStyle>

</ResourceDictionary>

  Part that is marked red is the one that actually shows the data. Now I do assume that data source has a name property. Which is one of the flaws that I need to fix :).  I will talk about the button part in detail below.

DrillDownCmd

When user clicks on a particular group, we want filtering to happen for that group, that means control needs to know what exactly user clicked on so that it can filter all other groups out. I had few options for doing this…

  1. Define an interface that control implements and gets called when user clicks on a group
  2. Raise an Event when group get clicked
  3. Using commanding.

I went with 3 because I thought that was the cleanest of all the solution. I could do things like data binding and still keep my default group style clean and in xaml only.  

There were three things to be done

Define a routed Command

This is done by declaring a static RoutedCommand called DrillDown.

//Routed command that control uses to describe that user wants to drill down
//on the current view. In the default group style, button will send this command that
//this control will catch to rearrange the it’s own view.
public static RoutedCommand DrillDownCmd = new RoutedCommand();

Specify a command binding

I need to specify a command binding that binds the control and command handler to the command. So when the command is raised, command handler is executed .

/// <summary>
/// Override to EndInit to specify a group style if it is not already specified
/// This is because the same model that works for ItemsStyle does not work
/// for group style In other words, control does not fall back and look inside
/// generic.xaml for grouping style
/// </summary>
public override void EndInit()
{
            //calling ItemsControl.EndInit
            base.EndInit();

            //Check if the group style has been specified by application developer
            //if the group style is specified then dont apply the default style
            //if the group style is not specified then applyc the default style.
            if (this.GroupStyle.Count == 0)
            {
                        //This code actually loads the resource dictionary embedded
                        // inside the assembly
                        //using pack:// syntax. It merges the loaded dictionary with
                       // controls dictionary.
                        ResourceDictionary r = new ResourceDictionary();
                        r.Source = new Uri(“pack://application:,,,/MyControls;component/groupstyle.xaml”, UriKind.RelativeOrAbsolute);
                        this.Resources.MergedDictionaries.Add(r);
                        this.GroupStyle.Add(this.Resources[“groupstyle”] as GroupStyle);
            }

            // Creates new command binding to bind a command to routed
            // command handler
            CommandBinding cmdbnd = new CommandBinding(PaperStack.DrillDownCmd, DrillDownExecuted);

            //adds the binding to control so that control can get those commands
            this.CommandBindings.Add(cmdbnd);
}

Raise the command

Now somebody needs to raise the drill down command when user clicks on a group. This is done by the button that is specified in the group style. This button is binds it command property to DrillDownCommand. This when when the button is Clicked it raise the command that controls command handler handles. There is another part. Now we know when somebody clicks on the group but we still don’t know what they clicked on this. This is solved by passing the data object that groupstyle is bound to as a command parameter.

<Button Opacity=”0.1″ Margin=”0,0,18,13″ Background=”{x:Null}” x:Name=”Button” Content=”btnFilter” Foreground=”{x:Null}” BorderBrush=”{x:Null}” Command=”{x:Static local:PaperStack.DrillDownCmd}” CommandParameter=”{Binding}”/>

Data Source manipulation

All the grouping and filter operations are done on the data source of the control. ListCollectionView is the type for the itemsource property of the control that is set by application in the page1.xaml above. It support both filtering and grouping operations.

Filtering

Filtering happens when user clicks on any group in the current view. When user clicks on the group, drill down command is raised and in the command handler we need to filter out other data. Weird part is grouping acts like a collection but filtering is done as an additive operation on the current view of the data. E.g If you apply filter F1 to data and then F2. You do it by setting filter = F1 first and then filter = F2 instead of saying filter.add(f1) and then filter.add(f2).  To filter you need to two things…

  1. Add the filter predicate to itemsource of the control (which is of type ListCollectionView
  2. Implement a filter function that gets called for each item to decide if the item should be filtered out or not.

I had to keep track the group name and group criteria so that filter function can make the decision. I also ended up using reflection to get the property of the current item. I might be able to do this with an interface that I could ask the data source to be implemented but I used this for now because I did not want to move away from having the datasource as any observable collection.

/// <summary>
/// Routed Command Handler. This command handler basically does two things…
/// 1) After user Clicks on a particular group,
/// this handler needs to filter out all other items except the group selected
/// 2) This handler needs to raise an event that allows application to specify
/// the next criteria for grouping
/// </summary>
/// <param name=”sender”></param>
/// <param name=”e”></param>
private void DrillDownExecuted(object sender, ExecutedRoutedEventArgs e)
{
            //gets the data source for the control
            ListCollectionView cs = this.ItemsSource as ListCollectionView;
            //gets the grouping property E.g. FolderName
            _currentgroupingproperty = (cs.GroupDescriptions[0] as PropertyGroupDescription).PropertyName;

            //Gets the selected group
            if ((e.Parameter as CollectionViewGroup).Name != null)
                        _selectedgroup = (e.Parameter as CollectionViewGroup).Name.ToString();
            else
                        _selectedgroup = null;
            //Adds the current group property and selected group for filter
            // E.g. if user clicks on foldername=friends
            //then all items except FolderName=friends need to be filtered out
            _filterdvalues.Add(_selectedgroup);
            _filterproperties.Add(_currentgroupingproperty);

            //Adds the filter delegate
            cs.Filter = new Predicate<object>(FilterOutOtherGroups);

            //After filtering is done, raises the Drill down even
            //to allow applications to specify the next level of grouping
            RaiseDrilledDownEvent();

}

/// <summary>
/// Filtering Delegate. This gets called for each item to decide
/// if the items should be filtered out. It uses reflection to get the
/// value of grouping property (foldername) for each of the item and
/// checks if it is equal to selected group (friends).
/// If it matches then it returns true (item should not be filtered)
/// If it does not match then it returns false (item should be filtered)
/// </summary>
/// <param name=”item”></param>
/// <returns></returns>
public bool FilterOutOtherGroups(object item)
{
            string _filtervalue = null;
            Type itemtype = item.GetType();
            PropertyInfo pinfo = itemtype.GetProperty(_currentgroupingproperty);
            if (pinfo.GetValue(item, null) != null)
            {
                        _filtervalue = pinfo.GetValue(item, null).ToString();
                        if (_filtervalue != null)
                        {
                                    if (_filtervalue.Equals(_selectedgroup))
                                                return true;
                                    else
                                                return false;
                        }
                        else
                                    return false;
            }
            else
                        return false;
}
 

Grouping

One the data is grouped, control uses groupstyle to display the results. But grouping needed to be handled differently than filtering. That is so because when user clicks on the group control knows exactly what to filter out but there is no way for control to guess what the next level of grouping should be. So  that means I need to give a chance to application to specify it is own grouping. This is done by supporting an event on the control After user Clicks on the group and all the filtering above is done, control raises an event that allows application to specify next level of grouping.

DrillDownEvent

This is pretty straight forward. It falls into three steps…

  1. Control Defines and Event
  2. Raises it appropriately
  3. App handles  to specify next level of grouping

Define an Event

// Create a custom routed event by first registering a RoutedEventID
// This event uses the bubbling routing strategy
public static readonly RoutedEvent DrilledDownEvent = EventManager.RegisterRoutedEvent(“DrilledDown”, RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(PaperStack));

/// <summary>
/// Provide CLR Accessor for events
/// </summary>
public event RoutedEventHandler DrilledDown
{
        add { AddHandler(DrilledDownEvent, value); }
        remove { RemoveHandler(DrilledDownEvent, value); }
}

/// <summary>
/// Method raises Drilled Down event that Application can use
/// </summary>
void RaiseDrilledDownEvent()
{
        RoutedEventArgs newEventArgs = new RoutedEventArgs        (PaperStack.DrilledDownEvent);
        RaiseEvent(newEventArgs);
}

 

Raise an Event

/// <summary>
/// Routed Command Handler. This command handler basically does two things…
/// 1) After user Clicks on a particular group,
/// this handler needs to filter out all other items except the group selected
/// 2) This handler needs to raise an event that allows application to specify
/// the next criteria for grouping
/// </summary>
/// <param name=”sender”></param>
/// <param name=”e”></param>
private void DrillDownExecuted(object sender, ExecutedRoutedEventArgs e)
{
            //gets the data source for the control
            ListCollectionView cs = this.ItemsSource as ListCollectionView;
            //gets the grouping property E.g. FolderName
            _currentgroupingproperty = (cs.GroupDescriptions[0] as PropertyGroupDescription).PropertyName;

            //Gets the selected group
            if ((e.Parameter as CollectionViewGroup).Name != null)
                        _selectedgroup = (e.Parameter as CollectionViewGroup).Name.ToString();
            else
                        _selectedgroup = null;
            //Adds the current group property and selected group for filter
            // E.g. if user clicks on foldername=friends
            //then all items except FolderName=friends need to be filtered out
            _filterdvalues.Add(_selectedgroup);
            _filterproperties.Add(_currentgroupingproperty);

            //Adds the filter delegate
            cs.Filter = new Predicate<object>(FilterOutOtherGroups);

            //After filtering is done, raises the Drill down even
            //to allow applications to specify the next level of grouping
            RaiseDrilledDownEvent();

}

 App Handling

In Page.xaml, app specifies this

<ccl:PaperStack Name=”stack” ItemsSource=”{Binding Source={StaticResource feedcollection}}” Grid.Row=”4″ DrilledDown=”OnDrilledDown”/>

In Page.xaml.cs

public void OnDrilledDown(object sender, RoutedEventArgs e)
{
         ListCollectionView src = (sender as PaperStack).ItemsSource as ListCollectionView;
         switch ((src.GroupDescriptions[0] as          PropertyGroupDescription).PropertyName.ToString())
         {
                  case “FolderName”:
                           PropertyGroupDescription temp = new PropertyGroupDescription(“Author”);
                           src.GroupDescriptions.Clear();
                           src.GroupDescriptions.Add(temp);
                           break;
                  case “Author”:
                           PropertyGroupDescription temp1 = new PropertyGroupDescription(“PubDate”);
                           src.GroupDescriptions.Clear();
                           src.GroupDescriptions.Add(temp1);
                           break;
                  case “PubDate”:
                           PropertyGroupDescription temp2 = new PropertyGroupDescription(“FeedName”);
                           src.GroupDescriptions.Clear();
                           src.GroupDescriptions.Add(temp2);
                           break;

         }
}

////////////////////////////////////////////////////////////////////////

COMPLETE CODE

///////////////////////////////////////////////////////////////////////

*******************************************************************
PaperStack  CODE
*******************************************************************

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Data;
using System.Collections;
using System.Reflection;

namespace MyControls
{

         //PaperStack control inheriting from ItemsControl.
         public class PaperStack : System.Windows.Controls.ItemsControl
         {

         //Routed command that control uses to describe that user wants to drill down
         //on the current view. In the default group style, button will send this 
         // command that this control will catch to rearrange the it’s own view.
         public static RoutedCommand DrillDownCmd = new RoutedCommand();

         //Current grouping criteria E.g Foldername
         private string _currentgroupingproperty = null;

         //data value of the group selected by user by clicking
         //E.g. FolderName = “Friends”
         private string _selectedgroup = null;

         //when user clicks on a button Let’s say FolderName=”Friends” then
         //Friends becomes the filtered value for the next navigation
         private ArrayList _filterdvalues = null;

         //when user clicks on a button Let’s say FolderName=”Friends” then
         //folderName becomes the filtered property for the next navigation
         //(Condition for filtering will be Foldername=”Frends”)
         private ArrayList _filterproperties = null;

         // Create a custom routed event by first registering a RoutedEventID
         // This event uses the bubbling routing strategy
         public static readonly RoutedEvent DrilledDownEvent =                   EventManager.RegisterRoutedEvent(“DrilledDown”, RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(PaperStack));

         /// <summary>
         /// Static Constructor
         /// </summary>
         static PaperStack()
         {
                  //This OverrideMetadata call tells the system that this element
                  //wants to provide a style that is different than its base class.
                  //This style is defined in themes\generic.xaml
                  DefaultStyleKeyProperty.OverrideMetadata(typeof(PaperStack), new FrameworkPropertyMetadata(typeof(PaperStack)));
         }

         /// <summary>
         /// Instance constructor…
         /// 1) Initilizes the arraylist to store the filter values
         /// </summary>
         public PaperStack()
         {
                  //Intialize the Filterd Value and Filter Properties lists
                  _filterdvalues = new ArrayList();
                  _filterproperties = new ArrayList();
         }

         /// <summary>
         /// Provide CLR Accessor for events
         /// </summary>
         public event RoutedEventHandler DrilledDown
         {
                  add { AddHandler(DrilledDownEvent, value); }
                  remove { RemoveHandler(DrilledDownEvent, value); }
         }

         /// <summary>
         /// Method raises Drilled Down event that Application can use
         /// </summary>
         void RaiseDrilledDownEvent()
         {
         RoutedEventArgs newEventArgs = new RoutedEventArgs         (PaperStack.DrilledDownEvent);
         RaiseEvent(newEventArgs);
         }

         /// <summary>
         /// Override to EndInit to specify a group style if it is not already specified
         /// This is because the same model that works for ItemsStyle does not work
        ///for group style In other words, control does not fall back and look inside
         ///generic.xaml for grouping style
         /// </summary>
         public override void EndInit()
         {
                  //calling ItemsControl.EndInit
                  base.EndInit();

                  //Check if the group style has been specified by application developer
                  //if the group style is specified then dont apply the default style
                  //if the group style is not specified then applyc the default style.
                  if (this.GroupStyle.Count == 0)
                  {
                           //This code actually loads the resource dictionary embedded 
                          //inside the assembly 
                          //using pack:// syntax. It merges the loaded dictionary with
                           //controls dictionary.
                           ResourceDictionary r = new ResourceDictionary();
                           r.Source = new Uri(“pack://application:,,,/MyControls;component/groupstyle.xaml”, UriKind.RelativeOrAbsolute);
                           this.Resources.MergedDictionaries.Add(r);
                           this.GroupStyle.Add(this.Resources[“groupstyle”] as GroupStyle);
                  }

                  // Creates new command binding to bind a command to routed command
                  //handler
                  CommandBinding cmdbnd = new CommandBinding(PaperStack.DrillDownCmd, DrillDownExecuted);

                  //adds the binding to control so that control can get those commands
                  this.CommandBindings.Add(cmdbnd);
         }

         /// <summary>
         /// Routed Command Handler. This command handler basically does two
        ///things…
         /// 1) After user Clicks on a particular group,
         /// this handler needs to filter out all other items except the group selected
         /// 2) This handler needs to raise an event that allows application to specify
         /// the next criteria for grouping
         /// </summary>
         /// <param name=”sender”></param>
         /// <param name=”e”></param>
         private void DrillDownExecuted(object sender, ExecutedRoutedEventArgs e)
         {
                  //gets the data source for the control
                  ListCollectionView cs = this.ItemsSource as ListCollectionView;
                  //gets the grouping property E.g. FolderName
                  _currentgroupingproperty = (cs.GroupDescriptions[0] as                            PropertyGroupDescription).PropertyName;

                  //Gets the selected group
                  if ((e.Parameter as CollectionViewGroup).Name != null)
                  {
                           _selectedgroup = (e.Parameter as CollectionViewGroup).Name.ToString();
                  }
                  else
                  {
                           _selectedgroup = null;
                  }

                  //Adds the current group property and selected group for filter
                  // E.g. if user clicks on foldername=friends
                  //then all items except FolderName=friends need to be filtered out
                  _filterdvalues.Add(_selectedgroup);
                  _filterproperties.Add(_currentgroupingproperty);

                  //Adds the filter delegate
                  cs.Filter = new Predicate<object>(FilterOutOtherGroups);

                  //After filtering is done, raises the Drill down even
                  //to allow applications to specify the next level of grouping
                  RaiseDrilledDownEvent();

         }

         /// <summary>
         /// Filtering Delegate. This gets called for each item to decide
         /// if the items should be filtered out. It uses reflection to get the
         /// value of grouping property (foldername) for each of the item and
         /// checks if it is equal to selected group (friends).
         /// If it matches then it returns true (item should not be filtered)
         /// If it does not match then it returns false (item should be filtered)
         /// </summary>
         /// <param name=”item”></param>
         /// <returns></returns>
         public bool FilterOutOtherGroups(object item)
         {
                  string _filtervalue = null;
                  Type itemtype = item.GetType();
                  PropertyInfo pinfo = itemtype.GetProperty(_currentgroupingproperty);
                  if (pinfo.GetValue(item, null) != null)
                  {
                           _filtervalue = pinfo.GetValue(item, null).ToString();
                           if (_filtervalue != null)
                           {
                                    if (_filtervalue.Equals(_selectedgroup))
                                             return true;
                                    else
                                             return false;
                           }
                           else
                                    return false;
                  }
                  else
                  {
                           return false;
                  }
         }
     }
}

*******************************************************************
Page.xaml.cs  CODE
*******************************************************************

public partial class Page1 : System.Windows.Controls.Page
{

         public Page1()
         {
                  InitializeComponent();
          }

          void OnSelectionChanged(object sender, RoutedEventArgs e)
          {
                    CollectionViewSource src = this.Resources[“feedcollection”] as   CollectionViewSource;
                    PropertyGroupDescription temp = new PropertyGroupDescription(((sender as ComboBox).SelectedItem as ComboBoxItem).Content.ToString());
                    src.GroupDescriptions.Clear();
                    src.GroupDescriptions.Add(temp);
          }

          public void OnDrilledDown(object sender, RoutedEventArgs e)
          {
                    ListCollectionView src = (sender as PaperStack).ItemsSource as   ListCollectionView;
                    switch ((src.GroupDescriptions[0] as   PropertyGroupDescription).PropertyName.ToString())
                    {
                              case “FolderName”:
                                        PropertyGroupDescription temp = new PropertyGroupDescription(“Author”);
                                        src.GroupDescriptions.Clear();
                                        src.GroupDescriptions.Add(temp);
                                        break;
                              case “Author”:
                                        PropertyGroupDescription temp1 = new PropertyGroupDescription(“PubDate”);
                                        src.GroupDescriptions.Clear();
                                        src.GroupDescriptions.Add(temp1);
                                        break;
                              case “PubDate”:
                                        PropertyGroupDescription temp2 = new    PropertyGroupDescription(“FeedName”);
                                        src.GroupDescriptions.Clear();
                                        src.GroupDescriptions.Add(temp2);
                                        break;
                    }
          }

}

*******************************************************************
IE RSS INTEROP CODE
*******************************************************************

namespace IERSSInterop
{
           public class FeedCollection : ObservableCollection<FeedItem>
           {

                    public FeedCollection()
                   {
                            IFeedsManager fmgr = new FeedsManagerClass() as IFeedsManager;
                            IFeedFolder rootFolder = fmgr.RootFolder as IFeedFolder;
                            GetAllFeeds(rootFolder);
                    }

        private void GetAllFeeds(IFeedFolder feedfolder)
       {
             if (feedfolder != null)
            {
                    AddFeedsFromFolder(feedfolder);
                    IFeedsEnum temp = feedfolder.Subfolders as IFeedsEnum;
                    for (int count = 0; count < temp.Count; count++)
                    {
                              GetAllFeeds(temp.Item(count) as IFeedFolder);
                              temp.GetEnumerator().MoveNext();
                     }
            }
     }

       private void AddFeedsFromFolder(IFeedFolder feedfolder)
      {
                  IFeedsEnum temp = feedfolder.Feeds as IFeedsEnum;
                   for (int count = 0; count < temp.Count; count++)
                 {
                        AddToCollection(temp.Item(count) as IFeed, feedfolder);
                        temp.GetEnumerator().MoveNext();
                  }
        }

       private void AddToCollection(IFeed feed, IFeedFolder feedfolder)
        {
                   IFeedsEnum temp = feed.Items as IFeedsEnum;
                    for (int i = 0; i < temp.Count; i++)
                     {
                             IFeedItem ifeeditem = temp.Item(i) as IFeedItem;
                             FeedItem feeditem = new FeedItem(ifeeditem);
                             feeditem.FeedName = feed.Name;
                             feeditem.FolderName = feedfolder.Name;
                             Add(feeditem);
                      }
         }

}

}

namespace IERSSInterop
{

public class FeedItem
{
            string _author;
            string _title;
            string _description;
            string _link;
            string _foldername;
            string _feedname;
            DateTime _pubdate;

            public string Author
            {
                        get { return _author; }
                        set { _author = value; }
            }

            public string Title
            {
                        get { return _title; }
                        set { _title = value; }
            }
            public string Description
            {
                        get { return _description; }
                        set { _description = value; }
            }
            public string Link
            {
                        get { return _link; }
                        set { _link = value; }
            }

            public string FolderName
            {
                        get { return _foldername; }
                        set { _foldername = value; }
            }

            public string FeedName
            {
                        get { return _feedname; }
                        set { _feedname = value; }
            }
            public DateTime PubDate
            {
                        get { return _pubdate; }
                        set { _pubdate = value; }
            }

            public FeedItem(IFeedItem ifeeditem)
            {
                        Author = ifeeditem.Author;
                        Title = ifeeditem.Title;
                        Description = ifeeditem.Description;
                        Link = ifeeditem.Link;
                        PubDate = ifeeditem.PubDate;
            }

}

}

Space and Time

Most widely understood ideas about motion of bodies come from Newton and Galileo Aristotle believed that natural state of a body was to stay at rest and it will move only when force is applied. Galileo conducted experiments (which manifested themselves in a fictitious but interesting story of dropping weights from the tower of Pisa), were used by Newton to formulate famous 3 newton’s laws of motion. Newton completely contradicted Aristotle’s idea of natural state of the body. According to Newtonian law, everybody  will continue to be in state of rest or state of motion unless it is acted upon by an unbalanced force. This idea was published in 1687 in newton’s Principia Mathematica.

Though Newton and Aristotle disagreed on the concept of Absolute space (famous ping pong example, where ping pong ball bouncing  at the same location on a table on a moving train would appear to be hit two different positions for an observer who is stationary, hence there is no absolute space), they do believed in Absolute time. So Newton introduced the concept of relativity of a position of the object in space but it still believes in absolute time meaning that time between two events would be measured same for all observers. e.g though place at which ping pong ball hits differs for two different observers, the time it takes the bounce the ball would be the same.

Apparently Newton’s law of physics fail to explain the motions of objects that travel at or near the speed of light. In 1676, Danish astronomer,Ole Christensen Roemerr, discovered the fact that light travels at finite but high speed while observing eclipses for Jupiter moons.

In 1865, James Clerk Maxwell, a British physicist, proposed a theory which unified partial theories used to explain electricity and magnetism . Interestingly since newton’s law had eliminated the idea of absolute rest, to explain the constant speed for the light (electromagnetic waves) that he invented to a matter called ether so that he can say that speed of light is constant relative to ether that fills up all the empty space.

In   1887, Albert Michelson (Later to become First American to win Nobel prize in sciences) and Edward Morley conducted experiment where they tried to measure speed of light in the direction of earth’s motion as well as right angle to it. If earth was orbiting around sun in ether, speed measure while in the direction of earth’s motion would be higher than the one measured while at right angle. BUT, experiments found the speed to  be exactly same!

Between 1887-1905 there were several attempts to explain this observation. In 1905, Albert Einstein, pointed out in his famous paper that idea of ether was unnecessary if we abolish the idea of absolute time. Einstein chose to disagree with fundamental beliefs of Aristotelian/Newtonian beliefs of absolute time. Similar point was made by leading french mathametician Henri Poincare.

With famous equation, E=Mc^2, there is direct relation between energy and mass of the body. So as energy of the object increases, it would also increase the mass of the energy since c=speed of light is constant. So as objects starts accelerating, it would add to its mass and in turn it will get difficult to accelerate.

Now here is the part I did not get from the book, Hawking mentions that at 10% of the speed of light there would be 0.5% increase in mass and at around 90%, mass would be more than double and as it approaches speed of light, mass would be so huge that it would be impossible to move the object and hence it is impossible to go beyond speed of light. So according to theory of relativity only ‘things” that can achieve speed of light are the things that don’t have any mass such as electromagnetic waves. I don’t understand why M would grow infinitely if the speed of light itself is finite.

So if the beam of light was observed by two observers (one stationary and one moving at X speed), then since…

  1. Distance = Speed * Time
  2. Distance is different

According to Newton’s law, since distance is different and time is absolute there would be two different speeds of light but in relativity theory if we make speed of light constant then guess what needs to change, TIME. That means same event would take two different times for two different observers.

A Brief History of ‘A Brief history of Time’

Started reading “A Brief History of Time” by Stephen Hawking. It is an old book and I had always heard a lot about it. Finally got around to reading it.. This book definitely makes me feel little stupid because some of the things he talks about, I just don’t get it. Maybe it will take few more books on relativity to really understand or maybe life time is not enough to understand the work done by these really smart people.  What fascinates me about theoretical physics is that the fact that the way it is explained in this book, it almost sounds like a philosophy rather than science, in fact it many of the early ideas/theories were put forward by philosophers. Maybe all different branches of science merge in one pure form of knowledge at certain point. First few pages, he talks about different theories or models that we had since as early as 340 BC to explain the nature of the universe. I just wanted to capture it in condensed version of it.

As Early as 340 BC, Greek philosopher Aristotle in his book, On the Heavens, talked about earth being a round sphere rather than flat plate. Three main observations made him think so….

    1. During eclipses of moon earth’s shadow on moon was always round, which would only be true of the earth was spherical.
    2. Different positions of north star from different positions of earth (Egypt and Greece). North start would appear directly over head at north pole Vs just over horizon when you go to Equator.
    3. Sails of the ship are seen before their hail when they come over the horizon

 Aristotle also though that earth was stationary and whole universe, the sun, the moon, the stars and planets revolved around the earth.

This idea was elaborated by Ptolemy in 2nd century AD. He put forward model where earth stood at the center of 8 other spheres (each one for the sun, the moon and fixed stars as well as 5 planets known at the time). Although it was not universally accepted, it was adopted by Christian church because it left room outside those 8 spheres for heaven and hell.

In 1514, polish priest Nicholus Copernicus, proposed a model where the sun stood at the center and earth and other planets moved around it in circular orbit.

After a century, around 1609, death blow to Aristotelian/Ptolemaic theory came when German, Johannes Kepler and Italian, Galileo Galilei supported Copernicus’s model for universe. In that year, Galileo started observing sky using the telescope. Galileo observed that Jupiter’s moons moved around it and since to make the really revolve around earth, they would have to follow extremely complicated path. In stead it was much easier to use Copernicus model and say that everything does not revolve around earth.

At the same time, Kepler, modified Copernicus theory and said planets move around sun in Eclipse and not in circle. This took care of mismatch between Copernicus model and actual observation made of planetary motion. Though, having found this by accident, he did not reconcile the orbit path with believed force (magnetic force) that was causing the motion.

This explanation was provided by Sir Isaac Newton in his, Philosophiae Naturalis Principia Mathematica, using gravitation force. He not only put forward theory that explained planetary motion but also invented mathematics needed to analyze the same that we all know “lovingly” as calculus. Newton realized that, according to his theory, gravitations force will eventually make everything collapse to one point. in a letter in 1691, to Richard Bentley, another leading thinker, Newton argued that it would have happened if Universe was finite space with finite numbers stars but if universe was infinite space with infinite number of starts more or less uniformly distributed, then they would have one central place to fall to.

Another big question plaguing the great minds of all ages has always been when/why/how the universe was created. One argument for the necessity of have a reason/time/method for creation was to have “first cause”. Within universe if you explain every event  has cause-effect relationship with another event then universe itself could only be explained in those terms.

St Augustine, in his City of God, accepted the date of 5000 BC as a date of creation of the universe according the book of genesis. Hawking mentions that is interesting that it is no so far away from 10000Bc, that is considered the end of last ice age and civilization really began. St Augustine also first introduced the idea that concept of time did not exist before the creation of universe. When asked “What did God do before he created universe”, he replied, Time was a property of the universe that God created and hence it did not exist before the universe was created”.

Aristotle and other Greek philosophers, believed that human race and its surrounding had existed for ever. The ancients had already considered the argument that history of civilization is more of a cycle that keeps on repeating that a one continuous progressive line.

In 1781, Philosopher, Immanuel Kant in his, Critique of Practical Reason (for some reason Hawking book calls it Pure instead of practical), called these questions antinomies because he though that there were compelling arguments for both side.

In 1929, Edwin Hubble, made a landmark discovery that distant galaxies are moving away, In other words, universe is expanding. this means at some point in time, all the matter in this universe was at a single point. This was the beginning of “Big Bang” theory.

Custom Journal in WPF

On Web  when you go to Page1 to Page2, you go back to Page1. This has become now universally accepted behavior. of course, it means, you are actually doing a server side trip to get a new page from the server.  With new AJAX based applications though since client side code now modifies the DOM on the fly, this helps with server side round trip part because actual communication with server now can happen asynchronously. But this means some times even though user thinks that they have gone from one state to another, there is no real Navigation. Since there is no real navigation happening, browser back/forward functionally seems broken. Windows Presentation Foundation has a concept of navigation which is pretty close to what you see on web. E.g. you can connect two pages using hyperlink and when in a host that support navigation, you click on a link you go from one page to another. This also gives you html+browser like journal where you can navigate back/forward using browser like buttons. When you run a Xbap (Xaml Browser Application) in Internet Explorer 7, you also get integration with browser back/forward navigation. With WPF also there are scenarios where user does not actually want to make real navigation but they want to change the state of the page but still have Back/forward paradigm work.

Let’s take a simple scenario where application is trying to show how a car would like in Red/Green/Blue color. When user changes the color, they can either hit back to go back to original color.

This is where concept of custom journal helps. Essentially, developer can not only make changes to rendered UI on the fly like all other platform, developer also has ability to store/replay the custom state in the journal so that it seems like navigation and works well with browser back/forward functionality.

There are three concepts that you need to understand here…

  1. NavigationService.AddBackEntry is the method that allows you to add a journal entry.
  2. CustomContentState: This is the base class that you will use to store the custom state information. It has a replay method when WPF calls when to restore the journal entry. You will override this method to restore the state.
  3. IProvideCustomContentState: This is the interface that you will need to implement on the page that is trying to store the custom state. This interface gets called using GetContentState when navigation using journal happens (using back/forward button).

So here is an simple example  that we talked about…

Scene1.xaml

I have a scene1.xaml that is the page which shows the car to user. Due to my great skills in drawing objects on the screen, car is represented here by a dockpanel that I will set color on. This page is set is that startup page for my application.

<Page
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″
xmlns:d=”http://schemas.microsoft.com/expression/interactivedesigner/2006″
Background=”#FFFFFFFF”
x:Name=”DocumentRoot”
x:Class=”ColorMyCar.Scene1″
Width=”590″ Height=”225″>
      <StackPanel>
          <DockPanel Height=”150″ Width=”550″ Background=”Red” Name=”myCar”/>
          <StackPanel Orientation=”Horizontal” HorizontalAlignment=”Center”>
                     <Button Height=”50″ Width=”100″ Content=”Red” Name=”btnRed” Click=”OnRedClick”/>
                     <Button Height=”50″ Width=”100″ Content=”Blue” Name=”btnBlue” Click=”OnBlueClick”/>
                     <Button Height=”50″ Width=”100″ Content=”Green” Name=”btnGreen” Click=”OnGreenClick”/>
          </StackPanel>
     </StackPanel>
</Page>

Scene1.xaml.cs

This has all the event handlers for each of the buttons that basically can change the background color for the dockpanel. It also implements the IProvideCustomContentState interface where you can return another journal entry with the current state of the page so that appropriate forward/backward journal entries can be created.

namespace ColorMyCar
{
          public partial class Scene1 : IProvideCustomContentState
         {
                  public Scene1()
                  {
                         this.InitializeComponent();
                    }

                  private void AddBackEntry(Brush oldbrush)
                 {
                         this.NavigationService.AddBackEntry(new CarColorJournalEntry(myCar.Background));
                   }

                public void OnRedClick(object sender, RoutedEventArgs e)
                 {
                        if (myCar.Background != Brushes.Red)
                        {
                                AddBackEntry(myCar.Background);
                                myCar.Background = Brushes.Red;
                        }
                 }

              public void OnBlueClick(object sender, RoutedEventArgs e)
              {
                       if (myCar.Background != Brushes.Blue)
                      {
                               AddBackEntry(myCar.Background);
                               myCar.Background = Brushes.Blue;
                       }
           }

           public void OnGreenClick(object sender, RoutedEventArgs e)
           {
                      if (myCar.Background != Brushes.Green)
                      {
                               AddBackEntry(myCar.Background);
                               myCar.Background = Brushes.Green;
                       }
           }

           CustomContentState IProvideCustomContentState.GetContentState()
           {
                      return new CarColorJournalEntry(myCar.Background);
           }
     }
}

First part of code that is marked as Red actually creates a custom journal entry. So everytime user clicks a button, code creates a CustomContentStateobject that can stores the current color of the car before it changes the color.

Second part of the that is marked gets called when users does journal navigation using back/forward button. It also returns CustomContentState object that stores the current color of the car before the other journal entry is restored.

CarColorJournalEntry

This is the code that actually implements a class that inherits from CustomContentState object and stores the color change data.This object needs to be serializable and hence the attribute Serializable. That is also the reason why actual color state is stored as string instead of brush that we get from the page. This string value of brush is restored using brushconverter class.

namespace ColorMyCar
{
        //A class that derives from CustomContentState must be serializable, 
       //which means it must either be augmented with SerializableAttribute 
       //or implement ISerializable
        [Serializable]
       class CarColorJournalEntry: CustomContentState
        {
                    private string _myCarColor;

                    public CarColorJournalEntry(Brush MyCar)
                    {
                            _myCarColor = MyCar.ToString();
                     }

         //The name that appears in the back and forward drop down navigation buttons displayed from NavigationWindow, Frame, or Microsoft Internet Explorer 7 Beta 3 navigation chrome is automatically generated, unless you return your own by overriding JournalEntryName property.
                  public override string JournalEntryName
                {
                         get {return “Journal: ” + _myCarColor.ToString();}
                 }

           //When a user navigates back through navigation history to a piece of content that has an associated CustomContentState object, Replay is called; you override Replay to provide an implementation that reapplies state to the current content.
                public override void Replay(NavigationService navigationService, NavigationMode mode)
              {
                        BrushConverter convertobrush = new BrushConverter();
                       (navigationService.Content as Scene1).myCar.Background = convertobrush.ConvertFromString(_myCarColor) as Brush;
                }

         }
}

In the overridden replay method, we use the brushconverterr class to convert the string back into  brush and then apply that brush to the dockpanel. To get to dockpanel, Content property on NavigationService  can be used.

When you compile the application you can get the journal behavior described in the above scenario.

Understanding Relationship between ClickOnce and Win32 SXS

Here is  an attempt to explain why things work the way they are described in the last article about FileOpenDialog. There are two things we did to make this work on Vista and XP.

  1. We removed assembly identity elements from the embedded manfiest
  2. We changed the Name for Assemblyidentity to Fileopen.package from FileOpen.exe

For the first part…

Every managed assembly has a CLR identity. This identity is described in the CLR metadata. Broadly speaking when you are dealing with a file that really does not need “SXS-ness” or “component-ness” there is no reason to specify an identity. If you want to plainly describe dependencies for a binary and you are never going to install it in Win32 GAC or no other win32 file(Manifest) is going to reference it, you don’t need to make up an identity for it. A lot of monolithic win32 Apps fall into this category. But the “cut & paste the manifest” legacy is making all these manifests have an identity even if it does not serve any real purpose. The VS 2005- When generating a mixed mode exes (Managed C++ exes) does not spit out an identity for an embedded sxs manifest. In our case, we had specified another ideneity. Now ideally tools should make sure that both these idenetities are same but in our case they are not (CLR: FileOpen, Embedded:FileOpen.exe). By removing the assemblyidentity from the embedded manifest, system uses identity from the managed metadata.

For the second part…

Ideally if the tools can pull out all the dependencies (Managed and UnManaged) from a given application dependency graph and put it in side the ClickOnce manifest, that would be perfect. In that case we would have replicated the flattened dependency graph of the app before the execution itself and it matches with the actual runtime dependencies. And of course any dependency that is not local would become a prerequisite. This is a perfect experience.

  1. All the local dependencies and pre-requisite dependencies are known upfront. ClickOnce can do the checks accordingly.
  2. One manifest serves for download and Activation (On XP)
  3. On Vista the app works the same way – only difference being that activation originated from the embedded manifest

When you SXS manifest inside (embedded) and outside(ClickOnce application manifest) on vista it looks at inside one to setup the correct execution context but on XP it looks at the outside one. Since ClickOnce creates external SXS manifest (fileopen.exe.manifest) in this case, this is what gets used by system to setup the execution context. Since ClickOnce manifest in our case does not have prerequiste element that describes ComCtl32.dll dependency, app works on Vista but fails on XP. By naming the name of assemblyideneity element (FileOpen.package) we cause ClickOnce to create FileOpen.package.manifest  to be created in the store since ClickOnce appends .manifest to assembly identity name. This means Windows SXS infrastructure does not fine FileOpen.exe.manifest next to EXE on XP, so it falls back to internal manifest which has ComCtl32.dll information and hence it works. We have to choose between one manifest vs the other. If we dont want ClickOnce manifest to serve for the activation context we can change the name

WPF (Avalon) ClickOnce Application and FileOpenDialog

One the main reasons why WPF (Avalon) provided its own OpenFileDialog is to provide access to file opening functionality in Internet zone security sandbox. This is done through SafeFileName and SafeFileNames properties. These properties can be used by applications running in security sandbox to provide functionality such as file uploads. These properties essentially dont allow applications running in sandbox access to full path of the file.

These class is wrapper on Windows Shell File Open dialog. This dialog is implemented in ComCtl32.dll. By default, application will use old version of ComCtl.dll and dialog will look different than XP themed dialog. Here are two different dialogs and I have marked the differences between two dialogs as my non-designers eyes can see.

When you run a Avalon application that uses OpenFileDialog class it does not bind against the version 6.0 of the ComCtl32.dll. You can get your application to bind against that particular version by embedding a manifest that specfies the version of the ComCtl32.dll that applications wants to use.

So this post is about two things…

  1. How to get WPF (Avalon) exe to load the right version of ComCtl32.dll so that you get right themed file open dialog on XP as well as Vista.
  2. How to get WPF (Avalon) ClickOnce applications to load the right version of ComCtl32.dll.

To get to there we will start with simple Application that invokes the OpenFileDialog.

Application Invoking FileOpenDialog

This is piece of cake but I listed it down here for completion sake. Also it is relevent to know that to get the right theme you dont need to change the actual code in the application.

Window1.Xaml

<Window x:Class=”FileOpen.Window1″
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”FileOpen” Height=”300″ Width=”300″>
<StackPanel>
<Button Height=”45″ Margin=”77,70,85,0″ Name=”button1″ VerticalAlignment=”Top” Click=”OnClick”>File Open</Button>
</StackPanel>
</Window>

Window1.xaml.cs

using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;

namespace FileOpen
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>

          public partial class Window1 : System.Windows.Window
         { 
                     public Window1()
                    {
                            InitializeComponent();
                     }

                     void OnClick(object sender, RoutedEventArgs e)
                      {
                             Microsoft.Win32.OpenFileDialog fOpen = new  Microsoft.Win32.OpenFileDialog();
                             fOpen.ShowDialog();
                       }
         }
}

Compiling this application will get you top dialog in the above picture. It will not have right theme.

EXE to get XP/Vista Themed File Open Dialog

This is pretty straightforward and it is been documented.

  1. Create  a file named embed.manifest (you can name it anything) and paste following xml in it. Parts in red are the things that you will need to change to fit your application. version number is not the assembly version but the file version that you get by right clicking on EXE in explorer and go to properties of that file.
  2. It looks like this<?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes”?>
    <assembly xmlns=”urn:schemas-microsoft-com:asm.v1″
    manifestVersion=”1.0″>
    <assemblyIdentity
    version=”1.0.2439.38563
    processorArchitecture=”msil”
    name=”FileOpen.exe
    type=”win32″
    />
    <description>Description of Application</description>
    <dependency>
    <dependentAssembly>
    <assemblyIdentity
    type=”win32″
    name=”Microsoft.Windows.Common-Controls”
    version=”6.0.0.0″
    processorArchitecture=”X86″
    publicKeyToken=”6595b64144ccf1df”
    language=”*”
    />
    </dependentAssembly>
    </dependency>
    </assembly>
  3. Start Visual studio and Open exe of your application
  4. you will see resources view. Right click to add Resources
  5. Click On Import and browse to the file that you created in Step1
  6. Set the resource type to “RT_MANIFEST”
  7. Change the ID from 101 to 1
  8. Save the file and close

When you run this EXE, you get the second dialog in the above picture with correct theme.

WPF (Avalon) ClickOnce Application to Get Right Themed Dialog

Generating the ClickOnce application is very easy. I will not go into detail here. But since VS generates the binary, application manifest and deployment manifest.  WPF supports two types of ClickOnce applications. Browser hosted Applications (XBap) and Windows based Application (.application). Xbap run in a hosting process called Presentationhost.exe. Since Presentationhost.exe already specifies something like above in its manifest, Xbaps running in presentationhost.exe process automatically get the right theme for the OpenFileDialog .

Window Based Application is where it gets interesting. Since these applications run in their own process. Each EXE needs to have above manifest embedded in them. Since generated manifest contains hashes of binaires and manifests, you can not change the EXE without updating the manifest that describe the application deployment. Tool called mage helps here where you can just update the manifest with new EXE and resign them. Do the same with deployment manifest. Sounds so simple so far…

But, it did not work. When I tried to regenerate manifests (Application as well as Deployment), deployment failed. Hence this post 🙂

So here is what you need to do….

Manifest that you will embed in this case will be little different from what you see in step 2 above. It will not have AssemblyIdentity element. It will just have reference to Common Controls dll.

<?xml version=”1.0″ encoding=”UTF-8″ standalone=”yes”?>
<assembly xmlns=”urn:schemas-microsoft-com:asm.v1″
manifestVersion=”1.0″>
<description>Description of Application</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type=”win32″
name=”Microsoft.Windows.Common-Controls”
version=”6.0.0.0″
processorArchitecture=”X86″
publicKeyToken=”6595b64144ccf1df”
language=”*”
/>
</dependentAssembly>
</dependency>
</assembly>

Embed this manifest in the same way as above.

Now if you regenerate application and deployment manifests using mage. It works !!. It works great on Vista!! lets open the champagene  

……………………………………….

Wait a second but deployment still fails on XP!!

Tweak to make it work on XP  too is

Open the Application manifest (FileOpen.exe.manifest in this case).

Change following line…

<asmv1:assemblyIdentity name=”FileOpen.exe” version=”1.0.0.0″ publicKeyToken=”855aa50c7de35a70″ language=”neutral” processorArchitecture=”msil” type=”win32″ />

to (Change in red bold)

<asmv1:assemblyIdentity name=”FileOpen.package” version=”1.0.0.0″ publicKeyToken=”855aa50c7de35a70″ language=”neutral” processorArchitecture=”msil” type=”win32″ />

Now you can again use MAGE to resign the application manifest and Deployment manifest. Now tihs works on XP as well as Vista with right themes for your OpenFileDialog.