Recently we struggled with the performance of our WPF application. Everything was slow. Changing size of the window - slow, maximizing - slow, refreshing - slow. Then I looked at CPU - it was using CPU in idle. Something definitely wasn't right.
And then I found WPF Performance Tool
Using this tool I found that we had animation running in the background. We used it to indicate that something is going on when we do asynchronous calls, and then we hided animated elements with visibility. Well, even being invisible, animation continued to use resources, and very noticeably
Here I will post problems I and my colleagues met and solutions we found.
Friday, November 26, 2010
Performance in WPF
Monday, November 15, 2010
app.config file and MSI
There are so many examples in the internet demonstrating how to update your app.config file during installation. But these examples never tell you about consequences of this action. And there are some.
If you use installer from Visual Studio 2008 or 2010, you probably use RemovePreviousVersion property, by setting it to true. However, starting from 2008, the the implementation of this feature changed significantly, so significantly that it has nothing to do with the name of the property. Previous version is no longer uninstalled, and there some rules for updating old files with new ones. While "versioned" files handled straightforwardly, by replacing the file with newer version, content files are replaced only if they were not changed after previous installation.
Back to updating app.config files during installation. When you changing them during installation, you change them from those that were included in your .msi package. That means it will be ignored in the next update.
See this link. http://msdn.microsoft.com/en-us/library/aa370531.aspx
Wednesday, August 18, 2010
Images in Toolbar for Disabled buttons
I started to use ToolBar control in our WPF application. The problem is that when toolbar buttons have only images, these images are not grayed when button is disabled. The solution I googled (and little bit modified) is this:
<Style x:Key="{x:Static ToolBar.ButtonStyleKey}" TargetType="{x:Type Button}">
<!-- To support graying images when button is disabled-->
<Style.Resources>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}, AncestorLevel=1}, Path=IsEnabled}" Value="False">
<Setter Property="Opacity" Value="0.30"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Style.Resources>
<Setter Property="Margin" Value="3"/>
</Style>
Make sure this style is available in you resources.
Update:
When I tried to put images as content for the button I got an error "Specified element is already the logical child..."
So, my styles look like this:
<!-- Cannot use Content to display image to avoid "specified element is already the logical child" error -->
<Style x:Key="ToolButton_Edit" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
<Setter Property="ToolTip" Value="Edit"/>
<!--<Setter Property="Content">
<Setter.Value>
<Viewbox>
<Image Source="pack://application:,,,/Actsoft.Styles;Component/ToolImages/icons-paper-edit.png"/>
</Viewbox>
</Setter.Value>
</Setter>-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Image Source="pack://application:,,,/Actsoft.Styles;Component/ToolImages/icons-paper-edit.png"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
One more update
OK, now I lost the "Mouse Over" effects. So, I copied existing Button Template, modified it with putting Image instead of ContentPresenter and "stole" Tag property for Image Source.
Here is what I got now, hope this is the last change.
<Style x:Key="{x:Static ToolBar.ButtonStyleKey}" TargetType="{x:Type Button}">
<!-- To support graying images when button is disabled-->
<Style.Resources>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}, AncestorLevel=1}, Path=IsEnabled}" Value="False">
<Setter Property="Opacity" Value="0.30"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Style.Resources>
<Setter Property="Margin" Value="3"/>
<!-- To support images -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="Bd"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<Image Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="Bd" Value="#FF3399FF"/>
<Setter Property="Background" TargetName="Bd" Value="#FFC2E0FF"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="BorderBrush" TargetName="Bd" Value="#FF3399FF"/>
<Setter Property="Background" TargetName="Bd" Value="#FFC2E0FF"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="BorderBrush" TargetName="Bd" Value="#FF3399FF"/>
<Setter Property="Background" TargetName="Bd" Value="#FF99CCFF"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- now buttons styles -->
<Style x:Key="ToolButton_Edit" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
<Setter Property="ToolTip" Value="Edit"/>
<Setter Property="Tag" Value="pack://application:,,,/Actsoft.Styles;Component/ToolImages/icons-paper-edit.png"/>
</Style>
<Style x:Key="ToolButton_Save" TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
<Setter Property="ToolTip" Value="Save"/>
<Setter Property="Tag" Value="pack://application:,,,/Actsoft.Styles;Component/ToolImages/icon_save.png"/>
</Style>
Wednesday, June 30, 2010
TFS 2010 build machine workspace issues
When we converted from TFS 2008 to TFS 2010 everything seemed fine. But then I created new build definition and got this error:
The path XXX
OK, I talked to IT who made the setup and they told they changed Build agent Working directory to
$(BuildDirectory)\$(BuildDefinitionPath)\$(BuildDefintionId) (added BuildDefinitionId).
When we tried to reverse it back we got the same error message for old converted builds, the new one started to work. Going back and forward, trying to clear workspace with tf.exe command we couldn't resolve it. Either one or another build failed.
Finally, I asked him to change Wokring Directory to some completely new folder, suspecting that we had conflict with some old settings in workspaces that we could not clean. And it helped. So, we may still have some old settings somewhere, but changing working folder to new one we had clean start and it's working so far.
Tuesday, June 29, 2010
Reporting Services and Web farm
So, we switched to using Microsoft Reporting Services instead of Local reporting. However we had one issue with our configuration. We have our web site running in a farm with two servers with NLB implemented. Also, we have one server where we have our Reporting Services running. Since reports not that critical for our application, we decided just to have one server for that.
What we found though, is very often we got this message: ReportServerException: Execution 'uj2yx4bxgzfvwsvegkwuex45' cannot be found (rsExecutionNotFound)
While first googling suggested problems with timeout, I was pretty sure it's not the issue, and started to google specifically for this error and load balancing.
What we found out is that Reporting Services create some session to handle subsequent requests. Since we have cluster, those requests come from different servers. The problem though is that in our configuration requests came from different users, particulary
Our solution was simple, we configured our web site to run under dedicated user, the same for both instances.
Here are links that helped me:
http://social.technet.microsoft.com/Forums/en-US/sqlreportingservices/thread/1eb12568-bfea-4e4e-bd09-1f09b055c595
http://www.andypotts.com/Blog/2009/03/30/rsExecutionNotFoundWithReportViewerInALoadBalancedReportingServicesEnvironment.aspx
Tuesday, April 13, 2010
Simple way of editing enumerators using ComboBox in WPF
Here is the simple way of setting ComboBox for editing values of some enum type.
I would not use in application for production, since it's not localizable etc. But when I need to quicky write some testing application, it works perfect.
First, put this to your resource section:
<Window.Resources>
<ObjectDataProvider
MethodName="GetValues"
ObjectType="{x:Type sys:Enum}"
x:Key="keyName">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="a:enumType" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
Then, in your combobox use it like this:
<ComboBox ItemsSource="{Binding Source={StaticResource keyName}}" SelectedItem="{Binding propertyName}">
Friday, February 12, 2010
Get Reflector Pro Beta
If you google Reflector and the open cached version of the page, you can download Beta version of Reflector Pro that integrates with Visual Studio. I assume it will be released soon for money.