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

}

}

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.

WPF Browser Hosted Applications (XBap) APTCA/Friend Assembly

Avalon browser hosted applications get trust based on the zone from which they deploy. This means Xbaps coming from Internet zone get Internet zone permission only and ones from Intranet zone get Intranet zone permissions etc…

These applications don’t have support for permission elevation where user can choose to give higher privileges than ones that application can get based on zone of deployment. There are three ways applications can demand more trust  that default permissionset allowed for the zone of deployment and demand can get satisfied.

  1. Deploy a custom CLR Security policy that modifies the default permissionset for the given zone
  2. Use a trusted publisher mechanism
  3. Use a fully trusted GAC‘ed assembly which allows partially trusted callers.

First way, is the most intrusive in terms of its impact. Only place it could be done is company intranet which is guaranteed to be secure. This means all the applications from anybody from that particular zone will get modified permission set. This can’t really work for Internet zone application because you would not deploy a custom CLR security policy on your machine that allows more trust to all the applications in the world.

Second way, is less intrusive than first one but it is still more open than the third one. By trusted publisher mechanism, you essentially say that all applications signed with that cert (which means all applications from particular publisher) can get permission set that they demand irrespective of their zone. This is still open check from end user’s perspective. because once this is granted anything from that publisher will run without a prompt. User needs to run full trust code (installer) on the machine to be able to do this.

Third way, can be less intrusive where installer runs to install a full trust assembly in the GAC which allows partially trusted callers. This in itself can be seen as worse than second option because GAC‘ed assembly that allows partially trusted caller means any code on Internet can call into that assembly. The security bar for this kind of assembly would/should be very high and it would mean you will need to think about not only bad used of your application calling that code but also *any* application calling that code. But you could lock down the code in this third way in such a way so that only your application and nobody else. It involves use of concept of Friend Assembly.  

For this to work three things need to happen…

  1. Assembly needs to be in the GAC to be found by Partially Trusted Code
  2. Assembly needs to to have AllowPartiallyTrustedCallers (assembly level) attribute so that it can be called by Partially Trusted Code
  3. Assembly needs to be marked InternalsVisibleTo (Assembly level) attribute so that it can be ONLY called by your Partially Trusted Code.

Doing first part, is pretty straightforward because on dev machine it can be done by GACUTIL or on user’s machine it can be done by installer.

Second part involves putting a assembly level attribute. You could put this in AssemblyInfo.cs that VS generates. It looks something like this…

//allows partially trusted caller
[assembly: System.Security.AllowPartiallyTrustedCallers]

Third part involves some more steps…

To use the InternalsVisibleTo in such a way that only your assembly can call into it, caller assembly needs to be strongly name signed. So lets say you will use FriendCaller.snk file that you can generate using sn.exe tool. You will use Public Key of that key to specific InternalsVisibleTo attribute. So it looks something like this…

//uses the strongname of the assembly to make internals visible
[assembly: InternalsVisibleTo(“FriendCaller,PublicKey=002400000480000094000000060200000024000
05253413100040000010001008b486e36a11b892dbe3c19af141bd9123c696
2ef7945fe10b1131e5bf6852d4a31ae192c21ad65ebe054fa94c6ac19c0c6ecd85
261ee2b0e7e4029aa8f569eddce5310c8731c8976405d19877c18163c6b0199
ffc4c617f8ab095b2b750dcf204e65bc000818ce36ce46e5c3f4d8a7ddfe6f9dfc3
99b65df1281a52a2ae5accc”)]

Note: I put line breaks to make it readable. In real code it would be one big line unless you have wrap set.

 First part of the attribute is pretty straight forward, it is the name of your assembly without the extension (.dll or .exe etc).

Here is how you generate second part…

  • sn -k FriendCaller.snk // Generate strong name key
  • sn -p FriendCaller.snk key.publickey // Extract public key from key.snk into key.publickey
  • sn -tp key.publickey > test.txt // dump the public key to text file instead of screen.
  • Copy paste in the public Key = part of the code above

Apart from these attributes the only code that I had in my sample FriendsCallee.dll is

using System;

namespace FriendCallee
{
        class MyPrivateClass
       {
                internal string PrivateFriendlyCall()
               {
                      return (“My friend is calling me”);
                }
        }
}

so all the classese and methods in my assembly are internal so nobody else could call into it from outside. As part of the post build process I added Gacutil command so that it gets added to GAC on my machine automatically. On users machine it would be installer doing it. Since you need to put this assembly in the GAC, it also needs to be strongly named signed but since it is irrelevant here, I am skipping that part.

Here is what I did to create an application…

  1. Create an XBAP from Visual Studio Template.(Assembly name FriendCaller.exe)
  2.  I will add reference to GAC’ed dll
  3. I will use the FriendCaller.snk to sign the assembly from Project/Properties Signing Tab.

Page1.xaml  of my applications looks like this…

<Page x:Class=”FriendCaller.Page1″
xmlns=”
http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”Page1″ Loaded=”OnPageLoaded”>
       <Grid>
               <TextBlock Name=”lblMessage”></TextBlock>
       </Grid>
</Page>

Page.xaml.cs looks like this…

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

                 public Page1()
                 {
                         InitializeComponent();
                  }

                 void OnPageLoaded(object sender, RoutedEventArgs e)
                  {
                            FriendCallee.MyPrivateClass mp = new FriendCallee.MyPrivateClass(); 
                            this.lblMessage.Text = mp.PrivateFriendlyCall();
                    }
          }
}

When you compile this application, I would have expected it to run and be able to call into a GAC’ed APTCA marked DLL even though I am calling internal method because FriendCaller.exe is friends with FriendsCallee.dll.

Unfortunately it did not work. CLR still threw security exception. For some reason, I needed to mark my application EXE (FriendCaller.exe) which was calling into that assembly also APTCA.

So when I added following code to AssemblyInfo.cs of my XBAP, it worked well as expected.

//allows partially trusted caller
[assembly: System.Security.AllowPartiallyTrustedCallers]

I have not yet figured out why I needed to add it or if there is better way to do this but once I do, I will post it.

Till then…you have a work around.

RSS

I read this RSS releated post today on  Dead 2.0 blog. I must say this is one of my favorite technology blog. It was great to re-confirm how far away we (people who work in software) with people in real world who are supposed to use the tecnology. I loved the way Dead2.0 used term “Vanish” in context of RSS and his explaination. I remember reading one of the design books where books took power steering as one of the best designs because user never notices it working for it explicitly. RSS needs to be like that. I love RSS.

It is interesting from the perspective of usage pattern for the task. Before RSS, the only way I can read content is I have to visit individual sites. RSS solves the problems where I can read the feeds by getting updates etc but now user has to sacrifice the experiences that web site provide. I think what we need to do here so people get advantage of RSS without losing benefits of great experience.

 Here are few things I was thinking about ….

App Integration with RSS Reading experience

Today RSS feed reader has some standard UI that allows users to read it. But there is nothing content publisher can do to make that experience better.  We could solve this  Ability for RSS feed to say what their preferred reader is. This might be just a custom tag in RSS that specifies link to the reader. Link can point user to get richer application. User goes and installs the reader if they want it. 

Problem it solves…

  • User does not have to be stuck with what default UI and can get richer experience if they wanted

Problem it does not solve…

  • It is not integrated experience.
  •  User still has to go out of his way first time to get the reader. 
  • Different application can build different usage semantic and will be confusing for users. 

It would be great if there was an ability to RSS feed to specify the app they want to use for reading. That app gets executed when browser hits that RSS feed. This could also be done via Add-in model where browser provides the default viwer application and content provider provides addin that enhances that experience. This way browser can keep control over the usage sementic and keep it in the same ballpark. It means we only have platform for DATA part of the RSS but maybe we should have plugin based platform for RSS applications that closely integrated with browsers.

Got to figure out Way to Monetize RSS feeds.

I could think of 3 ways to let content publisher make money

Ads

  • Showing the ads RSS has as they are
  • Controlling the Ad experience in default RSS reader (special tag for Ads that get displayed on the side of the page etc)
  • We could also think of ways to get LIVE search ads in the news reader based on the content (This wont work for the people who make money off their own ads but might be interesting for free content)

Subscription (Authentication)

 There might be people who make money off of the subscription based content. That means they need to identify who the owner is no matter what the reading model is. This means we have to support authentication of this content and possibility obfuscation.

 Licensing: Viwer App having to licensed

I can see people actually making money off of partner deals such as Newyork times recommends “Foo” reader build by BAR company. They push people to use FOO through their  RSS and they 10% of every new license that comes because of nytimes feed. This might be weak.