Header Ads

Windows Phone: Accessing Listbox Template Elements

Feels like ages since I last publish any coding base blog, but, finally, today's post is going to be coding base.
Today, I shall demonstrate how to access Windows Phone platform base list box template elements. Following are some prerequisites before you proceed any further in this tutorial. 

Prerequisites: 

1) Knowledge about Windows Phone platform

Accessing a template element of list box is not difficult when you simply map your element to its designated model, but, let say if you want to access the selected element as a visual element like TextBlock, CheckBox etc then accessing a list box element becomes trickier because model mapping only maps text to your designated model, it does not map any of the element property e.g. element styling, events, visibility etc. So, in order to do that let proceed with today's tutorial.

You can download the complete source code or you can follow step by step discussion below. The sample code is in Microsoft Visual Studio 2013 and Windows Phone SDK 8 has been targeted.

Download Now!

If you choose to follow step by step discussion then do not get worry about the application styling as I am not posting the code for application styling.

Now lets begin our coding.

1) Create a Windows Phone (silverlight) application with target of SDK 8 in visual studio 2013 and name it "AccessListboxTempElements".
2) In your "MainPage.xaml" file, replace the existing code with the following:


 <phone:PhoneApplicationPage  
   x:Class="AccessListboxTempElements.MainPage"  
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
   xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"  
   xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"  
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
   mc:Ignorable="d"  
   FontFamily="{StaticResource PhoneFontFamilyNormal}"  
   FontSize="{StaticResource PhoneFontSizeNormal}"  
   Foreground="{StaticResource PhoneForegroundBrush}"  
   SupportedOrientations="Portrait" Orientation="Portrait"  
   shell:SystemTray.IsVisible="True" Background="#FF855500">  
   <!--LayoutRoot is the root grid where all page content is placed-->  
   <Grid x:Name="LayoutRoot" Background="#FF001E85">  
     <Grid.RowDefinitions>  
       <RowDefinition Height="Auto"/>  
       <RowDefinition Height="*"/>  
     </Grid.RowDefinitions>  
     <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">  
       <TextBlock Text="Access ListBox Template Elements" Margin="12,0"/>  
       <TextBlock Text="Music" Margin="9,-7,0,0" />  
     </StackPanel>  
     <!--ContentPanel - place additional content here-->  
     <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">  
       <StackPanel Orientation="Vertical" Margin="3">  
         <Grid Height="109">  
           <Grid.ColumnDefinitions>  
             <ColumnDefinition Width="139*" />  
             <ColumnDefinition Width="311*" />  
           </Grid.ColumnDefinitions>  
           <TextBlock Grid.Column="0" Text="Selected" FontSize="26.667" VerticalAlignment="Top" Height="35" Margin="0,12,0,0" Foreground="Yellow" FontWeight="Bold"/>  
           <TextBlock Grid.Column="1" x:Name="txtSel" Margin="0,0,10,0" VerticalAlignment="Top" Height="109" FontSize="26.667" TextWrapping="Wrap" Foreground="#FF23FF00"></TextBlock>  
         </Grid>  
         <ListBox x:Name="lstMusic" Margin="5,10,5,5"  
              HorizontalAlignment="Left"  
              ItemsSource="{Binding}"   
              SelectionChanged="LstMusic_SelectionChanged"   
              >  
           <ListBox.ItemTemplate>  
             <DataTemplate>  
               <StackPanel Orientation="Vertical" Margin="2" d:DesignWidth="406">  
                 <Grid>  
                   <Grid.RowDefinitions>  
                     <RowDefinition Height="*"/>  
                     <RowDefinition Height="*"/>  
                   </Grid.RowDefinitions>  
                   <Grid Grid.Row="0" Margin="0,-4,0,0" RenderTransformOrigin="0.5,0.5" Background="Yellow">  
                        <Grid.RenderTransform>  
                             <CompositeTransform ScaleY="-1"/>  
                        </Grid.RenderTransform></Grid>  
                   <Grid Grid.Row="1">  
                     <Grid.RowDefinitions>  
                       <RowDefinition Height="*"/>  
                       <RowDefinition Height="*"/>  
                       <RowDefinition Height="*"/>  
                     </Grid.RowDefinitions>  
                     <Grid.ColumnDefinitions>  
                       <ColumnDefinition Width="160*" />  
                       <ColumnDefinition Width="241*" />  
                     </Grid.ColumnDefinitions>  
                     <TextBlock Grid.Row="0" Grid.Column="0"   
                         Text="Artist Name:" TextWrapping="Wrap" Margin="0,0,20,0" />  
                     <TextBlock Grid.Row="0" Grid.Column="1"  
                         x:Name="txtArtistName"  
                         Text="{Binding Artist}" TextWrapping="Wrap"/>  
                     <TextBlock Grid.Row="1" Grid.Column="0"  
                         Text="Album Name:" TextWrapping="Wrap" Margin="0,0,20,0" />  
                     <TextBlock Grid.Row="1" Grid.Column="1"  
                         x:Name="txtAlbumName"   
                         Text="{Binding Album}" TextWrapping="Wrap" />  
                     <TextBlock Grid.Row="2" Grid.Column="0"  
                         Text="Release Date:" Margin="0,0,20,0" />  
                     <TextBlock Grid.Row="2" Grid.Column="1"  
                         x:Name="txtReleaseDate"   
                         Text="{Binding ReleaseDate}" TextWrapping="Wrap" />  
                   </Grid>  
                 </Grid>  
               </StackPanel>  
             </DataTemplate>  
           </ListBox.ItemTemplate>  
         </ListBox>  
       </StackPanel>  
     </Grid>  
   </Grid>  
 </phone:PhoneApplicationPage>  

This is a simple list box base template. If you face any styling related issues the remove the styling classes that are not linked in your project.
3) Now create a new model class and name it "MusicRecord.cs". 
4) Replace existing code in file with following:


 namespace AccessListboxTempElements  
 {  
   using System;  
   using System.Collections.Generic;  
   using System.Linq;  
   using System.Text;  
   using System.Threading.Tasks;  
   /// <summary>  
   /// Music Record class.  
   /// </summary>  
   public class MusicRecord  
   {  
     #region Default Constructor method.  
     /// <summary>  
     /// Initializes a new instance of the <see cref="MusicRecord" /> class.  
     /// </summary>  
     /// <param name="artistName">Artist name parameter</param>  
     /// <param name="albumName">Album name parameter</param>  
     /// <param name="releaseDate">Release data parameter</param>  
     public MusicRecord(string artistName, string albumName, DateTime releaseDate)  
     {  
       try  
       {  
         // Settings.  
         this.Artist = artistName;  
         this.Album = albumName;  
         this.ReleaseDate = releaseDate;  
       }  
       catch (Exception ex)  
       {  
         // Info.  
         throw ex;  
       }  
     }  
     #endregion  
     #region Public / Protected Properties  
     /// <summary>  
     /// Gets or sets artist property.  
     /// </summary>  
     public string Artist { get; set; }  
     /// <summary>  
     /// Gets or sets album property.  
     /// </summary>  
     public string Album { get; set; }  
     /// <summary>  
     /// Gets or sets release date property.  
     /// </summary>  
     public DateTime ReleaseDate { get; set; }  
     #endregion  
   }  
 }  

This is a simple model class that we shall be using in our list box template for each element.

5) Now in "MainPage.xml.cs" file, replace the existing code with the following:


namespace AccessListboxTempElements  
 {  
   using System;  
   using System.Collections.Generic;  
   using System.Collections.ObjectModel;  
   using System.Linq;  
   using System.Net;  
   using System.Reflection;  
   using System.Windows;  
   using System.Windows.Controls;  
   using System.Windows.Media;  
   using System.Windows.Navigation;  
   using AccessListboxTempElements.Resources;  
   using Microsoft.Phone.Controls;  
   using Microsoft.Phone.Shell;  
   /// <summary>  
   /// Main page.  
   /// </summary>  
   public partial class MainPage : PhoneApplicationPage  
   {  
     #region Private property.  
     /// <summary>  
     /// Music records list.  
     /// </summary>  
     private ObservableCollection<MusicRecord> myMusic = new ObservableCollection<MusicRecord>();  
     #endregion  
     #region Default Constructor  
     /// <summary>  
     /// Initializes a new instance of the <see cref="MainPage" /> class..  
     /// </summary>  
     public MainPage()  
     {  
       try  
       {  
         // Initialization.  
         this.InitializeComponent();  
         // Add items to the collection.  
         this.myMusic.Add(new MusicRecord("Chris Sells", "Chris Sells Live", new DateTime(2008, 2, 5)));  
         this.myMusic.Add(new MusicRecord("Luka Abrus", "The Road to Redmond", new DateTime(2007, 4, 3)));  
         this.myMusic.Add(new MusicRecord("Jim Hance", "The Best of Jim Hance", new DateTime(2007, 2, 6)));  
         // Set the data context for the list box.  
         this.lstMusic.DataContext = this.myMusic;  
       }  
       catch (Exception ex)  
       {  
         // Info.  
         MessageBox.Show(ex.Message);  
       }  
     }  
     #endregion

     #region Music list selection changed event method.
     /// <summary>  
     /// Music list selection changed event method.  
     /// </summary>  
     /// <param name="sender">Sender parameter</param>  
     /// <param name="e">Event parameter</param>  
     private void LstMusic_SelectionChanged(object sender, SelectionChangedEventArgs e)  
     {  
       try  
       {  
       }  
       catch (Exception ex)  
       {  
         // Info.  
         MessageBox.Show(ex.Message);  
       }  
     }  
     #endregion  
   }  
 }  

In the above code, we have created a ObservableCollection list called myMusic with each element representing our model structure. Then, in our constructor we have added few elements into the list and finally bind our list to out list box element in view.

6) Execute the project and you will simply see a list displaying the data that we have provided. At this point we are not accessing anything.


7) For accessing visual element from the list on selection, add following code after the main constructor as follow:


    #region Music list selection changed event method.  
     /// <summary>  
     /// Music list selection changed event method.  
     /// </summary>  
     /// <param name="sender">Sender parameter</param>  
     /// <param name="e">Event parameter</param>  
     private void LstMusic_SelectionChanged(object sender, SelectionChangedEventArgs e)  
     {  
       try  
       {  
         var container = (FrameworkElement)this.lstMusic.ItemContainerGenerator.ContainerFromItem(e.AddedItems[0]);  
         if (container != null)  
         {  
           // Settings.  
           TextBlock artistName = this.FindVisualChild<TextBlock>(container, "txtArtistName");  
           TextBlock albumName = this.FindVisualChild<TextBlock>(container, "txtAlbumName");  
           TextBlock releaseDate = this.FindVisualChild<TextBlock>(container, "txtReleaseDate");  
           // Settings.  
           this.txtSel.Text = artistName.Text + " - " + albumName.Text + " - " + releaseDate.Text;  
         }  
       }  
       catch (Exception ex)  
       {  
         // Info.  
         MessageBox.Show(ex.Message);  
       }  
     }  
     #endregion  
     #region Find visual child method.  
     /// <summary>  
     /// Find visual child method.  
     /// </summary>  
     /// <typeparam name="T">Template type.</typeparam>  
     /// <param name="obj">Object parameter.</param>  
     /// <param name="objname">Object name parameter.</param>  
     /// <returns>Returns - visual child</returns>  
     private T FindVisualChild<T>(DependencyObject obj, string objname) where T : DependencyObject  
     {  
       try  
       {  
         // Initialization  
         string controlObjName = string.Empty;  
         // Verify if the object contains name property.  
         Type tyype = obj.GetType();  
         // Verification.  
         if (tyype.GetProperty("Name") != null)  
         {  
           // Settings.  
           PropertyInfo prop = tyype.GetProperty("Name");  
           controlObjName = prop.GetValue((object)obj, null).ToString();  
         }  
         else  
         {  
           // Info.  
           return null;  
         }  
         // Verify if specify object name matches to the require control name.  
         if (obj is T && objname.ToString().ToLower().Equals(controlObjName.ToString().ToLower()))  
         {  
           // Info.  
           return obj as T;  
         }  
         // Check for children  
         int childrenCount = VisualTreeHelper.GetChildrenCount(obj);  
         // Verification.  
         if (childrenCount < 1)  
         {  
           // Info.  
           return null;  
         }  
         // First check all the children  
         for (int i = 0; i <= childrenCount - 1; i++)  
         {  
           // Initialization.  
           DependencyObject child = VisualTreeHelper.GetChild(obj, i);  
           // Processing.  
           if (child is T && objname.ToString().ToLower().Equals(controlObjName.ToString().ToLower()))  
           {  
             // Info.  
             return child as T;  
           }  
         }  
         // Then check the childrens children  
         for (int i = 0; i <= childrenCount - 1; i++)  
         {  
           // Initialization.  
           string checkobjname = objname;  
           // Recursion.  
           DependencyObject child = this.FindVisualChild<T>(VisualTreeHelper.GetChild(obj, i), objname);  
           if (child != null && child is T && objname.ToString().ToLower() == checkobjname.ToString().ToLower())  
           {  
             // Info.  
             return child as T;  
           }  
         }  
       }  
       catch (Exception ex)  
       {  
         // Info.  
         MessageBox.Show(ex.Message);  
       }  
       // Info.  
       return null;  
     }  
     #endregion  

In the above code we have created a selection change event "LstMusic_SelectionChanged(...)" method and "FindVisualChild(...)" method. Selection changed method simply get the selected element, convert it into a FrameworkElement and pass the require information i.e. main container (which is our selected element) & the name of the element (in element template that need to be accessed from list box) into "FindVisualChild(...)" method. "FindVisualChild(...)" method basically searches the element that you want to access by name and return the element handler in a visual element form i.e. TextBlock, CheckBox etc.

8) Now build & execute the project and select an element from the list you will see following:


Thats about it!!!

Enjoy coding!!!

No comments