Fitting the CustomFilterRow in a MVVM application and other changes

5 posts, 0 answers
  1. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 14 Jun 2010 Link to this post

    Hello,

    In our application we are using the CustomFilterRow on every grid so the users can easily filter these grids. Our application is based on the MVVM principle. This had some complications for the CustomFilterRow (http://demos.telerik.com/silverlight/#GridView/CustomFilterRow). I would like to sum them up and check if other users also dealt with these issues, so that Telerik might improve the CustomFilterRow in future releases, or just to get some feedback from Telerik whether all the issues are dealt with properly.

    1. DataTypes for columns are not known
    Problem: We are using a DomainDataSource that is exposed by the ViewModel. The GridView is bound to a collection of entities in the ViewModel. When we just attach the CustomFilterRow via telerikGridViewFilter:CustomFilterRow.IsEnabled="True" than it will try to create the specific ColumnHeaders based on the DataType of each column. This happens in the FilteringRow_Loaded callback method. The issue here is, that the datatypes are not known in this callback method.

    Solution: Use the GridView's DataLoaded event to create the ColumnHeaders. We've changed this in the contructor for CustomFilterRow. If you just set the CustomFilterRow.IsEnabled= true in the GridView's DataLoaded event, then the filtering row is not drawn.
    (CustomFilterRow.cs)
      public CustomFilterRow(RadGridView radGridView) 
            { 
                this.radGridView = radGridView; 
                this.radGridView.IsFilteringAllowed = false
     
                this.filteringRow = new FilteringRow(); 
                //use the DataLoaded event, because column datatypes are not known yet 
                //this.filteringRow.Loaded += this.FilteringRow_Loaded; 
                this.radGridView.DataLoaded += radGridView_DataLoaded; 
     
                this.radGridView.RowLoaded += this.radGridView_RowLoaded; 
                this.radGridView.ColumnDisplayIndexChanged += this.ReorderFilterCells; 
                this.radGridView.Grouped += this.RecalculateIndentPresenterWidth; 
            } 

    2. The CustomFilterRow was always indented wrong
    Problem: The FilterRow was always indented to the right. The filteringRow adds a rowindicator in its first column, without checking whether the GridView has RowIndicatorVisibility set to true.

    Solution: (CustomFilterRow.cs)
      private void radGridView_RowLoaded(object sender, RowLoadedEventArgs e) 
            { 
                if (e.Row is GridViewHeaderRow) 
                { 
                    Grid rootPanelGrid = (from c in this.radGridView.ChildrenOfType<Grid>() 
                                          where c.Name == "PART_RootPanel" 
                                          select c).FirstOrDefault(); 
     
                    if (rootPanelGrid != null && this.filteringRow.Parent == null
                    { 
                        rootPanelGrid.Loaded += this.rootPanelGrid_Loaded; 
                    } 
     
                    //added extra check to only add the indicator when the grid has one 
                    if (radGridView.RowIndicatorVisibility == Visibility.Visible) 
                    { 
                        Border indicator = (from c in this.radGridView.ChildrenOfType<Border>() 
                                            where c.Name == "PART_IndicatorPresenter" 
                                            select c).FirstOrDefault(); 
     
                        if (indicator != null
                        { 
                            this.filteringRow.CustomFilterRowRoot.ColumnDefinitions[0].Width = new GridLength(indicator.Width); 
                        } 
                    } 
                } 
            } 

    3. Changed the contextmenu for every FilterColumnHeader
    Problem: There was no way for the user to see what filteroperator is being used to filter the grid. The only visual cue the user has, was whether he is filtering or not. (filtericon lit up or not). On top of that, the items in the context menu were not translated.

    Solution: We've created our own FilterMenuItem class which is used in the ItemsSource of the RadContextMenu. This FilterMenuItem has a property IsChecked we can use via a Telerik ContainerBinding in the itemssource. (I've tried to use the RadMenuItem to achieve this, but I wasn't allowed to use a RadMenuItem in a DataTemplate). Now the user can see in the context menu which filteroperator is being used.
    (FilterMenuItem.cs)
    public class FilterMenuItem : ViewModelBase 
        { 
            private string header; 
            private bool isChecked; 
     
            public bool IsChecked 
            { 
                get 
                { 
                    return isChecked; 
                } 
                set 
                { 
                    if (isChecked == value) 
                        return
                    isChecked = value; 
                    NotifyPropertyChanged("IsChecked");                 
                } 
            } 
     
            public string Header 
            { 
                get 
                { 
                    return header; 
                } 
                set 
                { 
                    if (header == value) 
                        return
                    header = value; 
                    NotifyPropertyChanged("Header"); 
                } 
            } 
     
            public object Tag { getset; } 
        } 

    (TextColumnHeader.xaml)
    <local:ColumnFilterCell x:Class="XXX.TextColumnHeader" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:telerikNavigation="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Navigation" 
            xmlns:telerik="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls" 
        xmlns:local="clr-namespace:XXX.CustomFilterRow"
        <UserControl.Resources> 
            <telerik:ContainerBindingCollection x:Key="ContainerBindings"
                <telerik:ContainerBinding PropertyName="IsChecked" 
                            Binding="{Binding IsChecked, Mode=TwoWay}" /> 
            </telerik:ContainerBindingCollection> 
     
            <DataTemplate x:Key="ItemTemplate" 
                        telerik:ContainerBinding.ContainerBindings="{StaticResource ContainerBindings}"
                <TextBlock Text="{Binding Header}" /> 
            </DataTemplate> 
     
        </UserControl.Resources> 
        <Border> 
            <Grid> 
                <Grid.ColumnDefinitions> 
                    <ColumnDefinition Width="*"/> 
                    <ColumnDefinition Width="Auto"/> 
                </Grid.ColumnDefinitions> 
                <TextBox Name="tbValue" Grid.Column="0" Text="{Binding FilterDescriptor.Value, Mode=TwoWay}" HorizontalAlignment="Stretch" TextChanged="TextBox_TextChanged"/> 
                <ContentControl Grid.Column="1" Content="{Binding FilterVisibility}"
                    <telerikNavigation:RadContextMenu.ContextMenu> 
                        <telerikNavigation:RadContextMenu Name="filterContextMenu"
                                           ItemClick
    ="filterContextMenu_ItemClick"                                         ItemsSource="{Binding FilterOperators}" EventName="MouseLeftButtonUp" ItemTemplate="{StaticResource ItemTemplate}" /> 
                    </telerikNavigation:RadContextMenu.ContextMenu> 
                </ContentControl> 
            </Grid> 
        </Border> 
    </local:ColumnFilterCell> 

    (ColumnFilterCell.cs)
    ... 
    //Adds the FilterMenuItems to be used in the ContextMenu for every ColumnHeader 
    public void SetFilterDescriptors() 
            { 
                _filterOperatorMenuItems = new List<FilterMenuItem>(); 
                //Add the "No Filter" menu item 
                _filterOperatorMenuItems.Add(this.CreateFilterMenuItem(ActionResources.ResourceManager.GetString(String.Format("FilterOperator{0}""NoFilter")))); 
                foreach (System.Windows.Controls.FilterOperator filterOperator in this.FilterDescriptor.MemberType.GetApplicableFilterOperators()) 
                { 
                    _filterOperatorMenuItems.Add(CreateFilterMenuItem(ActionResources.ResourceManager.GetString(String.Format("FilterOperator{0}", filterOperator.ToString())), filterOperator)); 
                } 
            } 
    ... 
     /// <summary> 
            /// Callback method for when a filterMenuItem has been clicked. This method  
            /// will check the current filterDescriptor in the menu so user can see what  
            /// type of filtering is being applied. 
            /// </summary> 
            /// <param name="menuItem"></param> 
            protected void OnFilterMenuItemClick(FilterMenuItem menuItem) 
            { 
                if (menuItem != null
                { 
                    var item = (FilterMenuItem)menuItem; 
                    item.IsChecked = true
                    UncheckOtherMenuItems(item); 
                    if (item.Tag != null
                    { 
                        this.FilterDescriptor.Operator = (FilterOperator)item.Tag; 
                        this.IsFilterActive = true
                    } 
                    else 
                    { 
                        this.IsFilterActive = false
                    } 
                } 
            } 
     

    4. NullPointerExceptions when trying to filter on a DateTime column
    Problem: When clicking an item in the contextmenu for a column with datatype=datetime, a nullpointer exception was thrown when no datetime was filled in.

    Solution: Override OnFilterDescriptorChanged and set the Value of the filter to DateTime.Now by default.
    (DateTimeColumnHeader.xaml.cs)
    protected override void OnFilterDescriptorChanged(Telerik.Windows.Data.FilterDescriptor oldValue, Telerik.Windows.Data.FilterDescriptor newValue) 
            { 
                base.OnFilterDescriptorChanged(oldValue, newValue); 
     
                if (newValue != null
                { 
                    newValue.Value = DateTime.Now; 
                } 
            } 

    5. Could not tab out of NumericColumnFilter
    Problem: Only certain keys were allowed to be used in NumericColumnFilter.

    Solution: Changed the eventhandler to check which keys were pressed, and changed the textbox by a telerik maskedtextbox.
    (NumericColumnHeader.xaml)
     
                <!--<TextBox Grid.Column="0" Text="{Binding FilterDescriptor.Value, Mode=TwoWay}" KeyDown="TextBoxKeyDown"  />--> 
                <telerikInput:RadMaskedTextBox Name="tbValue" Grid.Column="0" MaskType="Numeric" Mask="d" Value="{Binding FilterDescriptor.Value, Mode=TwoWay}" SelectionOnFocus="SelectAll" ValueChanged="tbValue_ValueChanged" /> 

    I hope this was clear and that it provides Telerik some feedback on the way the FilteringRow is used in a real-life sample.

    Kind regards,
    Sodi We
  2. Yavor Georgiev
    Admin
    Yavor Georgiev avatar
    982 posts

    Posted 14 Jun 2010 Link to this post

    Hello Sodi We,

     The CustomFilterRow was never meant to be thoroughly comprehensive, but rather to guide our users to implementing more complex solutions, as you have done. However, we are very grateful for your feedback and we shall incorporate your suggestions in our next release.

    Best wishes,
    Yavor Georgiev
    the Telerik team

    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
  3. DevCraft banner
  4. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 15 Jun 2010 Link to this post

    I've noticed another issue, but haven't found a solution for it yet. When I use a DateTimeColumnHeader, the width of the filtercell is not set to the width of the actual column header (of the other way around). Now the actual  column header has a smaller width than the filtercell. The result is that the borders of all filtercells after a DateTimeColumnHeader won't align with the actual columnheader borders.

    Does someone have a solution for this?

    Thanks,
    Sodi We
  5. Yavor Georgiev
    Admin
    Yavor Georgiev avatar
    982 posts

    Posted 15 Jun 2010 Link to this post

    Hi Sodi We,

     I've tried to replicate your issue, but so far I have been unable to do so. The CustomFilterRow example itself contains a DateTimeColumnHeader and even though I have reordered that particular column in several different configurations, I have been unable to introduce the mis-alignment bug. Could you please try to replicate the issue using the code from the example, before you've performed any modifications on it?

    Sincerely yours,
    Yavor Georgiev
    the Telerik team

    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
  6. Sodi We
    Sodi We avatar
    160 posts
    Member since:
    Apr 2010

    Posted 15 Jun 2010 Link to this post

    I've noticed that in the method below the filtercell.ActualWidth is always 0.0 except for the BooleanColumnHeader. Instead I use the MinWidth property and I set this explicitly when creating the CustomFilterCell.

    void filterCell_Loaded(object sender, RoutedEventArgs e) 
            { 
                ColumnFilterCell filterCell = sender as ColumnFilterCell; 
                if (filterCell.Column.MinWidth < filterCell.ActualWidth) 
                { 
                    filterCell.Column.MinWidth = filterCell.ActualWidth
                } 
            } 

    And I've also added the following line to PlaceCommonHeaders:
     
    filterCell.SetBinding(ColumnFilterCell.WidthProperty, new Binding("ActualWidth") { Source = filterCell.Column }); 

Back to Top
DevCraft banner