Custom Expander In CellTemplate

5 posts, 1 answers
  1. yijie
    yijie avatar
    14 posts
    Member since:
    Jul 2011

    Posted 25 May 2012 Link to this post

    Hello,

    I was requested to create a listview to meet follow requirements with telerik RadGridView control in verion 2011.2.712.1040
    1)  1st column set to '*' in order to auto-fit parent frame.
    2)  If the text in first column takes up more than 10 lines (assumed 160px high) an expander will be added at top left of target cell.
    3) A button added to the top right of the RadGridView to 'Expand All' & 'Collapse All' rows. If there is no expandable row then the button set to disabled and all of expanders in each row should be collapsed.
    4) User could expand / collapse a single row if expandable.
    5) If user resizes the browser or the first column the UI should be updated accordingly.

    The UI is expected as atttached pics.

    Durning the development I was confused about how RadGridView to load and unload the row. It obvious it will unload the row not appearing in view port and reload the row reappearing in view port if user scroll up and down. So I was struggling to retain the states for each row... BTW if just put a single RadExpander in the celltemplate for each row it will behave incorrectly when scrolling up and down I assume the RadGridView doesn't get along well with content in celltemplate that is resizeable.

    My Questions are :

    1) I wonder is there another better solution to meet the requirement ?
    2) It is reported under certain circumstance if user pressing 'Expand All' button the IE will crash.

    Please help me there. Thanks in advance.

    I attaced the xaml and codebehind as very below.

    XAML :
    <telerik:GridViewDataColumn DataMemberBinding="{Binding SourceDescription}" Header="SourceDescription" IsGroupable="False" IsFilterable="False" Width="*" TextWrapping="Wrap">
    <telerik:GridViewDataColumn.CellTemplate>
    <DataTemplate>
    <Grid Loaded="Grid_Loaded">
    <Grid.ColumnDefinitions>
    <ColumnDefinition Width="40"></ColumnDefinition>
    <ColumnDefinition Width="*"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <telerik:RadExpander x:Name="reRawText" Header="" Width="26" Height="26"
    Grid.Column="0" VerticalAlignment="Top" HorizontalAlignment="Center"
    IsExpanded="True" Margin="4" IsTabStop="True"
    Expanded="reRawText_Expanded"
    Collapsed="reRawText_Collapsed" />
    <StackPanel Loaded="StackPanel_Loaded" Grid.Column="1" MaxHeight="{Binding ElementName=reRawText, Path=IsExpanded, Converter={StaticResource RawTextMaxHeightConverter}}" >
    <TextBox x:Name="tbRawText" TextWrapping="Wrap" Text="{Binding SourceDescription}"
    Style="{StaticResource TextBoxStyle}"
    Loaded="tbRawText_Loaded" />
    </StackPanel>
    </Grid>
    </DataTemplate>
    </telerik:GridViewDataColumn.CellTemplate>
    </telerik:GridViewDataColumn>


    Converter:
    public class RawTextMaxHeightConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                bool isExpanded = (bool)value;
                double maxHeight = 160;
                if (isExpanded == true)
                {
                    return double.MaxValue;
                }
                else
                {
                    return maxHeight;
                }
            }
     
            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }

    CodeBehind :
    public partial class MainPage : UserControl
        {
            private bool _Expanded = true;
     
            private const double RawTextMinHeight = 160;
     
            private Dictionary<int, bool> _RowTextIsExpandedList = new Dictionary<int, bool>();
     
            private Dictionary<int, bool> _RowTextIsExpanderVisibleList = new Dictionary<int, bool>();
     
            private Dictionary<int, RadExpander> _RowTextExpanderList = new Dictionary<int, RadExpander>();
     
            public MainPage()
            {
                InitializeComponent();
     
                this.DataContext = new MainPageViewModel();
            }
     
            private void radGridView1_RowLoaded(object sender, Telerik.Windows.Controls.GridView.RowLoadedEventArgs e)
            {
     
            }
     
            private void radGridView1_RowUnloaded(object sender, RowUnloadedEventArgs e)
            {
     
            }
     
            private void radGridView1_DataLoading(object sender, Telerik.Windows.Controls.GridView.GridViewDataLoadingEventArgs e)
            {
     
            }
     
            private void radGridView1_Loaded(object sender, RoutedEventArgs e)
            {
     
            }
     
            private void radGridView1_DataLoaded(object sender, System.EventArgs e)
            {
                this._RowTextIsExpandedList.Clear();
     
                this._RowTextIsExpanderVisibleList.Clear();
     
                this._RowTextExpanderList.Clear();
     
                this._Expanded = true;
     
                this.btnExpander.IsEnabled = false;
     
                this.btnExpander.Content = "Collapse All";
     
                this.radGridView1.UpdateLayout();
            }
     
            /// <summary>
            /// Step 1 - RawText Loaded
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void tbRawText_Loaded(object sender, RoutedEventArgs e)
            {
                TextBox tb = sender as TextBox;
     
                StackPanel spRawTextWrapper = tb.Parent as StackPanel;
     
                Grid gd = spRawTextWrapper.Parent as Grid;
     
                GridViewCell gvc = gd.Parent as GridViewCell;
     
                int rowIndex = this.radGridView1.Items.IndexOf(gvc.ParentRow.Item);
     
                RadExpander ex = VisualTreeHelper.GetChild(gd, 0) as RadExpander;
     
                if (ex != null && tb.ActualHeight > RawTextMinHeight)
                {
                    ex.Visibility = System.Windows.Visibility.Visible;
     
                    if (_RowTextIsExpandedList.Keys.Contains(rowIndex))
                    {
                        ex.IsExpanded = _RowTextIsExpandedList[rowIndex];
                    }
                    else
                    {
                        _RowTextIsExpandedList.Add(rowIndex, _Expanded);
                        ex.IsExpanded = _Expanded;
                    }
     
                    this.btnExpander.IsEnabled = true;
                }
                else
                {
                    ex.Visibility = System.Windows.Visibility.Collapsed;
                }
     
                if (_RowTextExpanderList.Keys.Contains(rowIndex))
                {
                    _RowTextExpanderList[rowIndex] = ex;
                }
                else
                {
                    _RowTextExpanderList.Add(rowIndex, ex);
                }
     
                if (_RowTextIsExpanderVisibleList.Keys.Contains(rowIndex))
                {
                    _RowTextIsExpanderVisibleList[rowIndex] = (ex.Visibility == System.Windows.Visibility.Visible);
                }
                else
                {
                    _RowTextIsExpanderVisibleList.Add(rowIndex, (ex.Visibility == System.Windows.Visibility.Visible));
                }
     
                // Check whether there is no visible expander in the grid
                bool isExpanderColumnVisible = this._RowTextIsExpanderVisibleList.Count(o => o.Value == true) > 0;
     
                double expanderColumnWidth = isExpanderColumnVisible == true ? 40 : 0;
     
                //for (int i = 0; i < this._RowTextExpanderList.Count; i++)
                //{
                //    Grid parentGrid = (Grid)_RowTextExpanderList[i].Parent;
                //    parentGrid.ColumnDefinitions[0].Width = new GridLength(expanderColumnWidth);
                //}
     
                this._RowTextExpanderList.ToList().ForEach(o =>
                {
                    Grid parentGrid = (Grid)o.Value.Parent;
                    parentGrid.ColumnDefinitions[0].Width = new GridLength(expanderColumnWidth);
                });
            }
     
            private void btnExpand_Click(object sender, RoutedEventArgs e)
            {
                RadButton btn = sender as RadButton;
     
                if (btn.Content.ToString() == "Expand All")
                {
                    foreach (var ex in this.radGridView1.ChildrenOfType<RadExpander>())
                    {
                        ex.IsExpanded = true;
                    }
     
                    for (int i = 0; i < this._RowTextIsExpandedList.Count; i++)
                    {
                        _RowTextIsExpandedList[i] = true;
                    }
     
                    btn.Content = "Collapse All";
                }
                else
                {
                    foreach (var ex in this.radGridView1.ChildrenOfType<RadExpander>())
                    {
                        ex.IsExpanded = false;
                    }
     
                    for (int i = 0; i < this._RowTextIsExpandedList.Count; i++)
                    {
                        _RowTextIsExpandedList[i] = false;
                    }
     
                    btn.Content = "Expand All";
                }
     
                _Expanded = !_Expanded;
            }
     
            private void radGridView1_SizeChanged(object sender, SizeChangedEventArgs e)
            {
                int count = 0;
     
                foreach (var ex in this.radGridView1.ChildrenOfType<RadExpander>())
                {
                    Grid gd = ex.Parent as Grid;
                    StackPanel spRawTextWrapper = VisualTreeHelper.GetChild(gd, 1) as StackPanel;
                    TextBox tb = VisualTreeHelper.GetChild(spRawTextWrapper, 0) as TextBox;
     
                    if (tb != null && tb.ActualHeight > RawTextMinHeight)
                    {
                        ex.Visibility = System.Windows.Visibility.Visible;
                        count++;
                    }
                    else
                    {
                        ex.Visibility = System.Windows.Visibility.Collapsed;
                    }
                }
     
                if (count > 0)
                {
                    this.btnExpander.IsEnabled = true;
                }
                else
                {
                    this.btnExpander.IsEnabled = false;
                }
     
                this.radGridView1.UpdateLayout();
            }
     
            private void reRawText_Expanded(object sender, Telerik.Windows.RadRoutedEventArgs e)
            {
                RadExpander ex = sender as RadExpander;
                Grid gd = ex.Parent as Grid;
                if (gd != null)
                {
                    GridViewCell gvc = gd.Parent as GridViewCell;
                    int rowIndex = this.radGridView1.Items.IndexOf(gvc.ParentRow.Item);
                    _RowTextIsExpandedList[rowIndex] = true;
                }
            }
     
            private void reRawText_Collapsed(object sender, Telerik.Windows.RadRoutedEventArgs e)
            {
                RadExpander ex = sender as RadExpander;
                Grid gd = ex.Parent as Grid;
                if (gd != null)
                {
                    GridViewCell gvc = gd.Parent as GridViewCell;
                    int rowIndex = this.radGridView1.Items.IndexOf(gvc.ParentRow.Item);
                    _RowTextIsExpandedList[rowIndex] = false;
                }
            }
     
            private void Grid_Loaded(object sender, RoutedEventArgs e)
            {
     
            }
     
            private void StackPanel_Loaded(object sender, RoutedEventArgs e)
            {
     
            }
     
            private void radGridView1_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e)
            {
                int count = 0;
     
                foreach (var ex in this.radGridView1.ChildrenOfType<RadExpander>())
                {
                    Grid gd = ex.Parent as Grid;
                    StackPanel spRawTextWrapper = VisualTreeHelper.GetChild(gd, 1) as StackPanel;
                    TextBox tb = VisualTreeHelper.GetChild(spRawTextWrapper, 0) as TextBox;
     
                    if (tb != null && tb.ActualHeight > RawTextMinHeight)
                    {
                        ex.Visibility = System.Windows.Visibility.Visible;
                        count++;
                    }
                    else
                    {
                        ex.Visibility = System.Windows.Visibility.Collapsed;
                    }
                }
     
                if (count > 0)
                {
                    this.btnExpander.IsEnabled = true;
                }
                else
                {
                    this.btnExpander.IsEnabled = false;
                }
     
                this.radGridView1.UpdateLayout();
            }
        }

  2. yijie
    yijie avatar
    14 posts
    Member since:
    Jul 2011

    Posted 30 May 2012 Link to this post

    I found the root cause of the IE crash issue the other day it seemed the private dictionary has been expanded to a large number of items which causing OutOfMemory exception i have fixed this but i still wonder and want to know is there another way to reach the same target. 
  3. Answer
    Ivan Ivanov
    Admin
    Ivan Ivanov avatar
    1217 posts

    Posted 04 Jun 2012 Link to this post

    Hi,

    It is quite a bad idea to use DataTemplates that resize themselves at runtime, as this may lead to issues with clipping, row virtualization, scrolling etc. A safer approach would be to use a CellTemplateSelector for that column that would alternate the "expanded" and "collapsed" templates, according to a property of your ViewModel. Would you please confirm whether such a solution would be a viable one for your case?

    All the best,
    Ivan Ivanov
    the Telerik team
    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>
  4. yijie
    yijie avatar
    14 posts
    Member since:
    Jul 2011

    Posted 04 Jun 2012 Link to this post

    Thanks for your reply I will try your solution and give feedback later. BTW I temporarily fix the issue by setting EnableRowVirtualization to 'False' since the performance is not that bad according to the size of return data in most of case.
  5. yijie
    yijie avatar
    14 posts
    Member since:
    Jul 2011

    Posted 04 Jul 2012 Link to this post

    Still can't not confirm just mark answered. Recently we will migrate to WPF and I will apply your approach by then.
Back to Top