I must admit that coming up with a meaningful and relatively short title was really hard this time. Anyway, here is the scenario:

  • You have a page layout with Grid panel with several cells and one of the cells is occupied by a RadChart control instance.
  • Your requirements indicate that the grid layout should be flexible so you have placed several grid splitters in your panel as well.
  • Soon enough, one of your QAs decides to play around with the GridSplitter and notices that the application slows down and becomes irresponsive (and can even crash) when the splitter is dragged around in a hectic fashion.

Grid Layout

 

The problem is related to the fact that every splitter movement (no matter how small it is) triggers update of the whole layout cycle (measure-and-arrange) for the grid contents, and the framework cannot cope with that for a control with complex layout mechanics like RadChart. The solution? You can take advantage of the RadChart Exporting API and improve the user experience of the application by replacing the actual chart control with a snapshot image during the splitter drag action (once the drag is complete, you can restore the actual control).

Here are the necessary steps in order to achieve this functionality:

  • First – set up the respective XAML for the application. Note that I am declaring an Image instance along RadChart that will serve as a placeholder for the chart snapshot. Also, in order to use the GridSplitter control, you will need to add the following xmlns mapping that is not defined by default in a Silverlight project: xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls":
<UserControl x:Class="SilverlightApplication1.MainPage" 
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mscorlib
="clr-namespace:System;assembly=mscorlib"
    xmlns:control
="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Charting"
    xmlns:chart
="clr-namespace:Telerik.Windows.Controls.Charting;assembly=Telerik.Windows.Controls.Charting"
    xmlns:controls
="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls">

   
<Grid x:Name="LayoutRoot">
       
<Grid.ColumnDefinitions>
           
<ColumnDefinition />
            <
ColumnDefinition />
        </
Grid.ColumnDefinitions>

       
<control:RadChart x:Name="RadChart1">
       
</control:RadChart>

       
<Image x:Name="ChartSnapshotImage" Visibility="Collapsed" Stretch="Fill" />
        <
controls:GridSplitter Grid.Column="0" 
                               MouseLeftButtonDown
="GridSplitter_MouseLeftButtonDown" 
                               MouseLeftButtonUp
="GridSplitter_MouseLeftButtonUp" 
                               VerticalAlignment
="Stretch" 
                               Background
="Red" 
                               Width
="5" />
    </
Grid>

</UserControl> 
  • Now in code behind you need to generate the snapshot that will be used in place of the chart control. Note in the code below that I am using timer to pre-generate the snapshot in order to ensure smooth experience for the user once he starts to drag the splitter around (that’s why I have disabled the chart animation so that the pre-generated snapshot contains the actual data at the earliest possible time). I am using the RadChart.ExportToImage(stream) method to get hold of the actual snapshot image:
public MainPage()
{
   
InitializeComponent();

    …

   
RadChart1.DefaultSeriesDefinition = new LineSeriesDefinition() { ShowItemLabels = false, ShowPointMarks = false };
   
RadChart1.DefaultView.ChartArea.EnableAnimations = false;
   
RadChart1.ItemsSource = data;

   
this.PreGenerateChartSnapshot();
}

private void PreGenerateChartSnapshot()
{ 
   
// Pre-generate the snapshot in order to avoid the lag on first splitter resize.
    DispatcherTimer timer = new DispatcherTimer();
   
timer.Interval = TimeSpan.FromMilliseconds(50);
   
timer.Tick += this.TimerTick;
   
timer.Start();
}

private void TimerTick(object sender, EventArgs e)
{
    // Generate the snapshot only once.
   
(sender as DispatcherTimer).Stop();

   
this.GenerateChartSnapshot();
}

private void GenerateChartSnapshot()
{
    using (MemoryStream stream = new MemoryStream())
   
{
       
RadChart1.ExportToImage(stream);
       
BitmapImage bitmapImage = new BitmapImage();
       
bitmapImage.SetSource(stream);
       
ChartSnapshotImage.Source = bitmapImage;
   
}
}
  • The final missing piece of code is adding the logic that toggles the visibility of RadChart and the snapshot Image on GridSplitter MouseDown / MouseUp respectively (setting the Visibility to Collapsed effectively disables any layout updates for the chart control):
private void GridSplitterMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
   
RadChart1.Visibility = System.Windows.Visibility.Collapsed;
   
ChartSnapshotImage.Visibility = System.Windows.Visibility.Visible;
}

private void GridSplitterMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
   
RadChart1.Visibility = System.Windows.Visibility.Visible;
   
ChartSnapshotImage.Visibility = System.Windows.Visibility.Collapsed;
}

 

You can find attached a sample application as well.

 

Hope this helps.


About the Author

Vladimir Milev

Vladimir Milev is a developer manager.

Comments

Comments are disabled in preview mode.