Here I will post problems I and my colleagues met and solutions we found.

Showing posts with label WPF. Show all posts
Showing posts with label WPF. Show all posts

Saturday, November 12, 2011

ResourceDictionary and memory leaks

I profiled our application for memory leaks recently, and finally get clear understanding for how ResourceDictionary can result in memory leaks or just excessive memory usage.


I can identify three problems:
  • MergedDictionaries
  • References from Control to ResourceDictionary
  • Using DropShadowEffect
MergedDictionaries:

The typical use of MergedDictionaries looks like this:


<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/X.Styles;component/TextBoxStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
This code suggests that there was a ResourceDictionary with Source /X.Styles;component/TextBoxStyle.xaml defined somewhere. The way ResourceDictionary is implemented, there will be two instances of ResourceDictionary objects created in the memory. The original one, and then the one that is added to the MergedDictionaries collection.

Solution:
The solution to this problem is to create your own implementation of ResourceDictionary and use it instead. One of the examples can be found here.

Then, our code will look like this:

<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<a:CachedResourceDictionary Source="/X.Styles;component/TextBoxStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

References from control to Resource Dictionary
Assuming we implemented this approach, what if we have this code now in our application?

<UserControl A>
<UserControl.Resources>
<ResourceDictionary.MergedDictionaries>
<a:CachedResourceDictionary Source="/Actsoft.Styles;component/TextBoxStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
...
</UserControl.Resources>
...
</UserControl>
We should be fine now don't you think? Apparently not. The problem is that every ResourceDictionary keeps reference to what is called owner. Or even multiple owners. For example, when we add ResourceDictionary to UserControl, this user control becomes the owner of this ResourceDictionary. If we load ResourceDictionary to the application, the application becomes the owner. Not only that, when we add ResourceDictionary to the MergedDictionaries collection, all owners form parent dictionary added to the list of owners for child dictionary. In example above, if we have TextBoxStyle.xaml loaded to the application resources, and then to the UserControl resources, we create references from application to the instance of UserControl. As result, UserControl will remain in heap indefinitely and will not be disposed by garbage collector. And we created this problem by using CachedResourceDictionary, since if we used regular ResourceDictionary, we would have separate copy, which could be disposed as part of UserControl.

Solution:
If you decide to use CachedResourceDictionary, create and remove your user controls dynamically and want them to be disposed, you have to choose how you use resource dictionaries
  • Either use them in application resources only and don't use them in UserControl
  • Or, use them in User Controls only and don't use them in Application Resources. Beware if you activate one user control before you deactivate another one.
To simplify it, CachedResourceDictionary are for application resources or static user controls.

Problems with DropShadowEffect
The description of the problem can be found here. The problem will appear if you have code like that:


<ResourceDictionary>
<DropShadowEffect x:Key="key" ... />
And then, this resource dictionary is loaded into application resources.

Solution:
Most likely, you don't have intentions to modify this effect, so instead, you should make it look like this:

<ResourceDictionary>
<DropShadowEffect x:Key="key"
PresentationOptions:Freeze=True
... />
This will create frozen effect. As result there will be no events assigned, and no references.

Thursday, March 24, 2011

The order in Xaml is important

Today I had one more chance to notice that the order in XAML is important.What I had was a button with code like this:



<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding CommandParameter}"/>

Notice that I assigned Command before CommandParameter. When I was doing it, I din't even think about it. I just added support for CommandParameter at some point so I naturally added this line at the end. However, CanExecute method from ICommand was triggered immediatelly after Command was assigned to the control, even before CommandParameter was assigned. Obviously, it didn't work.

When I changed the order, it's all started to work.

Thursday, February 17, 2011

The power of DataTemplate.DataType property

Somehow I overlooked the existence of DataType property from DataTemplate class. I noticed it only when I started using Microsoft Ribbon and checked how they implemented MVVM pattern I realized how convenient it can be, particularly for lists.

So, how and when to use it.
1. You want to implement MVVM pattern for you lists, which means you have some objects in your view model to support your list.
2. These are objects of different types, which means you would need to create different UI elements to support them. Like for ribbon you can use buttons, or edit boxes, or anything.
3. You use some ItemsControl UI element, which supports ItemsSource property.

Make sure that it's DataTemplate you need to change. It won't work for separators, for example, since you would need to replace Control Template for that.

Friday, January 21, 2011

Default focus in XAML

Very simple task, you open window and you want some text box to have focus. And since it's common and simple task I thought it should be possible using XAML. To do this, you should use FocusManager. However, you should pay attention where you put it. I had to use it right in the Grid where I placed my TextBox. Which is logical, if you think about it. How else TextBox can be found.

Here is the xaml:



<UserControl x:Class="Actsoft.CometTracker.Layouts.AddViewView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="40" d:DesignWidth="300"
>
<Grid
FocusManager.FocusedElement="{Binding ElementName=textBox}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label
Margin="4">View Name:</Label>
<TextBox Grid.Column="1" x:Name="textBox"
Margin="4" Text="{Binding Path=ViewName}"/>
</Grid>
</UserControl>

Friday, November 26, 2010

Performance in WPF

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


My conclusions are:
1. Be very careful with animation. Especially if you run without expiration time, but wish to stop them manually, don't forget to stop them.
2. There are performance tools that can help. Particularly, I paid attention to the "frame rate", which helped me to identify my problem. The values should be close to zero when application is idle. If you are serious, learn these tools.

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>

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}">

Wednesday, March 04, 2009

Fill WrapPanel from the list (WPF)

Sometime, when it is necessary to make a choice from the list, and the list is not that big, it makes sense to use buttons with some actions instead of lists. I found useful technique to do it when the list of actions is dynamic.

The idea is somehow populate WrapPanel with buttons dynamically. We can do it by replacing template for ItemsController.

I lave a collection of elements (it will be assigned to the ItemsSource property in runtime) with Key and Value properties.



<ItemsControl x:Name="activitiesControl" Margin="10">
<ItemsControl.Template>
<ControlTemplate>
<WrapPanel Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
FlowDirection="LeftToRight" IsItemsHost="true">
</WrapPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{DynamicResource ActionButton}" HorizontalAlignment="Right" Margin="5"
Content="{Binding Value}" Width="200"
Command="{Binding Path=ViewModel.ActionTypeCommand,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=local:CustomerEditView}}"
CommandParameter="{Binding Key}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>


Note that I am using Commands. Finally, we have it. Not in Silverlight yet though.

Sunday, March 01, 2009

Binding to nullable values (WPF)

I don't usually use nullable types, since my approach is to use them only when there is real difference between zero and null values, and it's not often when such difference exists. However, sometimes you may not really know how the value is going to be used. And then having it nullable may be preferable.

That was the case in one of our custom projects. Naturally, you would want to display empty edit box for null value, which was defined as nullable integer. The problem is that by default binding would not handle empty string as null value. Fortunately, with .NET 3.5 SP1 there is a way. You should use TargetNullValue attribute. The binding would look like this then:


{Binding Path=Customer.ClientYear, TargetNullValue={x:Static sys:String.Empty}}

This, and other attributes for the binding collected in one document here

Wednesday, December 24, 2008

Sorting in WPF DataGrid

As you could see from my previous post, I am using WPF Toolkit DataGrid now. One of the reason to do it is to sort data. Sure, it is possible to do sorting in ListView, but using DataGrid I can do it with less efforts.

The only problem was that what I had as data source was regular BindingList, that does not support sorting. So, sorting just didn't work.

The solution I found the easiest is using Linq. Since I assigned my data source in code, not XAML, there was no inconvenience at all.

Instead of line


ItemsSource = list


I used


ItemsSource = from item in list select item;

Thursday, December 11, 2008

Changing background color for header in WPF Toolkit DataGrid

Today I decided to look at DataGrid from WPF Toolkit, available at CodePlex. As you may expect, the first thing I needed to do is to change it look. I started from changing background and immediately stuck.

1. Changed Background - didn't work for everything
2. Changed RowBackground - still not everything.
3. And then I added style for DataGridColumnHeader - now I had geader, but still some small rectungles remained gray.
4. And then something strange happened, I added style for DataGridRowHeader with yellow background, but result was completely unexpected. I got what I wanted, but I still don't understand how.

Here is my result.


<dg:DataGrid x:Name="grid" Background="Red" RowBackground="Red" IsReadOnly="True" GridLinesVisibility="None" >
<dg:DataGrid.Resources>
<Style TargetType="{x:Type dgp:DataGridColumnHeader}">
<Setter Property="Background" Value="Red"/>
</Style>
<Style TargetType="{x:Type dgp:DataGridRowHeader}">
<Setter Property="Background" Value="Green"/>
</Style>
</dg:DataGrid.Resources>
<dg:DataGrid.Columns>
<dg:DataGridTextColumn Header="Test column"/>
</dg:DataGrid.Columns>
</dg:DataGrid>

Friday, September 19, 2008

ComboBox - DisplayMemberPath or TextSearch.TextPath

Probably it's just too late already, and I am slow, but I was confused with DisplayMemberPath and TextSearch.TextPath properties.

First, I added some objects to ComobBox.Items. Not using XML, I did it dynamically from C#. It worked find on Vista computer, but then it didn't work under XP. I just got empty values instead of value of ToString() method.

OK, I looked in my favorite book WPF Unleashed, and found that TextSearch.TextPath should be used. Well, it didn't help. It took me a while until I got to try old familiar DisplayMemberPath property and then it worked.

So, the question I have now is why TextSearch.TextPath didn't work? Or if asked in more general way, what are the rules. When I am supposed to use DisplayMemberPath and when TextSearch.TextPath. Anybody?

Wednesday, September 10, 2008

Hyperlinks in WPF Continued

I wrote before about using hyperlinks in WPF. For my new side project I need to switch from hypertext to the simple text depending on availability of URL. Switching to simple text seemed easy, just assigning text value to the Text property worked. Switching back was not obvious for me.

Then I found that TextBlock has Inlines property. The code became this:

tHypertext.Inlines.Clear();
if (!string.IsNullOrEmpty(uri))
{
if (!string.IsNullOrEmpty(value))
tHypertext.Inlines.Add(value);
tHypertext.NavigateUri = new Uri(uri);
tTextBlock.Inlines.Clear();
tTextBlock.Inlines.Add(tWord);
}
else
{
tTextBlock.Text = value;
tHypertext.NavigateUri = null;
}

Where TextBlock and Hypertext are from previouse example

Thursday, September 04, 2008

Keyboard.Modifiers sometimes doesn't work

There are many examples how to implement keyboard hooks in .NET. The one I used as example is http://blogs.vertigo.com/personal/ralph/Blog/archive/2007/02/12/wpf-low-level-keyboard-hook-sample.aspx

There is very important comment there that says Keyboard.Modifier property does not return correct values. First, I wanted to avoid linking to System.Windows.Form assembly and didn't pay much attention go this comments. Well, it didn't work. I had come back to using System.Windows.Forms.Control.ModifierKeys. To make interface more WPF compatible I just converted value this way:

System.Windows.Forms.Keys m = System.Windows.Forms.Control.ModifierKeys;
ModifierKeys m2 = ModifierKeys.None;
if ((m & System.Windows.Forms.Keys.Control) != 0)
m2 = m2 | ModifierKeys.Control;
if ((m & System.Windows.Forms.Keys.Alt) != 0)
m2 = m2 | ModifierKeys.Alt;
if ((m & System.Windows.Forms.Keys.Shift) != 0)
m2 = m2 | ModifierKeys.Shift;
if ((m & System.Windows.Forms.Keys.Apps) != 0)
m2 = m2 | ModifierKeys.Windows;

Saturday, August 09, 2008

How to implement LinkControl in WPF

There is no LinkControl in WPF, so if you need to have hyperlink in your form, different approach is needed. I used this:

XAML:

<TextBlock>Go<Hyperlink NavigateUri="http://counttime.alexeyev.org" Click="Hyperlink_Click">http://counttime.alexeyev.org</Hyperlink></TextBlock>

C#:

private void Hyperlink_Click(object sender, RoutedEventArgs e)

{
Hyperlink link = (Hyperlink)sender;
Process.Start(link.NavigateUri.AbsoluteUri);
}

Tuesday, December 18, 2007

Changing background for selected item in WPF ListBox

When I wanted to change background color for WPF ListBox the first thing I did was creating triggers in the style:

The problem is that it doesn't work. I have to change resources instead. Here is the link that describes it very well.


http://blogs.msdn.com/wpfsdk/archive/2007/08/31/specifying-the-selection-color-content-alignment-and-background-color-for-items-in-a-listbox.aspx

Friday, December 07, 2007

Master Detail Binding in WPF

I spent hours today trying to figure out how to display Master Detail relation in two lists, if I have good old DataSet with Relation between tables. All examples I could find in Google were about objects or XML structures.

What worked is this:

Let's assume we have DataSet with two tables, MasterTable and DetailTable. We also have DataRelation named Master_Detail. The binding in code looks like that:

masterList.IsSynchronizedWithCurrentItem = true;
detailList.IsSynchronizedWithCurrentItem = true;


DataSet dataSet = CreateDataSet1();
DataView dataView = new DataView(dataSet.Tables["MasterTable"]);


Binding binding = new Binding();
binding.Source = dataView;
masterList.SetBinding(ListView.ItemsSourceProperty, binding);


binding = new Binding();
binding.Source = dataView;
binding.Path = new PropertyPath("Master_Detail");
detailList.SetBinding(ListView.ItemsSourceProperty, binding);


Now, let's say we have another relation named "Detail_SmallDetail". We would add following:

smallDetailList.IsSynchronizedWithCurrentItem = true;
binding = new Binding();
binding.Source = dataView;
binding.Path = new PropertyPath("Master_Detail/Detail_SmallDetail");
smallDetailList.SetBinding(ListView.ItemsSourceProperty, binding);

I didn't figure out how to do it in XAML yet.

Tuesday, August 14, 2007

Understanding 3d in WPF

When I read about 3d support in WPF in MSDN documentation, I had difficulties with understanding. In fact, whole area of WPF in MSDN disappointed me. Anyway, this link helped me to understand it better. http://blogs.msdn.com/danlehen/archive/2005/11/06/489627.aspx