WPF performance for large number of elements on the screen - wpf

Im currently trying to create a Scene in WPF where I have around 250 controls on my screen and the user can Pan and Zoom in and out of these controls using the mouse.
I have run the WPF Performance Suite tools on the application when there are a large number of these controls on the screen (i.e. when the user has zoomed right out) the FPS drops down to around 15 which is not very good.
Here is the basic outline of the XAML:
<Window>
<Window.Resources>
<ControlTemplate x:Key="LandTemplate" TargetType="{x:Type local:LandControl}">
<Canvas>
<Path Fill="White" Stretch="Fill" Stroke="Black" StrokeThickness="1" Width="55.5" Height="74.687" Data="M0.5,0.5 L55,0.5 L55,74.187 L0.5,74.187 z"/>
<Canvas x:Name="DetailLevelCanvas" Width="24.5" Height="21" Canvas.Left="15.306" Canvas.Top="23.972">
<TextBlock Width="21" Height="14" Text="712" TextWrapping="Wrap" Foreground="Black"/>
<TextBlock Width="17.5" Height="7" Canvas.Left="7" Canvas.Top="14" Text="614m2" TextWrapping="Wrap" FontSize="5.333" Foreground="Black"/>
</Canvas>
</Canvas>
</ControlTemplate>
</Window.Resources>
...
<local:LandControl Width="55.5" Height="74.552" Canvas.Top="xxx" Template=" {StaticResource LandTemplate}" RenderTransformOrigin="0.5,0.5" Canvas.Left="xxx">
<local:LandControl Width="55.5" Height="74.552" Canvas.Top="xxx" Template=" {StaticResource LandTemplate}" RenderTransformOrigin="0.5,0.5" Canvas.Left="xxx">
<local:LandControl Width="55.5" Height="74.552" Canvas.Top="xxx" Template=" {StaticResource LandTemplate}" RenderTransformOrigin="0.5,0.5" Canvas.Left="xxx">
<local:LandControl Width="55.5" Height="74.552" Canvas.Top="xxx" Template=" {StaticResource LandTemplate}" RenderTransformOrigin="0.5,0.5" Canvas.Left="xxx">
... and so on...
</Window>
Ive tried to minimise the details in the control template and I even did a massive find and replace of the controls to just put their raw elements inline instead of using a template, but with no noticeable performance improvements.
I have seen other SO questions about this and people say to do custom drawing, but I dont really see how that make sense when you have to zoom and pan like I do.
If anyone can help out here, that would be great!
Mark

This may sound trite but - There is no free lunch.
I have worked on [this][1] for the past two years. That product consists of 4 browsers whose interfaces are primarily ZUIs. All but the Atlas use Visuals for their graphic rendering and there were a lot of lessons learned and a few deadends.
FrameworkElements are not your friend. The FE engine on a modern GFX card and CPU maxes out at about 500-600 elements, but it depends on their complexity. FE's are about 10x as heavy as Visuals.
Text will significantly impact your framerate. Rendering curves are expensive See [Robby Ingebretsens post][4] for hints on using animated text
Culling is important, but adding/removing from the VisualTree is expensive. Collapsing/Hiding is kind of a compromise.
In WPF 3.5 you have 2 choices - Program down to the Visuals layer or use something like the [Planerator][2] then manipulate the camera to Pan and Zoom but it requires your users have a good gfx card.
In WPF 4.0 things are much better due to something called [Cached Composition][3]. It works for the same reason the Planerator works. The GFX card is rendering your controls to a bitmap and is panning zooming the bitmap.
Using this in 4,0 is easy - Setup .CacheMode for your most expensive FrameworkElements and things will get a lot faster. You also have the ability to control how text is anti-aliased and the scales at which bitmaps will get regenerated (EnableClearType and RenderAtScale)
In my Atlas browser I could display well over 700 pieces of text + simple rectangles without losing interactivity of panning and zooming. Before 4 the map was unusable.
Getting better interactive performance takes time, targets and measurement. Good luck.
[Kael Rowan][5] has an excellent series of articles on a ZoomableCanvas he is working on. It uses a Quadtree and PriorityQueue for implementation and will let you implement semantic zoom.
Update: 8-07-10 Added Text hints and ZoomableCanvas links
[1]: http://globible.com
[2]: https://learn.microsoft.com/en-us/archive/blogs/greg_schechter/
[3]: https://learn.microsoft.com/en-us/dotnet/api/system.windows.media.bitmapcache?redirectedfrom=MSDN&view=net-5.0
[4]: https://web.archive.org/web/20101210104554/http://blog.nerdplusart.com:80/archives/making-the-most-of-silverlight-text-rendering?
[5]: https://learn.microsoft.com/en-us/archive/blogs/kaelr/

You should be able to use custom drawing in combination with Transformations to achieve the zooming and panning you need. You'll probably need to do some basic culling to prevent elements that are not within the viewport from rendering...but the transformation capabilities of WPF are pretty rich, and you should gain a decent amount of performance with it.
Transforms Overview
How to: Scale an Object
How to: Translate an Element

Related

Geometry drawing in WPF

I'm kinda new to all this geometry part, but I can see it gives me the ability to draw basically whatever I want. I can't find a good manual that teaches how to create whatever image I have in my head. I really hoped to find some kinda painter that extracts the data of the geometry for me but no luck so far.
For example, I found this online:
<Geometry x:Key="MagnifierIconGeometry">M44,12 C32,12 22,22 22,34 22,46 32,56 44,56 56,56 66,46 66,34 66,22 56,12 44,12z M44,0 C63,0 78,15 78,34 78,53 63,68 44,68 40,68 36.5,67.5 33,66 L32.5,66 14,90 0,79.5 18,55.5 17,55 C13,49 10,42 10,34 10,15 25,0 44,0z</Geometry>
You can tell by the name what it draws, but I would like to know how to do it myself?
If anyone could point me to a manual/any kind of program that would be fantastic!
Thanks.
To display vector graphics, in this case you can use the Path like this:
<Window.Resources>
<Geometry x:Key="MagnifierIconGeometry">M44,12 C32,12 22,22 22,34 22,46 32,56 44,56 56,56 66,46 66,34 66,22 56,12 44,12z M44,0 C63,0 78,15 78,34 78,53 63,68 44,68 40,68 36.5,67.5 33,66 L32.5,66 14,90 0,79.5 18,55.5 17,55 C13,49 10,42 10,34 10,15 25,0 44,0z</Geometry>
</Window.Resources>
<Grid>
<Path Data="{StaticResource MagnifierIconGeometry}"
Width="30"
Height="30"
Fill="Aqua"
Stretch="Fill" />
</Grid>
Output
More info
For more information you can see this:
MSDN: Shapes and Basic Drawing in WPF Overview
Charles Petzold: Vector Graphics and the WPF Shape Class
Graphics in WPF
Source of vector images
The www.modernuiicons.com contains a huge amount of vector images that you can use in your applications.
Program for working with vector images
To work with vector graphics you can use Microsoft Expression Blend:
MSDN: Drawing overview
Convert SVG to XAML and use it in Silverlight or WPF

WPF Zoom but keeping some items width, height and position

What I want to get is any way for making zoom on a collection of controls but keep the width, height and position for a subset of this controls. I have seen the question How to keep element size while WPF zoom in and out?, seems very much to what I want, but is not answered and also is not very explicit so a will improve the question.
Currently I'm using the Zoombox control that comes with the WPF Toolkit extended for .net framework 4.0, but I can change it. The structure that I have is the following:
<Border x:Name="drawRegionBorder" Grid.Column="1" Grid.Row="1" d:LayoutOverrides="Width, Height" BorderThickness="1" CornerRadius="4" BorderBrush="{StaticResource BorderBrush}" >
<xctk:Zoombox x:Name="zoomBox">
<Grid x:Name="drawRegion" Height="{Binding Height}" Width="{Binding Width}" HorizontalAlignment="Left" VerticalAlignment="Top" Background="{DynamicResource DrawBackgroundBrush}">
<Image Source="{Binding Image}" ... />
<ListBox x:Name="points" ItemsSource="{Binding Points}">
<ListBox.Template>
<ControlTemplate>
<Canvas IsItemsHost="True"/>
</ControlTemplate>
</ListBox.Template>
</ListBox>
<ListBox x:Name="paths" ItemsSource="{Binding SomePaths}">
<ListBox.Template>
<ControlTemplate>
<Canvas IsItemsHost="True"/>
</ControlTemplate>
</ListBox.Template>
</ListBox>
<!--... Others ...-->
</Grid>
</xctk:Zoombox>
</Border>
What I have here, are several list boxes, inside a grid, and the items panel for each list box is a Canvas, so each child (but the image) will be located inside a canvas and also each child will set the Canvas.X and Canvas.Y properties. So what I want, is any way of make the zoom (zoom-in or zoom-out), and keep the size of poitns (ellipsed) or paths...
Due the zoom, is a wpf's scale transformation, I suppose maybe a way for doing this when the zoom-in, make the zoom-out to the control I want to keep the size, and viceversa.
An example of the spected behavior is the blend designer, for instance when you zoom in a grid with rows and columns, the columns indicators keeps the original size, some thing like that is what I want for my points and paths.
I will appreciate any solution, maybe library, attached property, behavior or code.
Thaknks
The Blend designer uses Adorners for its manipulators. The sizes are calculated according to zoom the current zoom. If you're interested in using a similar technique, it's actually not that hard to do the calculation yourself.
You would create an adorner set to the Bounds of your control, then apply a scale factor according to the zoom in the designer. So if you zoom by 2.0, then you apply a RenderTransform of Scale 2.0 to your zoomed control, while calculating your adorner to be 2.0 of the ActualWidth and ActualHeight of the zoomed control (because those two properties do not take into account RenderTransformations). The nice thing is that since everything uses doubles, you get pixel perfect precision when doing this kind of calculation.
By using this approach, you gain the ability to zoom your main controls, while your manipulators simply scale to the zoomed controls, but maintain their control size throughout.
This tutorial is a good starting point. In the OnRender method is where you'd want to apply the scaling factor (by calculating the bounds of your adorner based on the UIElement's ActualWidth/Height, then multiplying by your scaling factor). How you apply your adorner depends a lot on application context - if you're doing a designer, then you'd want to apply the adorners in a design canvas or upon item selection.
I had made a research and found some useful things, for instace, if you are working with Adorners you can override the GetDesiredTransform in order to set what transform to do you want your adorner to do, here you can make null the transform made to your adorner. For more details see How to exclude scaleTransform from GeneralTransform in Adorner GetDesiredTransform method. In Msdn. But I think that if I want make it on controls I need to control the transform by my self.

WP7 Silverlight -- tapping image in a stackpanel scrollview to show that same picture in another grid

I am probably the absolute worst "coder" trying to create a Windows Phone 7 app, but I am in dire need of help, and some of you may even consider it to be laughably easy (which it probably is).
My problem: How on earth do I code the tapping of a picture from one grid to display as a bigger image on another grid?
And I'll elaborate:
I have an app page (in landscape mode only), with two grids splitting the screen.
The first grid (smallgrid) contains a Scrollviewer (small) with a Stackpanel (smallimages) of images reduced to 1/10 of their size within it, essentially showing thumbnails of images.
The second grid (contentgrid) is designed where once you tap on a thumbnail image from smallgrid that image will be shown in contentgrid
By default, balloon0 is displayed in the contentgrid and will change when a person taps on one of the smaller images.
I'll try to provide some mock code for this:
<grid x:name="smallgrid">
<scrollviewer x:name="small">
<stackpanel x:name="smallimages">
<image="balloon0.jpg"><image>
<image="balloon1.jpg"><image>
<image="balloon2.jpg"><image>
<image="balloon3.jpg"><image>
</stackpanel>
</scrollviewer>
</grid>
<grid x:name="contentgrid">
<image source="balloon0.jpg"><image>
</grid>
The code behind is where I need help. I am thinking I either use a button that once clicked, that image then replaces the image in contentgrid but I have no idea how to do that.
Or I can use a gesturelistener that when an image is tapped, it will replace the image in contentgrid... but I also don't know how to do that.
Any insight is helpful. Thank you for any help, as I am not a C# coder, let alone know the language or WP7 silverlight too well.
Be sure to add the Silverlight Toolkit assembly reference to your phoneapplicationpage element (and as a reference to the project):
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
You can use the GestureListener from the Silverlight Toolkit in your XAML like this (also be sure to add the name property to your large image):
<grid x:name="smallgrid">
<scrollviewer x:name="small">
<stackpanel x:name="smallimages">
<image="balloon0.jpg">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="smallImage_Tap" />
</toolkit:GestureService.GestureListener>
<image>
<image="balloon1.jpg">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="smallImage_Tap" />
</toolkit:GestureService.GestureListener>
<image>
<image="balloon2.jpg">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="smallImage_Tap" />
</toolkit:GestureService.GestureListener>
<image>
<image="balloon3.jpg">
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="smallImage_Tap" />
</toolkit:GestureService.GestureListener>
<image>
</stackpanel>
</scrollviewer>
</grid>
<grid x:name="contentgrid">
<image x:Name="BigImage" source="balloon0.jpg"><image>
</grid>
Then in your code-behind you can handle the event like this:
private void smallImage_Tap(object sender, GestureEventArgs e)
{
BigImage.Source = (sender as Image).Source;
}
If you look into the toolkit source code, then it appears that GestureListener.Tap event is generated whenever XNA TouchPanel detects Tap gesture. Intuitively I would expect that this happens whenever MouseLeftButtonUp event is generated. Ok, not always, but in the described type of interaction it is basically "always".
Hence I feel both these levels (XNA and Toolkit classes) as unnecessary overhead - at least for something as simple as the tap event. (Other negative consequences: App size increases as you have to include the toolkit, slower launching as more assemblies have to be loaded.)
Having said that, I would start by simply listening to MouseLeftButtonUp event as for example
<Image Source="123.jpg" MouseLeftButtonUp="smallImage_Tap" ImageOpened="..." ImageFailed="..."/>
I included also ImageOpened/Failed events. You can optionally use them to fine tune your app. They could solve problems such as too frequent tap events or image load failures.

How to center text around point using xaml

I would like to be able to place the word "hello" centered on a specific point. I need to do this completely in XAML without extra code. Best I can tell, all the text alignment properties/styles in XAML act on text within some bounding canvas or other element.
Since I don't know the length of the text I want to center, I can't center it using my own code.
The reason I need to solve the problem entirely in XAML is that I'm not using WPF to create the XAML, I'm writing it directly to an XML DOM. It will then be loaded into a Silverlight or WPF control for display.
In most graphic languages, including SVG, which is where my code originated, text can be aligned against a "stationary point" without a bounding box.
Any suggestions appreciated
(Yes, I know this question is old.)
The effectiveness of this solution may vary with the version of Silverlight or the .NET Framework you are using, and I haven't tried it with Silverlight for Windows Phone 7. I wrote a version for stand-alone WPF applications, and I wrote another version that also works in Silverlight.
First, the version that works in Silverlight and WPF. Please note that you will need to refactor the code a little bit if you aren't using a Canvas to provide an absolute position for the center of your TextBlock. For example, you may be using a TranslateTransform to position your text.
<Canvas>
<Canvas.Resources>
<ScaleTransform x:Key="transform" ScaleX="-1" ScaleY="-1" />
</Canvas.Resources>
<Grid RenderTransform="{StaticResource transform}" RenderTransformOrigin="-.25 -.25">
<TextBlock RenderTransform="{StaticResource transform}">
Hello!
</TextBlock>
</Grid>
</Canvas>
Second, the version that works only in WPF. It doesn't work in Silverlight because it depends on the presence of the Canvas.Right and Canvas.Bottom attached properties. UniformGrid isn't in Silverlight either, but that code could have been replaced by a regular Grid with 2 star-length rows and columns.
<Canvas>
<UniformGrid Rows="2" Columns="2"
DataContext="{Binding ElementName=textBox1}"
Width="{Binding Path=ActualWidth}"
Height="{Binding Path=ActualHeight}">
<Canvas>
<TextBlock Name="textBox1" Canvas.Right="0" Canvas.Bottom="0">
Hello!
</TextBlock>
</Canvas>
</UniformGrid>
</Canvas>
By the way, there may be more efficient ways to solve this problem available. I am making no guarantees!

What's the best way to display a video with rounded corners in Silverlight?

The MediaElement doesn't support rounded corners (radiusx, radiusy). Should I use a VideoBrush on a Rectangle with rounded corners?
Yeah - In a way you're both asking and answering the question yourself... But that is one of the two options I can think of. The reasons that might be a problem is that you lose some of the features/control you get from the MediaElement control. Another option is to do this:
Add your MediaElement to your page.
Draw a Rectangle on top of it and set wanted corner radius
Right click the rectangle in Blend and choose "Create Clipping Path"
Apply the clipping path to your MediaElement
That way you're still using a MediaElement control, but you can "clip" away what ever you want to get the desired rounded effect.
This example shows a clipped MediaElement. I know it's not easy to picture the vector path, but if you open it open in Blend you will see a rounded MediaElement.
<MediaElement
Height="132" Width="176" Source="Egypt2007.wmv"
Clip="M0.5,24.5 C0.5,11.245166 11.245166,0.5 24.5,0.5 L151.5,0.5
C164.75484,0.5 175.5,11.245166 175.5,24.5 L175.5,107.5 C175.5,
120.75484 164.75484,131.5 151.5,131.5 L24.5,131.5 C11.245166,
131.5 0.5,120.75484 0.5,107.5 z"/>
Using a rounded rectangle and a VideoBrush doesn't lose you any features/control over using a displayed MediaElement - since the element has to be in the Xaml anyway, you can control it using the usual Play/Pause/Stop methods, except that the playback happens in your rectangle. Using a clip region is a little unwieldy because it's harder to resize the region. A Rectangle is better because you have flexibility of layout.
<MediaElement x:Name="myElement" Source="clip.wmv" Visibility="Collapsed"/>
<Rectangle RadiusX="10" RadiusY="10" Width="640" Height="480">
<Rectangle.Fill>
<VideoBrush Source="myElement" Stretch="Uniform"/>
</Rectangle.Fill>
<Rectangle/>
The clip path with give you "hard" edges - you could also use an OpacityMask as well (though I imagine this requires much more processing power).
Try this
<Border CornerRadius="8" BorderBrush="Black" Background="Black" BorderThickness="3">
<MediaElement HorizontalAlignment="Center" VerticalAlignment="Top" Stretch="Fill" x:Name="Player" Source="/Assets/Videos/x.mp3" />
</Border>

Resources