Tech Tock

Time is of the essence.

TranslateTransform vs Canvas Top/Left

Did a little performance testing of Canvas positioning vs TranslateTransform.  Turns out Canvas positioning is almost twice as fast.

Its just a single window with code behind. The asterisks stand in for marker symbols that could show Long/Short/Net/Gross is a custom chart.

image

You can download the code in the PositionPerformance directory of my .Net Demos project on GitHub or below.

 

MainWindow.xaml
  1. <Window x:Class="PositionPerformance.MainWindow"
  2.        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation&quot;
  3.        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml&quot;
  4.        Title="MainWindow" Height="350" Width="525">
  5.     <Grid>
  6.         <Grid.ColumnDefinitions>
  7.             <ColumnDefinition Width="*" />
  8.             <ColumnDefinition Width="Auto" />
  9.         </Grid.ColumnDefinitions>
  10.  
  11.         <ListBox ItemsSource="{Binding Data}">
  12.             <ListBox.ItemTemplate>
  13.                 <DataTemplate>
  14.                     <Canvas Width="10" Height="100" >
  15.  
  16.                         <!–<TextBlock Text="*"
  17.                                Canvas.Left="{Binding X}"
  18.                                Canvas.Top="{Binding Y}" />
  19.  
  20.                         <TextBlock Text="*"
  21.                                Canvas.Left="{Binding X1}"
  22.                                Canvas.Top="{Binding Y1}" />
  23.  
  24.                         <TextBlock Text="*"
  25.                                Canvas.Left="{Binding X2}"
  26.                                Canvas.Top="{Binding Y2}" />
  27.  
  28.                         <TextBlock Text="*"
  29.                                Canvas.Left="{Binding X3}"
  30.                                Canvas.Top="{Binding Y3}" />
  31.  
  32.                         <TextBlock Text="*"
  33.                                Canvas.Left="{Binding X4}"
  34.                                Canvas.Top="{Binding Y4}" />–>
  35.  
  36.  
  37.                         <!–<Canvas Width="10" Height="10">–>
  38.                         <TextBlock Text="*">
  39.                                 <TextBlock.RenderTransform>
  40.                                     <TranslateTransform X="{Binding X}" Y="{Binding Y}" />
  41.                                 </TextBlock.RenderTransform>
  42.                             </TextBlock>
  43.                             <TextBlock Text="*">
  44.                                 <TextBlock.RenderTransform>
  45.                                     <TranslateTransform X="{Binding X1}" Y="{Binding Y1}" />
  46.                                 </TextBlock.RenderTransform>
  47.                             </TextBlock>
  48.                             <TextBlock Text="*">
  49.                                 <TextBlock.RenderTransform>
  50.                                     <TranslateTransform X="{Binding X2}" Y="{Binding Y2}" />
  51.                                 </TextBlock.RenderTransform>
  52.                             </TextBlock>
  53.                             <TextBlock Text="*">
  54.                                 <TextBlock.RenderTransform>
  55.                                     <TranslateTransform X="{Binding X3}" Y="{Binding Y3}" />
  56.                                 </TextBlock.RenderTransform>
  57.                             </TextBlock>
  58.                             <TextBlock Text="*">
  59.                                 <TextBlock.RenderTransform>
  60.                                     <TranslateTransform X="{Binding X4}" Y="{Binding Y4}" />
  61.                                 </TextBlock.RenderTransform>
  62.                             </TextBlock>
  63.                             <TextBlock Text="*">
  64.                                 <TextBlock.RenderTransform>
  65.                                     <TranslateTransform X="{Binding X}" Y="{Binding Y}" />
  66.                                 </TextBlock.RenderTransform>
  67.                             </TextBlock>
  68.  
  69.                         <!–</Canvas>–>
  70.                     </Canvas>
  71.                 </DataTemplate>
  72.             </ListBox.ItemTemplate>
  73.             <ListBox.ItemsPanel>
  74.                 <ItemsPanelTemplate>
  75.                     <VirtualizingStackPanel Orientation="Horizontal" />
  76.                 </ItemsPanelTemplate>
  77.             </ListBox.ItemsPanel>
  78.         </ListBox>
  79.  
  80.         <StackPanel Grid.Column="1" Margin="10">
  81.             <Button Click="Button_Click">Run</Button>
  82.             <HeaderedContentControl
  83.                Header="WPF Time"
  84.                Content="{Binding ElapsedMilliseconds}"
  85.                TextBlock.TextAlignment="Right"
  86.                />
  87.             <TextBox x:Name="History" />
  88.         </StackPanel>
  89.     </Grid>
  90. </Window>

MainWindow.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Windows;
  6. using System.Windows.Controls;
  7. using System.Windows.Data;
  8. using System.Windows.Documents;
  9. using System.Windows.Input;
  10. using System.Windows.Media;
  11. using System.Windows.Media.Imaging;
  12. using System.Windows.Navigation;
  13. using System.Windows.Shapes;
  14. using System.Collections.ObjectModel;
  15. using System.Diagnostics;
  16.  
  17. namespace PositionPerformance
  18. {
  19.     /// <summary>
  20.     /// longeraction logic for MainWindow.xaml
  21.     /// </summary>
  22.     public partial class MainWindow : Window
  23.     {
  24.  
  25.         public ObservableCollection<Data> Data { get; set; }
  26.         Stopwatch sw = new Stopwatch();
  27.  
  28.         public MainWindow()
  29.         {
  30.             InitializeComponent();
  31.             DataContext = this;
  32.             Data = new ObservableCollection<Data>();
  33.         }
  34.  
  35.  
  36.  
  37.  
  38.         public long ElapsedMilliseconds
  39.         {
  40.             get { return (long)GetValue(ElapsedMillisecondsProperty); }
  41.             set {
  42.                
  43.                 SetValue(ElapsedMillisecondsProperty, value);
  44.  
  45.                 this.History.Text += "\n" + ElapsedMilliseconds.ToString();
  46.             }
  47.         }
  48.  
  49.         // Using a DependencyProperty as the backing store for ElapsedMilliseconds.  This enables animation, styling, binding, etc…
  50.         public static readonly DependencyProperty ElapsedMillisecondsProperty =
  51.             DependencyProperty.Register("ElapsedMilliseconds", typeof(long), typeof(MainWindow), new UIPropertyMetadata(0l));
  52.  
  53.         private void Button_Click(object sender, RoutedEventArgs e)
  54.         {
  55.  
  56.             Data.Clear();
  57.  
  58.             var r = new Random();
  59.  
  60.             foreach (var d in Enumerable.Range(0, 1000).Select(i =>
  61.                 new Data() {
  62.                     X = r.Next(1, 10), Y = r.Next(1, 100),
  63.                     X1 = r.Next(1, 10),
  64.                     Y1 = r.Next(1, 100)
  65.                 ,
  66.                     X2 = r.Next(1, 10),
  67.                     Y2 = r.Next(1, 100)
  68.                 ,
  69.                     X3 = r.Next(1, 10),
  70.                     Y3 = r.Next(1, 100)
  71.                 ,
  72.                     X4 = r.Next(1, 10),
  73.                     Y4 = r.Next(1, 100)
  74.                 }))
  75.             {
  76.                 Data.Add(d);
  77.             }
  78.            
  79.  
  80.             sw.Restart();
  81.  
  82.             Dispatcher.BeginInvoke(new Action(() => ElapsedMilliseconds = sw.ElapsedMilliseconds), System.Windows.Threading.DispatcherPriority.SystemIdle);
  83.  
  84.         }
  85.  
  86.        
  87.  
  88.  
  89.     }
  90.  
  91.     public class Data
  92.     {
  93.         public double X { get; set; }
  94.         public double Y { get; set; }
  95.         public double X1 { get; set; }
  96.         public double Y1 { get; set; }
  97.         public double X2 { get; set; }
  98.         public double Y2 { get; set; }
  99.         public double X3 { get; set; }
  100.         public double Y3 { get; set; }
  101.         public double X4 { get; set; }
  102.         public double Y4 { get; set; }
  103.     }
  104. }

January 21, 2013 Posted by | Uncategorized | , | Leave a comment

Another Thing about WPF Virtualization

Just a little follow-up to my virtualization post from the other day.  WPF virtualization works great. I had it working horizontally and vertically at the same time and with synchronized scrolling controls.

To do horizontal and vertical, just make a vertical items list and for each item, make it a horizontal list.  Works straight out of the box.

Unless:

Make sure your virtualized control isn’t wrapped in a scrollbar.  Even if its 3 controls outside, if a scrollbar is giving unlimited space to an ItemsControl you want to virtualize, it won’t work. A virtualizing control needs to be width (or height for vertical) constrained. I’d also bet that virtualization in a Canvas without setting a width on your ItemsControl would fail for the same reason, it just goes on and on.

January 14, 2013 Posted by | Uncategorized | , | Leave a comment

Another way to Check WPF Virtualization

If you Snoop a virtualized control you’ll only see ItemsContainers for the displaying items and a few cached ones if you’ve scrolled.  So if you have a collection of hundreds of items and you only see a dozen containers with Snoop, its because its virtualizing.

Snoop, the WPF Spy Utility

January 11, 2013 Posted by | Uncategorized | , , | Leave a comment

WPF Performance Tip

If you have a lot of controls displaying, layout passes can cause performance issues.  One way to avoid layout passes is to hide rather than collapse elements in converters or triggers. Collapsing an element will cause layout passes while hiding an element will preserve all spacing avoiding the need for a layout pass.

Similarly, frequently changing height and width can be slow in a window with a lot of controls.

If you’re looking to shave off a few hundred extra milliseconds these are worth trying.

January 9, 2013 Posted by | Uncategorized | , | Leave a comment

Reading like its 2009

WPF Control Development Unleashed: Building Advanced User Experiences

I’m reading fellow Lab49er Pavan Podilla’s book:

WPF Control Development Unleashed:
Building Advanced User Experiences

It has some nice advanced examples like the circular minute timer. You can get the book’s great demo app with code here.  Anybody working in WPF should read this book.

Here’s the Circular Minute Timer:

This demo has an interesting resizing issue:

When the window width is reduced, the Arc, which is a subclass of Shape, doesn’t resize.  Resizing is fine for increasing the height or width, or decreasing the height.  Its only decreasing the width that causes a problem.  The problem is that WPF respects the width of the Geometry and doesn’t force it to resize and since the arc starts drawing on the right side it never allows a smaller width to take effect.

I made a simple fix by having the Arc size itself the same as another control:

Code Snippet
  1. <Ellipse
  2.         StrokeThickness=”30″
  3.                 Margin=”5″
  4.         x:Name=”SizeGuide”
  5.                 />
  6. <Runner:Arc StrokeThickness=”30″
  7.             Stroke=”{TemplateBinding BorderBrush}
  8.             Margin=”5″
  9.            SizeGuide=”{Binding ElementName=SizeGuide}
  10.            >

The Ellipse named SizeGuide will automatically size itself and the Arc can use the ellipse actual height and width to size its own geometry:

Code Snippet
  1. doubleSizeWidth
  2. {
  3.     get
  4.     {
  5.         returnthis.SizeGuide == null ? this.RenderSize.Width : this.SizeGuide.ActualWidth;
  6.     }
  7. }
  8. doubleSizeHeight
  9. {
  10.     get
  11.     {
  12.         returnthis.SizeGuide == null ? this.RenderSize.Height : this.SizeGuide.ActualHeight;
  13.     }
  14. }
  15. privatePointPointAtAngle(doubleangle)
  16. {
  17.     doubleradAngle = angle * (Math.PI / 180);
  18.     doublexRadius = (this.SizeWidthStrokeThickness) / 2;
  19.     doubleyRadius = (this.SizeHeightStrokeThickness) / 2;

So, the resize works now:

January 3, 2013 Posted by | Uncategorized | | 1 Comment

Quick and Dirty WPF Performance Tester

Here’s a quick way to test WPF performance.  Set some dummy data, then log the the next time the UI thread is available via a Dispatcher.BeginInvoke.  This is a good way to show virtualization is working.  If you need more than this you’re either in high performance land or trouble.

WPF Tester
  1. publicclassViewModel
  2. {
  3.     int_timeTaken;
  4.     publicintWpfTimeTaken
  5.     {
  6.         get { return_timeTaken; }
  7.         set
  8.         {
  9.             CheckPropertyChanged(“WpfTimeTaken”, ref_timeTaken, refvalue);
  10.         }
  11.     }
  12.     privatevoidGenerateData()
  13.     {
  14.         //set data
  15.         varsw = newSystem.Diagnostics.Stopwatch();
  16.         Dispatcher.CurrentDispatcher.BeginInvoke(
  17.             newAction(() =>
  18.             {
  19.                 this.WpfTimeTaken = sw.ElapsedMilliseconds;       
  20.             }), System.Windows.Threading.DispatcherPriority.ApplicationIdle);
  21.     }
  22. }

December 18, 2012 Posted by | Uncategorized | , | Leave a comment

WPF Virtualization–Finicky?

I’m making a fun new control for a lot of data and needed to use a Virtualizing ItemsControl. We already had one in the project.  It looked like the example and was obviously intended to virtualize the ItemsControl. Upon testing, of course it didn’t work.  It gave exactly linear performance.

Virtualizing Stack Panel?
  1. <ItemsControl
  2. VirtualizingStackPanel.IsVirtualizing=”True”
  3. ScrollViewer.CanContentScroll=”True”
  4. ItemsSource=”{Binding Path=AccountViews.Tables[0]}”>
  5. <ItemsControl.ItemTemplate>
  6. <DataTemplate>
  7. <TextBlock
  8. Initialized=”TextBlock_Initialized”
  9. Text=”{Binding Path=Name}” />
  10. </DataTemplate>
  11. </ItemsControl.ItemTemplate>
  12. <ItemsControl.ItemsPanel>
  13. <ItemsPanelTemplate>
  14. <VirtualizingStackPanel />
  15. </ItemsPanelTemplate>
  16. </ItemsControl.ItemsPanel>
  17. <ItemsControl.Template>
  18. <ControlTemplate>
  19. <Border
  20. BorderThickness=”{TemplateBinding Border.BorderThickness}
  21. Padding=”{TemplateBinding Control.Padding}
  22. BorderBrush=”{TemplateBinding Border.BorderBrush}
  23. Background=”{TemplateBinding Panel.Background}
  24. SnapsToDevicePixels=”True”>
  25. <ScrollViewer
  26. Padding=”{TemplateBinding Control.Padding}
  27. Focusable=”False”>
  28. <Grid>
  29. <ItemsPresenter
  30. SnapsToDevicePixels=”{TemplateBinding UIElement.SnapsToDevicePixels}” />
  31. </Grid>
  32. </ScrollViewer>
  33. </Border>
  34. </ControlTemplate>
  35. </ItemsControl.Template>
  36. </ItemsControl>

Here’s the offending lines:

Virtualizing Antagonist
  1.      <ScrollViewer
  2. Padding=”{TemplateBinding Control.Padding}
  3. Focusable=”False”>
  4.          <Grid>
  5.              <ItemsPresenter
  6.     SnapsToDevicePixels=”{TemplateBinding UIElement.SnapsToDevicePixels}” />
  7.          </Grid>
  8.      </ScrollViewer>

 

That Grid wrapping the ItemsPresnter stops virtualization. It really has no business being there, but still…

As for WPF Virtualization being finicky, if you use one of the Controls That Implement Performance Features, such as ListBox, you shouldn’t have a problem (until you change the template).  OTOH, there are arguments against that too…

December 14, 2012 Posted by | Uncategorized | , , , | Leave a comment

Losing my Memory

After fixing the classic event handler reference memory leak, I uncovered a WPF memory leak in my application.  It seems that WPF itself has a memory issue in the way it tries to lazy load dictionary resources.

I had a lot of additional memory in snapshot diffs being taken up by DeferredAppResourceReference objects.  There seems to be a Hotfix that addresses the issue, but I found a decent code solution by Andrew S on MSDN.  This code will instantiate the dictionary objects on startup, avoiding the buggy MS lazy loading.

public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
WalkDictionary(this.Resources);

base.OnStartup(e);
}

private static void WalkDictionary(ResourceDictionary resources)
{
foreach (DictionaryEntry entry in resources)
{
}

foreach (ResourceDictionary rd in resources.MergedDictionaries)
WalkDictionary(rd);
}
}

 

 

Ironically, now one of the largest growth objects in my app is WeakReference.

January 31, 2011 Posted by | Uncategorized | , , | 1 Comment

WPF MetUp

I had a great time presenting at the .Net Meetup last week.  It was a lively discussion thanks to everyone who showed up.  David Barnhill gave me great support and his extensive knowledge and experience really added depth to the conversation.  Everyone’s feedback and prompting was just great and it was very interactive.

Thanks everybody.

Some of what we covered:

WPF is not the same old system.  Some older ways of doing things in WinForms are just alien to the WPF paradigm.  Extensive use of UI inheritance is out.  Use of templates and Prism is in.

WPF is 4 years old and still early in the adoption cycle.  Paradoxically, this is because its so good.  Its entirely revolutionary (at least compared to WinForms) and so different from what came before that in some ways its like starting over.  This leads to learning curves, staffing and project delivery issues during adoption.

The question is what does it really do for business?  The main thing is that you can do more.  Many things you may not have attempted in WinForms are done simply in WPF.

But how do you get started with WPF?  Same as any new technology I suppose:

  • Read the books.  WPF Unleashed is the top recommendation.
  • The videos on Channel9 are great.
  • There’s more videos from Microsoft here and here.
  • Jason Dolinger’s MVVM video is a classic.
  • Take training.
  • Hire an experienced WPF developer to join the team or bring in a consultant to help out on your first project.  A mentor is invaluable when starting out.
  • You can use WPF simply to start.  Don’t try to use every feature, just get the basics. At its most simple its not so different from WinForms.
  • You can have WPF controls, modules and/or pages in a WinForms app and vice versa so it can come slowly into an organization even with a legacy application.

September 27, 2010 Posted by | Uncategorized | , , , | Leave a comment

INotify Snippet Update

Here’s an update to my INotify snippet.  The only change is that I took out an unnecessary ref from the CheckPropertyChanged signature.  Now I’m hosting the code at google so its easier to keep updated.

protected bool CheckPropertyChanged<T>(string propertyName, ref T oldValue, (no ref here)  T newValue)

September 15, 2010 Posted by | Uncategorized | , , , , | Leave a comment

Follow

Get every new post delivered to your Inbox.