Click here to Skip to main content
15,886,110 members
Articles / Operating Systems / Windows
Article

WPF ("Avalon") Demo 2 - A Personal Clone of well-known MediaMania - Described

Rate me:
Please Sign up or sign in to vote.
3.22/5 (7 votes)
25 Jun 20064 min read 53.1K   283   10   12
This demo application, combines both XAML declaration and C# Code to search for items on Amazon and populate the result into a WPF ListBox

Sample Image - mediamaniac.jpg

Introduction

If you've seen Mark Boulter and Mark Harsh's talk about Cider the designer for WinFX applications in visual studio and its corresponding demo video, you've found a very simple but useful application called MediaMania Online Catalogue.

I've searched for the demo project to investigate through the facilities the designer has, but no luck. Hopefully I had some motivation to build up the whole thing by myself according to the demonstration video combined with my own experience.

This project has been explained briefly here. Also there's a sample just like this one on gotdotnot. Many thanks to that, we both have similar thoughts about the demo but used different search policies. Mine is simpler I think.

Requirements

To run the application you need: WinFX Runtime Components February 2006 CTP.

To compile the source code containing your modification, you need additionally Visual Studio Orcas which brings you the "Cider" designer, Expression Interactive Desinger March 2006 CTP for style editing and gradient brushes and also A Valid Access Key from Amazon to Launch the WebService Requests.

Additional Things...

If you digg into the demonstration video and compare the resulting application with mine, you'll see that I've put a "Cancel" button on screen to Cancel the search.

I've used the new .NET 2.0 background wroker component to implement worker thread model and make the UI responsive.

Other things are exactly try to clone job over the main idea of the demo video.

The XAML - Described

According the two types of WinFX Applications (Windows and Browser Application) WinFX Projects basically contain WinFX Windows or WinFX Pages.

My last demo was a WinFX Browser Application which consisted a single Page (Scene1.xaml) despite this one which is a WinFX Winodws Application.

WinFX Windows Applications run on target machines which trust and full control over the local and internet resources. As you decided to install them you gave them the permission.

However WinFX Browser Applications are not fully trusted, cause they are distributed and accessed over the network. Just like Web Applications, WinFX Browser Applications cannot do anything Desktop Applications. And the differrence with Web Applications is that the UI is WPF ("Avalon") Host Managed one, means you can use greate Avalon Components on your application despite poor html applications which even AJAX won't help you do such thing.

By the way, although the two type are different in deployment and execution, but they are developed all the same way in .xaml files.

The Main Window

As all .xaml files are using XML markup, they are referrencing appropriate schemas:

XML
<Window x:Class="MediaMania.WindowMain"
    xmlns="<A href="http://schemas.microsoft.com/winfx/2006/xaml/presentation">http://schemas.microsoft.com/winfx/2006/xaml/presentation</A>"
    xmlns:x="<A href="http://schemas.microsoft.com/winfx/2006/xaml">http://schemas.microsoft.com/winfx/2006/xaml</A>"
    Title="MediaMania" Height="456" Width="596"
    >

...
</Window>

Window Resources

Any control styles and templates and data bindings which will be used by the window and its controls are defined in <Window.Resources> section.

Styles are a set of propperty setter markups which decorate a control or manages its behavior. And by using control templates you can build up a pre-defined control from scratch based on its core functionality. I've done this in a detailed change for button control in the last demo.

Here is the style and control template for <ListBoxItem>:

XML
<BR>  <Window.Resources>
    <Style x:Key="{x:Type ListBoxItem}" TargetType="{x:Type ListBoxItem}">
      <Setter Property="OverridesDefaultStyle" Value="true" />
      <Setter Property="Background" Value="Transparent" />
      <Setter Property="BorderBrush" Value="Transparent" />
      <Setter Property="BorderThickness" Value="1" />
      <Setter Property="HorizontalContentAlignment" Value="Left" />
      <Setter Property="VerticalContentAlignment" Value="Center" />
      <Setter Property="Padding" Value="3" />
      <Setter Property="Template">
        <Setter.Value>
          <ControlTemplate TargetType="{x:Type ListBoxItem}">
            <Border 
              Background="{TemplateBinding Background}" 
              BorderBrush="{TemplateBinding BorderBrush}" 
              BorderThickness="{TemplateBinding BorderThickness}" 
              Padding="{TemplateBinding Padding}">
              <ContentPresenter x:Name="ContentSite" 
                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
            </Border>
            <ControlTemplate.Triggers>
              <Trigger Property="IsSelected" Value="true">
                <Setter Property="Background">
                  <Setter.Value>
                    <LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
                      <LinearGradientBrush.GradientStops>
                        <GradientStop Offset="0" Color="#F9F9F9"/>
                        <GradientStop Offset="1" Color="#E9E9E9"/>
                      </LinearGradientBrush.GradientStops>
                    </LinearGradientBrush>
                  </Setter.Value>
                </Setter>
                <Setter Property="BorderBrush" Value="Silver" />
              </Trigger>
              <MultiTrigger>
                <MultiTrigger.Conditions>
                  <Condition Property="IsSelected" Value="true" />
                  <Condition Property="Selector.IsSelectionActive" 
                             Value="false" />
                </MultiTrigger.Conditions>
                <Setter Property="Background" Value="#F8F8F8" />
                <Setter Property="BorderBrush" Value="#E0E0E0" />
              </MultiTrigger>
            </ControlTemplate.Triggers>
          </ControlTemplate>
        </Setter.Value>
      </Setter>
      <Style.Triggers>
        <Trigger Property="IsEnabled" Value="false">
          <Setter Property="Foreground" Value="Gray" />
        </Trigger>
      </Style.Triggers>
    </Style>
  </Window.Resources>

The style element is defined for a TargetType of ListBoxItem and is has also a x:Key which defines the style to be referrenced by its key for a particular compatible element.

The control template has defines some <Trigger> and <MultiTrigger> sections. The role of these two element is to define actions or style changes while a specific property-value match occures. The latter support multiple conditions, which is an AND between the conditions.

As shown above, while a ListBoxItem is selected it has some decoration on background, and also when its not the active control of the window but its selected, it has some other decoration.

Main Window's Visual Tree

The term Visual Tree is reffered to rendering system of WPF. Each element in Avalon has its own visual tree. This sample's window has tree <DockPanel>s, each of them has their own visual tree as well, like <RadioButton>s, <Label>s and <TextBox>s.

Here is the XMAL code:

XML
<BR>  <DockPanel MinHeight="50" MinWidth="50" LastChildFill="True" Name="dockPanelMain" >
    <DockPanel MinHeight="50" MinWidth="50" LastChildFill="False" Name="dockPanelTop" <BR>	DockPanel.Dock="Top" Height="65">
      <DockPanel.Background>
        <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
          <LinearGradientBrush.RelativeTransform>
            <TransformGroup>
              <TranslateTransform X="-0.5" Y="-0.5"/>
              <ScaleTransform ScaleX="1" ScaleY="1"/>
              <SkewTransform AngleX="0" AngleY="0"/>
              <RotateTransform Angle="90.0172795589426"/>
              <TranslateTransform X="0.5" Y="0.5"/>
              <TranslateTransform X="-0.0015136743804197244" Y="-0.19736842105263153"/>
            </TransformGroup>
          </LinearGradientBrush.RelativeTransform>
          <LinearGradientBrush.GradientStops>
            <GradientStop Color="sc#0.595107, 0, 0.2904481, 1" Offset="0"/>
            <GradientStop Color="sc#1, 0, 0.09987141, 0.798212945" Offset="1"/>
          </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
      </DockPanel.Background>
      <Label Name="labelMyMedia" DockPanel.Dock="Right" Content="My Media" 
		VerticalContentAlignment="Center" Foreground="#FFFFFFFF" <BR>		Margin="0,0,10,0" FontSize="14" VerticalAlignment="Center" />
      <RadioButton Name="radioButtonMusics" Margin="10,0,0,0" Height="Auto" Content="Musics" 
		Checked="ChangeSearchMode" Foreground="#FFFFFFFF" FontSize="14" <BR>		HorizontalAlignment="Stretch" VerticalAlignment="Center" />
      <RadioButton Name="radioButtonBooks"  Margin="10,0,0,0" Height="Auto" Content="Books"  
		Checked="ChangeSearchMode" Foreground="#FFFFFFFF" FontSize="14" <BR>		HorizontalAlignment="Stretch" VerticalAlignment="Center" />
      <RadioButton Name="radioButtonGames"  Margin="10,0,0,0" Height="Auto" Content="Games"  
		Checked="ChangeSearchMode" Foreground="#FFFFFFFF" FontSize="14" <BR>		HorizontalAlignment="Stretch" VerticalAlignment="Center" />
      <RadioButton Name="radioButtonMovies" Margin="10,0,0,0" Height="Auto" Content="Movies" 
		Checked="ChangeSearchMode" Foreground="#FFFFFFFF" FontSize="14" <BR>		VerticalAlignment="Center" />
    </DockPanel>
    <DockPanel MinHeight="50" MinWidth="50" LastChildFill="False" Name="dockPanelBottom" <BR>		DockPanel.Dock="Bottom" Height="68">
      <DockPanel.Background>
        <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
          <LinearGradientBrush.RelativeTransform>
            <TransformGroup>
              <TranslateTransform X="-0.5" Y="-0.5"/>
              <ScaleTransform ScaleX="1" ScaleY="1"/>
              <SkewTransform AngleX="0" AngleY="0"/>
              <RotateTransform Angle="-89.956494128077338"/>
              <TranslateTransform X="0.5" Y="0.5"/>
              <TranslateTransform X="-0.025830258302582756" Y="-0.091702786377709039"/>
            </TransformGroup>
          </LinearGradientBrush.RelativeTransform>
          <LinearGradientBrush.GradientStops>
            <GradientStop Color="sc#0.7111675, 0, 0.116357282, 0.841735661" Offset="0"/>
            <GradientStop Color="sc#0.08734215, 0, 0.2759405, 1" Offset="1"/>
          </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
      </DockPanel.Background>
      <TextBox Name="textBoxSearchPhrase" Width="258" Height="22.75" 
		Margin="10,0,0,0" BorderBrush="{DynamicResource BorderBrush}" 
		BorderThickness="1,1,1,1" VerticalAlignment="Center" 
		TextAlignment="Left" Text="Search Phrase Here" Padding="0,1,4,1" FontSize="12" />
      <Label x:Name="labelStatus" DockPanel.Dock="Right" Content="Ready" VerticalContentAlignment="Center" 
		Margin="0,0,10,0" FontWeight="Bold" VerticalAlignment="Center">
        <Label.RenderTransform>
          <TransformGroup>
            <TranslateTransform X="0" Y="0"/>
            <ScaleTransform ScaleX="1" ScaleY="1"/>
            <SkewTransform AngleX="0" AngleY="0"/>
            <RotateTransform Angle="0.59862535884994372"/>
            <TranslateTransform X="0" Y="0"/>
            <TranslateTransform X="0" Y="0"/>
          </TransformGroup>
        </Label.RenderTransform>
      </Label>
      <Button x:Name="buttonSearch" Height="22.75" Content="Search Amazon" DockPanel.Dock="Left" 
		Click="buttonSearch_Clicked" Margin="10,0,0,0" Width="130" FontSize="12"/>
      <Button x:Name="buttonCancel" Height="22.75" Content="Cancel" DockPanel.Dock="Left" 
		Click="buttonCancel_Clicked" Margin="2,0,0,0" Width="70" IsEnabled="False">
      </Button>
    </DockPanel>
    <ListBox Name="listBoxMedia" Margin="0,0,0,0">
      <ListBox.Background>
        <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
          <LinearGradientBrush.RelativeTransform>
            <TransformGroup>
              <TranslateTransform X="-0.5" Y="-0.5"/>
              <ScaleTransform ScaleX="1" ScaleY="1"/>
              <SkewTransform AngleX="0" AngleY="0"/>
              <RotateTransform Angle="-221.27298981718721"/>
              <TranslateTransform X="0.5" Y="0.5"/>
              <TranslateTransform X="0.048890101502995775" Y="-0.061982591730506818"/>
            </TransformGroup>
          </LinearGradientBrush.RelativeTransform>
          <LinearGradientBrush.GradientStops>
            <GradientStop Color="sc#0.08734215, 0, 0.2413202, 1" Offset="0"/>
            <GradientStop Color="sc#0.435523748, 0, 0.09987141, 0.5499357" Offset="1"/>
          </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
      </ListBox.Background>
      <ListBox.BorderBrush>
        <LinearGradientBrush StartPoint="0,0.5" EndPoint="1,0.5">
          <LinearGradientBrush.RelativeTransform>
            <TransformGroup>
              <TranslateTransform X="-0.5" Y="-0.5"/>
              <ScaleTransform ScaleX="1" ScaleY="1"/>
              <SkewTransform AngleX="0" AngleY="0"/>
              <RotateTransform Angle="-221.27298981718721"/>
              <TranslateTransform X="0.5" Y="0.5"/>
              <TranslateTransform X="0.048890101502995775" Y="-0.061982591730506818"/>
            </TransformGroup>
          </LinearGradientBrush.RelativeTransform>
          <LinearGradientBrush.GradientStops>
            <GradientStop Offset="0" Color="sc#0.08734215, 0, 0.2413202, 1"/>
            <GradientStop Offset="1" Color="sc#0.435523748, 0, 0.09987141, 0.5499357"/>
          </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>
      </ListBox.BorderBrush>
    </ListBox>
  </DockPanel>

This creates my MainWindow as you can see in the screenshot.

The Code

Now we need to write some code to handle the operation. We want to handle button clicks for Search and Cancel buttons and change the search mode when a specific radio button is selected.

If you open the demo project In the Visual Studio Orcas or Expression Interactive Designer, you see that the MainWindow.xaml has an associated MainWindow.xaml.cs C# code file.

Here is where the window class we declared in the xaml appears:

XML
<BR><Window x:Class="MediaMania.WindowMain".../>

Using Namespaces

C#
<BR>using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging; // used for the Image control on ListBoxItem
using System.Windows.Shapes;
using Amazon = MediaMania.Amazon;   // Amazon's web service referrence
                                    //(the proxy is auto-generated)

WindowMain Class

C#
<BR>namespace MediaMania
{
    /// <summary>
    /// Interaction logic for WindowMain.xaml
    /// </summary>
    
	public partial class WindowMain : Window
	{
	...
    	}
}

Just like windows forms we write our codes inside the partial class definition. except we use Window as parent class.

RadioButton Handler

In the XAML file we've set <RadioButton ... Checked="ChangeSearchMode" ... />, which leads the WPF host to execute the method we should declare in our class.

C#
<BR>        private string searchMode = "books";

	...

        
	void ChangeSearchMode(object sender, EventArgs e)        
	{            
		RadioButton checkedRadioButton = (RadioButton)sender;            
		searchMode = checkedRadioButton.Content.ToString().ToLower();        
	}

Seach and Cancel Buttons

When we click the Search button, an instance of Amazon Search Service initializes and we make an Async call to the webservice then catch the results when it's completed in the corresponding handler.

C#
<BR>        protected const string AMAZON_ID = "Put Your Amazon Acess Key here";

        ...
	void buttonSearch_Clicked(object sender, EventArgs e)        
	{            
		this.labelStatus.Content = "Initializing search...";
            
		Amazon.KeywordRequest keywordRequest = new Amazon.KeywordRequest();       
		keywordRequest.locale = "us";            
		keywordRequest.sort = "reviewrank";
            
		keywordRequest.mode = searchMode;            
		keywordRequest.keyword = this.textBoxSearchPhrase.Text;            
		keywordRequest.tag = AMAZON_ID;            
		keywordRequest.devtag = AMAZON_ID;            
		keywordRequest.type = "heavy";            
		keywordRequest.page = "1";<P>            
		try           
		{                
			if(Ass == null)                    
				Ass = new Amazon.AmazonSearchService();
                
			Ass.KeywordSearchRequestCompleted += 
			  new MediaMania.Amazon.KeywordSearchRequestCompletedEventHandler(<BR>				Ass_KeywordSearchRequestCompleted);
                
			this.labelStatus.Content = "Searching...";
                
			Ass.KeywordSearchRequestAsync(keywordRequest);
                
			buttonCancel.IsEnabled = true;            
		}
            
		catch       
		{            
		}
	}</P>
<P>	void buttonCancel_Clicked(object sender, EventArgs e)        
	{            
		buttonCancel.IsEnabled = false;            
		labelStatus.Content = "Aborting...";            
		Ass.Abort();            
		labelStatus.Content = "Ready";        
	}</P>
<P>	void Ass_KeywordSearchRequestCompleted(object sender, 
		MediaMania.Amazon.KeywordSearchRequestCompletedEventArgs e)        
	{            
		try            
		{
                
			buttonCancel.IsEnabled = false;                
			this.listBoxMedia.Items.Clear();
                
			Amazon.ProductInfo Pi = e.Result;</P>
<P>                
			this.labelStatus.Content = "Populating results...";
                
			foreach (Amazon.Details itemDetail in 	Pi.Details)                
			{
                    
				DockPanel itemPanel = new DockPanel();                    
				itemPanel.Margin = new Thickness(5);                    
				BitmapImage bitmapImage;                    
				if (itemDetail.ImageUrlMedium == "")                        
					bitmapImage = new BitmapImage(
						new Uri("<A href="http://g-images.amazon.com/images/G/01/x-site/icons/no-img-sm.gif">http://g-images.amazon.com/images/G/01/x-site/icons/no-img-sm.gif</A>"));                   
				else                        
					bitmapImage = new BitmapImage(new Uri(itemDetail.ImageUrlSmall));                    
				Image itemImage = new Image();                    
				itemImage.Width = 100;                    
				itemImage.Height = 100;                    
				itemImage.Margin = new Thickness(0, 0, 5, 0);                    
				Image.Source = bitmapImage;                    
				Panel.Children.Add(itemImage);                    
				BoxItem lbItem = new ListBoxItem();                    
				lbIte,m.Content = itemDetail.ProductName;</P><P>				lbItem.Background = new SolidColorBrush(Colors.Transparent);                   
				lbItem.FontSize = 14;                    
				itemPanel.Children.Add(lbItem);                    
				this.listBoxMedia.Items.Add(itemPanel);                
			}
                
			this.labelStatus.Content = Pi.Details.Length.ToString() + " of " + Pi.TotalResults;
		}
            
		catch            
		{                
			this.labelStatus.Content = "Not Found";            
		}
	}
</P>

 

Feedback

Please send your feedbacks. I'm really serious.

Acknowledgement

Many Thanks to Marc! He motivated me to complete the article, with the very correct words by his message. ;)

History

     4 May 2006 - Original Article

     5 May 2006 - Now it's an Article with detail description

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Iran (Islamic Republic of) Iran (Islamic Republic of)
I'm lazy to retype it, check my blog plz.

An Intelligent Sense Of A Computer
http://aisac.wordpress.com

Comments and Discussions

 
Questioncan we list controls present on WPF form [modified] Pin
sam_g129-May-06 18:43
sam_g129-May-06 18:43 
Hi This is a nice example.
But I have a query regarding WPF application,
I want to list all the controls present on the WPF window…
I have done that in Windows application (in C#) but I m not getting any related properties in WPF…Pls help me out…
Thanks….


-- modified at 0:44 Tuesday 30th May, 2006
AnswerRe: can we list controls present on WPF form [modified] Pin
A.Gharighi30-May-06 18:25
A.Gharighi30-May-06 18:25 
QuestionRe: can we list controls present on WPF form [modified] Pin
sam_g130-May-06 23:05
sam_g130-May-06 23:05 
AnswerRe: can we list controls present on WPF form Pin
A.Gharighi10-Jun-06 0:03
A.Gharighi10-Jun-06 0:03 
Generalinteresting example, thanks Pin
BillWoodruff4-May-06 23:31
professionalBillWoodruff4-May-06 23:31 
GeneralRe: interesting example, thanks Pin
Marc Clifton5-May-06 0:51
mvaMarc Clifton5-May-06 0:51 
GeneralRe: interesting example, thanks Pin
BillWoodruff5-May-06 3:07
professionalBillWoodruff5-May-06 3:07 
GeneralIt would be really nice... Pin
Marc Clifton4-May-06 12:16
mvaMarc Clifton4-May-06 12:16 
GeneralRe: It would be really nice... Pin
Vertyg04-May-06 22:22
Vertyg04-May-06 22:22 
GeneralRe: It would be really nice... Pin
Marc Clifton5-May-06 0:47
mvaMarc Clifton5-May-06 0:47 
GeneralRe: It would be really nice... Pin
Vertyg05-May-06 11:07
Vertyg05-May-06 11:07 
GeneralRe: It would be really nice... Pin
A.Gharighi6-May-06 1:41
A.Gharighi6-May-06 1:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.