Wednesday, August 25, 2010

Lazy(Of T) class in .Net Framework4.0

.Net Framework provides us with a new class called Lazy<T> which is used to initialize the object in lazy pace. It includes the ability to initialize the value types and to use null values. Using this class, we could implement complex navigation related applications very effectively with less memory use. For instance, consider the situation like I have a property which holds array of user controls(Approx 50 User Controls) in Silverlight application. In this instance, need to display five user controls in main page, but need to initiate all these fifty user controls to get the actual references while application load since I need to bind some elements between user controls. At the same time, rest of the user controls will be displayed when user navigate explicitly.

If we implement such a application using typical .Net property declaration/initialization without using the Lazy class, all the user control will be initialized when we use new keyword while adding items in my array property. Below is the typical .Net initialization.

// Property declaration
private UserControl[] loadMyUserControl;
public UserControl[] LoadMyUserControl
{
get
{
return loadMyUserControl;
}
set
{
loadMyUserControl = value;
}
}

// Initializing property.
public MainPage()
{

InitializeComponent();
var userControls = new UserControl[]
{
new UserControl1(),
new UserControl2(),
new UserControl3()
};
}

In this approach, my application consumes unnecessary memory(Nearly 45 user control memory) when user open my application and closes without navigating into other pages.

But Lazy initialization occurs the first time the Lazy<T>.Value property is accessed or the Lazy<T>.ToString method is called. For my scenario, five user controls instance and memory only created while loading my application. Rest of the user controls will be created when user navigate to the corresponding page through navigation link from my main page. Below is the code snippet for lazy load.

// Property declaration
private Lazy<UserControl>[] loadMyUserControl;
public Lazy<UserControl>[] LoadMyUserControl
{
get
{
return loadMyUserControl;
}
set
{
loadMyUserControl = value;
}
}

// Initializing property.
public MainPage()
{
InitializeComponent();
LoadMyUserControl = new Lazy<UserControl>[]
{
new Lazy<UserControl>(() => {return new UserControl1();
}),
new Lazy<UserControl>(() => {return new UserControl2();
}),
new Lazy<UserControl>(() => {return new UserControl3();
})
};
}

Now I am going to load my user controls based on navigation at runtime as below mentioned.

// Loading my fifth user control on button click
private void Button_Click_1(object sender, RoutedEventArgs e)
{
if (!TopPanel.Children.Contains(LoadMyUserControl[5].Value))
TopPanel.Children.Add(LoadMyUserControl[5].Value);
}

Note : TopPanel is the stack panel which is in main page of the application. See the demo sample for more idea.

Run the demo app and click “Status” button. Check the status of user controls. Still all the user controls are not loaded as shown in below mentioned snap.

InitialLoad

Click “Load UserControl1” button and check the status. Now you could see, first user control loaded in view and status has been changed as True. What a great feature in framework4.0. :)

FirstLoaded

You can refer this in msdn documentation for more idea about Lazy class.

Here is the simple real time Silverlight sample which illustrates the same.

Enjoy :)

Thanks,

Venugopal.

Wednesday, August 4, 2010

Silverlight Image download indicator with MVVM Pattern

Introduction

Microsoft Silverlight provides a BitmapImage class, which supports a set of features for Image expression including some of the useful events such as DownloadProgress, ImageFailed and ImageOpened events. These events can be effectively used to have a UI with nice look and feel by displaying download progress in case the images are downloaded from the web dynamically.

Probably, some folks might already have solution for this scenario. But I hope many developers don’t think in the same way. So, have got something to share on this based on my experience. My approach involves simple SpinAnimation custom control implementation. Also there are chances that my solution may not be offering something of the expertise level but at least it can help to discover the approach :).

This post explores a number of concepts; first, it involves creating tiny custom SpinAnimation control with animation, next, implementing a View Model (To follow MVVM pattern) with necessary properties for indicating progress details to the View and finally, creating View to design the UI with Model property binding.

SpinAnimationControl - Custom Silverlight control

As there are more articles on how to create custom control in Silverlight, I am directly jumping on to the main code that is related to changing the visibility of the control based on download progress value. I know, you are asking now, why SpinAnimation control? J Can’t we change the visibility of the SpinAnimation Control based on model property value? Yes, we can change, but I want to do some operation when the visibility of animation element gets changed. Like stop and start the animation when element visibility value gets changed. Both WPF and SL do not provide the Visibility property changed callback by default. But you could achieve using property metadata override logic in WPF. Unfortunately Silverlight doesn’t have feature to override the property metadata like WPF does. So we can go ahead with generic solution which works both in SL and WPF. Let's start with SpinAnimation control constructor, which is pretty basic:

Setting default style key for custom control.

public class SpinAnimationControl : Control

{

public SpinAnimationControl ()

{

this.DefaultStyleKey = typeof(SpinAnimationControl);

}

}

Creating dependency property called Progress which receives the download progress value from application side. Note that I have mentioned property value changed callback OnProgressChanged in PropertyMetadata. In this call back, if new value is less then 100, I am assuming that image is still not downloaded. If value is equal to 100, image gets downloaded witout any issue. So, I can change the visibility of my SpinAnimation control based on the new value. And also I can stop and start the animation after I change the visibility. Please refer the property value changed callback method for more details below.

public int Progress

{

get { return (int)GetValue(ProgressProperty); }

set { SetValue(ProgressProperty, value); }

}

public static readonly DependencyProperty ProgressProperty = DependencyProperty.Register("Progress", typeof(int), typeof(SpinAnimationControl), new PropertyMetadata(0, OnProgressChanged));

Override OnApplyTemplate method for getting template child which has animation storyboard in resource section of root element. You can get template child using GetTemplateChild method. Grid is the root element in control template.

Grid spinGrid = null;

public override void OnApplyTemplate()

{

base.OnApplyTemplate();

spinGrid = GetTemplateChild("SpinGrid") as Grid;

}

Property value changed callback.

private bool isAnimationStarted = false;

private static void OnProgressChanged(object sender, DependencyPropertyChangedEventArgs args)

{

if (sender is SpinAnimationControl)

{

(sender as SpinAnimationControl).OnProgressChanged(args);

}

}

public void OnProgressChanged(DependencyPropertyChangedEventArgs args)

{

int newValue = (int)args.NewValue;

Storyboard storyBoard = null;

if (spinGrid != null)

{

// Storyboard from grid resource.

storyBoard = spinGrid.Resources["RetrievalCircleAnimation"] as Storyboard;

}

if (newValue < 100)

{

this.Visibility = Visibility.Visible;

if (!isAnimationStarted)

{

if (storyBoard != null)

{

// Start the animation

storyBoard.Begin();

isAnimationStarted = true;

}

}

}

else

{

this.Visibility = Visibility.Collapsed;

if (isAnimationStarted)

{

if (storyBoard != null)

{

// Stop the animation.

storyBoard.Stop();

isAnimationStarted = false;

}

}

}

}

SpinAnimation control template – generic.xaml

Here is the template of SpinAnimation control. I put some ellipses with storyboard for animating download progress indicator. Please refer the generic.xaml for complete template code.

<ControlTemplate TargetType="local:SpinAnimationControl">

<Grid x:Name="SpinGrid" Height="{TemplateBinding Height}" VerticalAlignment="Center" HorizontalAlignment="Center" Width="{TemplateBinding Width}">

<Grid.Resources>
<Storyboard x:Name="RetrievalCircleAnimation" RepeatBehavior="Forever" SpeedRatio="4">
<!-- Animation for ellipse -->

<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="ellipse" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
<
EasingDoubleKeyFrame KeyTime="00:00:00" Value="1"/>
<
EasingDoubleKeyFrame KeyTime="00:00:01.5000000" Value="0.5"/>
<
EasingDoubleKeyFrame KeyTime="00:00:03.5000000" Value="0.5"/>
<
EasingDoubleKeyFrame KeyTime="00:00:04" Value="1"/>
</
DoubleAnimationUsingKeyFrames>
</
Storyboard>
<!- More codes -->
</
Grid.Resources>

<Ellipse x:Name="ellipse" Height="12" Width="12" HorizontalAlignment="Center" VerticalAlignment="Top" RenderTransformOrigin="0.5,0.5" Fill="{StaticResource ballbrush}">

<Ellipse.RenderTransform>

<TransformGroup>

<ScaleTransform ScaleX="0.5" ScaleY="0.5"/>

<SkewTransform/>

<RotateTransform/>

<TranslateTransform/>

</TransformGroup>

</Ellipse.RenderTransform>

</Ellipse>

<!- More codes -->

</Grid>

</ControlTemplate>

SpinAnimation Control Snap.


Creating ViewModel and View to present the Image with download indicator.

If you are a SL or WPF developer, I believe that you prefer MVVM approach only, nevertheless I always prefer MVVM since it is the only powerful pattern for SL or WPF apps till now. So that is the reason I decided that implementing image Uri and Progress notification from View Model.

ImageAppViewModel class

ImageAppViewModel class has property called ImageList with the type of ObservableCollection. This is the property that we are going to bind as an ItemsSource of Listbox control in View.

public ImageAppViewModel()

{

ImagesList = new ObservableCollection<ImageExt>();

ImagesList.Add(new ImageExt() { Uri = "http://www.pdssb.com.my/images/stories/gallery/car.jpg" });

ImagesList.Add(new ImageExt() { Uri = "http://joshua.maruskadesign.com/blog/uploaded_images/Car-02-723748.png" });

ImagesList.Add(new ImageExt() { Uri = "http://www.carbodydesign.com/archive/2009/02/13-automotive-designers-show-program/Autodesk-Concept-Car-rendering-lg.jpg" });

ImagesList.Add(new ImageExt() { Uri = "http://www2.hiren.info/desktopwallpapers/other/car-silver-87c.jpg" });

ImagesList.Add(new ImageExt() { Uri = "http://brianhansford.com/wp-content/uploads/2010/02/red-sports-car.jpg" });

}

private ObservableCollection<ImageExt> m_ImagesList;

public ObservableCollection<ImageExt> ImagesList

{

get

{

return m_ImagesList;

}

set

{

m_ImagesList = value;

OnPropertyChanged("ImagesList");

}

}

ImageExt class

ImageExt class has property called Uri, ImageSource(Type of BitmapImage) and Progress property. Why two property for image source in view model?. Typically we will send Image uri as a string type. See the below

Uri="http://www.pdssb.com.my/images/stories/gallery/car.jpg"

But we need to construct the BitmapImage object using this uri and register the DownloadProgress event in property getter. In Download progress event handler, update the Progress property of ImageExt class which we are using for indicating how much percentage of bytes has been downloaded for particular image. Now bind only ImageSource property in ItemTemplate of ListBox in View.

public class ImageExt : INotifyPropertyChanged

{

private int m_Progress;

public int Progress

{

get

{

return m_Progress;

}

set

{

m_Progress = value;

OnPropertyChanged("Progress");

}

}

private string m_Uri;

public string Uri

{

get

{

return m_Uri;

}

set

{

m_Uri = value;

OnPropertyChanged("Uri");

}

}

BitmapImage image = null;

public BitmapImage ImageSource

{

get

{

image = new BitmapImage(new Uri(Uri, UriKind.Absolute));

image.DownloadProgress += new EventHandler<DownloadProgressEventArgs>(image_DownloadProgress);

return image;

}

}

}

DownloadProgress Event Handler

// Updating Progress property value based on DownloadProgressEventArgs value and un registering the event once its // gets downloaded.

void image_DownloadProgress(object sender, DownloadProgressEventArgs e)

{

Progress = e.Progress;

if (e.Progress == 100)

{

image.DownloadProgress -= new EventHandler<DownloadProgressEventArgs>(image_DownloadProgress);

}

}

ImageAppView and ImageAppView.xaml

Now, create the ImageAppView.xaml and put the simple list box control with ViewModel property binding. See the below code snippet, ListBox ItemsSource property bound with ImageList property which we declared in ViewModel.

<ListBox ItemsSource="{Binding ImageList}" Height="110" BorderBrush="Blue" BorderThickness="2">

</ListBox>

Set the ItemsPanelTemplate with StackPanel to display images in horizontal view.

<ListBox.ItemsPanel>

<ItemsPanelTemplate>

<StackPanel Orientation="Horizontal"></StackPanel>

</ItemsPanelTemplate>

</ListBox.ItemsPanel>

Set the ItemTemplate to host our SpinAnimation and Image control to display the actual image once gets downloaded. And also I put some textboxes to show the download percentage details as well.

<ListBox.ItemTemplate>

<DataTemplate>

<Grid>

<Image Name="icon" Width="100" Height="100" Source ="{Binding ImageSource}" Stretch="Uniform" VerticalAlignment="Center" HorizontalAlignment="Center" />

<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center" Visibility="{Binding Path=Visibility, ElementName=spinAnimationControl}">

<TextBlock FontFamily="Cambria" FontSize="10" Text="{Binding Progress}" FontWeight="Bold" Foreground="Black" TextAlignment="Center" TextWrapping="Wrap" LineStackingStrategy="BlockLineHeight" LineHeight="9" VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="1"/>

<TextBlock Text="%" FontFamily="Cambria" FontSize="10" FontWeight="Bold" Foreground="Black" TextAlignment="Center" TextWrapping="Wrap" LineStackingStrategy="BlockLineHeight" LineHeight="9" VerticalAlignment="Center" HorizontalAlignment="Stretch" Margin="1"/>

</StackPanel>

<control:SpinAnimationControl Name="spinAnimationControl" Width="50" Height="50" VerticalAlignment="Center" HorizontalAlignment="Stretch" Progress="{Binding Progress}" Margin="1"/>

</Grid>

</DataTemplate>

</ListBox.ItemTemplate>

Finally assign the ViewModel in DataContext of View .

public partial class MainPage : UserControl

{

public MainPage()

{

InitializeComponent();

this.DataContext = new ImageAppViewModel();

}

}

Run the demo application and see the output. Here is some snaps.

Download initiated



Download completed


Download complete source code from here.

Enjoy!!!!.