Professional, Software

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

}

}

Advertisements
Standard

One thought on “Grouping Control: PaperStack

  1. Pingback: Navigation Support for Paperstack Control « Breakpoint Inside Recursive Mind

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s