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>

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.