Part 3: Image Search Result View
Next step after calling a webservice was to visualize the result of a search. To start with Basic, I wanted to get to basic grid (I have few ideas to use by Stack3dPanel in the future but wanted to start with Basics).Some of the functionality explained in this posts are…
I wanted to get to screen shot below.
To get started, I created few helper classes and I will use them later.
ImageSourceConverter
I needed to create a value converter that converts a URI into ImageSource (BitmapImage) so that I can use the results returned by query in databinding. Code here is pretty simple.
1: using System;
2: using System.Windows;
3: using System.Windows.Controls;
4: using System.Windows.Media.Imaging;
5: using System.Windows.Data;
6:
7: namespace Search
8: {
9: /// <summary>
10: /// Value converter that can convert Uri to ImageSource (BitmapImage)
11: /// </summary>
12: public class ImageSourceConverter : IValueConverter
13: {
14: #region Public Members
15:
16: public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
17: {
18: Uri source = new Uri((string)value, UriKind.Absolute);
19: return new BitmapImage(source);
20: }
21:
22: public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
23: {
24: return ((BitmapImage)value).UriSource;
25: }
26: #endregion
27: }
28: }
SearchResultCollection
I needed to create a class that I can use as a source of Collection. I ended up doing this is because my actual source for databinding is collection of this collection class (2-D array)
1: using System;
2: using Search.SearchService;
3: using System.Collections.ObjectModel;
4:
5: namespace Search
6: {
7: /// <summary>
8: /// Class to hold collection of results that can be databound
9: /// </summary>
10: public class SearchResultCollection : ObservableCollection<SearchService.Result>
11: {
12: }
13: }
Tile
Eventually I needed to have to sources for databinding. One was the actual collection itself and I use datacontext to set the source of databinding. Then there are some settings (height/width of each tile or padding around the tile). I created this class so that I can use Source based databinding
1: using System;
2: using System.Windows;
3: using System.ComponentModel;
4:
5: namespace Search
6: {
7: /// <summary>
8: /// Class that holds the tile settings. It implements INotifyPropertyChanged
9: /// so that it can be used as a source of databinding
10: /// </summary>
11: public class Tile : INotifyPropertyChanged
12: {
13:
14: #region Public Members
15: //implements INotifyPropertyChanged event
16: public event PropertyChangedEventHandler PropertyChanged;
17:
18: //represents the height of a tile
19: public double TileHeight
20: {
21: get { return _tileheight; }
22: set
23: {
24: _tileheight = value;
25: NotifyPropertyChanged("TileHeight");
26: }
27: }
28:
29: //represents width of a tile
30: public double TileWidth
31: {
32: get { return _tilewidth; }
33: set
34: {
35: _tilewidth = value;
36: NotifyPropertyChanged("TileWidth");
37: }
38: }
39:
40: //represents padding around a tile
41: public Thickness TilePadding
42: {
43: get { return _tilepadding; }
44: set
45: {
46: _tilepadding = value;
47: NotifyPropertyChanged("TilePadding");
48: }
49: }
50: #endregion
51:
52: #region Private Memebers
53:
54: private double _tileheight = 10;
55: private double _tilewidth = 10;
56: private Thickness _tilepadding = new Thickness(5);
57:
58: //fires Property Change event.
59: private void NotifyPropertyChanged(String info)
60: {
61: if (PropertyChanged != null)
62: {
63: PropertyChanged(this, new PropertyChangedEventArgs(info));
64: }
65: }
66: #endregion
67: }
68: }
69:
Grid View
Xaml
Having all this classes, now lets start with a grid view. Here is the XAML that I ended up with…
1: <UserControl x:Class="Search.ImageGridView"
2: xmlns="http://schemas.microsoft.com/client/2007"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:control="clr-namespace:Search;assembly=Search" >
5: <UserControl.Resources>
6: <control:ImageSourceConverter x:Key="converter"/>
7: <control:Tile x:Key="TileInfo"/>
8: </UserControl.Resources>
9: <Grid x:Name="LayoutRoot">
10: <ItemsControl Name ="outic" ItemsSource="{Binding}" VerticalAlignment="Top" >
11: <ItemsControl.ItemTemplate>
12: <DataTemplate>
13: <ItemsControl ItemsSource="{Binding}">
14: <ItemsControl.ItemsPanel>
15: <ItemsPanelTemplate>
16: <StackPanel Orientation="Horizontal"/>
17: </ItemsPanelTemplate>
18: </ItemsControl.ItemsPanel>
19: <ItemsControl.ItemTemplate>
20: <DataTemplate>
21: <Border
22: BorderThickness="2,2,2,2" BorderBrush="#FF313131"
23: CornerRadius="5,5,5,5"
24: Height="{Binding TileHeight,Source={StaticResource TileInfo}}"
25: Width="{Binding TileWidth,Source={StaticResource TileInfo}}"
26: Margin="{Binding TilePadding,Source={StaticResource TileInfo}}"
27: MouseEnter="Border_MouseEnter"
28: MouseLeave="Border_MouseLeave" RenderTransformOrigin="1,1">
29: <Border.RenderTransform>
30: <TransformGroup>
31: <ScaleTransform ScaleX="1" ScaleY="1"/>
32: <SkewTransform AngleX="0" AngleY="0"/>
33: <RotateTransform Angle="0"/>
34: <TranslateTransform X="0" Y="0"/>
35: </TransformGroup>
36: </Border.RenderTransform>
37: <Border.Background>
38: <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
39: <GradientStop Color="#FFFFFFFF"/>
40: <GradientStop Color="#FF71A7BB" Offset="1"/>
41: </LinearGradientBrush>
42: </Border.Background>
43: <Image
44: Source="{Binding Image.ImageURL, Converter={StaticResource converter}}"
45: ImageFailed="Image_ImageFailed"
46: Cursor="Hand" Opacity="0.7"
47: Margin="1,1,1,1"/>
48: </Border>
49: </DataTemplate>
50: </ItemsControl.ItemTemplate>
51: </ItemsControl>
52: </DataTemplate>
53: </ItemsControl.ItemTemplate>
54: </ItemsControl>
55: </Grid>
56: </UserControl>
Here are some of the interesting points about this XAML…
- I did not use Grid panel, instead I used nested ItemsControl. so think of one item control shows a row of images. Then I use another ItemsControl to repeat these rows to get the grid look.
- To achieve this, I set the ItemsPanel property on the inner ItemsControl. I set the panel to be StackPanel (which is default) but I changed the orientation to be Horizontal (Vertical is default). This way I get a row of images
- Outer Items control repeats this inner items control (showing a row of images). This way I get a Grid of Images.
- Since the data context for this control is Collection of collection. Outer Items control sets collection instance in collection of collection to be the source for inner items control.
- Inside inner items control, I have a border that lays out each image (Image element inside).
- Border defines Height/Width/Padding for each tile.
- In the resources section, I have defined a Tile instance.
- Border height/width/margin are databound to TileHeight, TileWidth, TilePadding property of a Tile class.
- Border also defines the interactive behavior through Mouse Enter/leave events.
- Image element is contained within each of the border.
- Image element is bound to Uri returned by search service call
- Since Uri needs to be converted to ImageSource (BitmapImage), I use Value Converters of type ImageSourceConverter
- In the resources section, ImageSourceConverter is added.
- Image element also hooks up the ImageFailed event so that placeholder image can be shown in case, actual image can not be shown.
Code Behind
Now here is the code behind for the control.
1: using System;
2: using System.Windows;
3: using System.Windows.Controls;
4: using System.Windows.Media.Imaging;
5: using System.Windows.Media;
6:
7: namespace Search
8: {
9: public partial class ImageGridView : UserControl
10: {
11: #region Public Members
12:
13: //public constructor
14: public ImageGridView()
15: {
16: // Required to initialize variables
17: InitializeComponent();
18: _tileinfo = this.Resources["TileInfo"] as Tile;
19: }
20:
21: //height of each tile
22: public double TileHeight
23: {
24: get { return _tileinfo.TileHeight; }
25: set { _tileinfo.TileHeight = value; }
26: }
27:
28: //width of each tile
29: public double TileWidth
30: {
31: get { return _tileinfo.TileWidth; }
32: set { _tileinfo.TileWidth = value; }
33: }
34:
35: //padding around each tile applied as uniform thickness
36: public Double TilePadding
37: {
38: get { return _tileinfo.TilePadding.Top; }
39: set
40: {
41: //converts double value to uniform thickness value
42: _tileinfo.TilePadding = new Thickness((double)value);
43: }
44: }
45: #endregion
46:
47: #region Private Members
48: //handle to tile settings
49: private Tile _tileinfo;
50:
51: //if iamge fails to load then placeholder image is shown
52: private void Image_ImageFailed(object sender, ExceptionRoutedEventArgs e)
53: {
54: ((Image)sender).Source = new BitmapImage(new Uri("notfound.jpg", UriKind.Relative));
55: }
56:
57: //on mouse enter, increase the size of image and set opacity to 1
58: private void Border_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
59: {
60: Border b = (Border)sender;
61: b.Child.Opacity = 1;
62: TransformGroup tg = b.RenderTransform as TransformGroup;
63: ScaleTransform st = tg.Children[0] as ScaleTransform;
64: st.ScaleX = 1.1;
65: st.ScaleY = 1.1;
66: }
67:
68: //on mouse leave, reset the size of image and set opacity to 0
69: private void Border_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
70: {
71: Border b = (Border)sender;
72: b.Child.Opacity = 0.7;
73: TransformGroup tg = b.RenderTransform as TransformGroup;
74: ScaleTransform st = tg.Children[0] as ScaleTransform;
75: st.ScaleX = 1;
76: st.ScaleY = 1;
77: }
78: #endregion
79: }
80: }
This is pretty straight forward too
- Control exposes following public APIs
- Constructor
- TileHeight/TileWidth so that consumer of this control can decide how big the tile should be
- TilePadding because setting height/width to be large has impact on things getting clipped so I also thought consumer should be allowed to set the padding. Padding property is interesting because it actually needs to be bound to Margin which is of type thickness but since I could not parse this property on my custom control, I use a hack. Even though Tile class exposes TilePadding to be of type thickness, I expose it as double. This way, I convert the double to thickness before setting it to Tile that was declared in the resource section and then I can use it in databinding without needing another value converter. Side effect is I assume uniform margin, which I did not think was a big problem for me for this scenario
- These are private members
- ImageFailed event handler, sets the source of the image that failed to place holder image
- Border’s mouse enter event handler, applies a scaletransform to itself so that image looks bigger when mouse is over it, it also sets the opacity to be 1 so that image appears clear
- Border’s mouse leave event handler, resets the scaletransform to 1 so that image goes back to original size and also sets the opacity to be 0.7.
Consumption of Grid Control
Xaml
1: <UserControl x:Class="Search.Page"
2: xmlns="http://schemas.microsoft.com/client/2007"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5: xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6: xmlns:control="clr-namespace:Search;assembly=Search"
7: mc:Ignorable="d" d:DesignHeight="500" d:DesignWidth="500">
8:
9: <UserControl.Resources>
10: <RadialGradientBrush x:Key="PageBackgroundBrush"
11: GradientOrigin="-0.0240000002086163,-0.0240000002086163">
12: <RadialGradientBrush.RelativeTransform>
13: <TransformGroup>
14: <ScaleTransform CenterX="0.5" CenterY="0.5"
15: ScaleX="0.991" ScaleY="0.987"/>
16: <SkewTransform CenterX="0.5" CenterY="0.5"/>
17: <RotateTransform CenterX="0.5" CenterY="0.5"/>
18: <TranslateTransform X="-0.001" Y="-0.005"/>
19: </TransformGroup>
20: </RadialGradientBrush.RelativeTransform>
21: <GradientStop Color="#FFFFFFFF"/>
22: <GradientStop Color="#FFACD3E5" Offset="1"/>
23: <GradientStop Color="#FFF1F5F7" Offset="0.60000002384185791"/>
24: <GradientStop Color="#FFC8E7F7" Offset="0.29499998688697815"/>
25: </RadialGradientBrush>
26: <Style x:Key="ButtonStyle" TargetType="Button">
27: <Setter Property="Background" Value="#FF007FFF"/>
28: <Setter Property="Foreground" Value="Black"/>
29: <Setter Property="FontFamily" Value="Fontin-Bold.ttf#Fontin"/>
30: <Setter Property="FontSize" Value="20"/>
31: </Style>
32: <LinearGradientBrush x:Key="TextBoxBackgroundBrush"
33: EndPoint="0.5,1" StartPoint="0.5,0">
34: <GradientStop Color="#FFFFFFFF" Offset="0.518"/>
35: <GradientStop Color="#FF9BDAFB" Offset="1"/>
36: <GradientStop Color="#FF9BDAFB"/>
37: <GradientStop Color="#FFFFFFFF" Offset="0.138"/>
38: <GradientStop Color="#FFFFFFFF" Offset="0.857"/>
39: </LinearGradientBrush>
40: <Storyboard x:Name="SearchProgressAnimation" RepeatBehavior="1x">
41: <DoubleAnimationUsingKeyFrames Storyboard.TargetName="LayoutRoot"
42: Storyboard.TargetProperty="(Panel.Background).(Brush.RelativeTransform).(TransformGroup.Children)[2].(RotateTransform.Angle)"
43: BeginTime="00:00:00">
44: <SplineDoubleKeyFrame KeyTime="00:00:01" Value="90.145"/>
45: <SplineDoubleKeyFrame KeyTime="00:00:02" Value="180.143"/>
46: <SplineDoubleKeyFrame KeyTime="00:00:03" Value="270.128"/>
47: <SplineDoubleKeyFrame KeyTime="00:00:04" Value="359.946"/>
48: </DoubleAnimationUsingKeyFrames>
49: <PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse"
50: Storyboard.TargetProperty="(Shape.Fill).(LinearGradientBrush.StartPoint)"
51: BeginTime="00:00:00">
52: <SplinePointKeyFrame KeyTime="00:00:01" Value="0.716000020503998,0.439000010490417"/>
53: <SplinePointKeyFrame KeyTime="00:00:02" Value="0.382999986410141,0.684000015258789"/>
54: <SplinePointKeyFrame KeyTime="00:00:03" Value="0.0759999975562096,0.231000006198883"/>
55: <SplinePointKeyFrame KeyTime="00:00:04" Value="0.726000010967255,0.395999997854233"/>
56: </PointAnimationUsingKeyFrames>
57: <PointAnimationUsingKeyFrames Storyboard.TargetName="ellipse"
58: Storyboard.TargetProperty="(Shape.Fill).(LinearGradientBrush.EndPoint)"
59: BeginTime="00:00:00">
60: <SplinePointKeyFrame KeyTime="00:00:01" Value="0.0769999995827675,0.228000000119209"/>
61: <SplinePointKeyFrame KeyTime="00:00:02" Value="0.409999996423721,-0.017000000923872"/>
62: <SplinePointKeyFrame KeyTime="00:00:03" Value="0.717000007629395,0.435999989509583"/>
63: <SplinePointKeyFrame KeyTime="00:00:04" Value="0.0670000016689301,0.270999997854233"/>
64: </PointAnimationUsingKeyFrames>
65: </Storyboard>
66: <Storyboard x:Name="MakeSearchResultVisible">
67: <DoubleAnimationUsingKeyFrames Storyboard.TargetName="gridscroll" Storyboard.TargetProperty="(UIElement.Opacity)" BeginTime="00:00:00">
68: <SplineDoubleKeyFrame x:Name="startopacity" KeyTime="00:00:00" Value="0"/>
69: <SplineDoubleKeyFrame x:Name="endopacity" KeyTime="00:00:02" Value="1"/>
70: </DoubleAnimationUsingKeyFrames>
71: </Storyboard>
72: </UserControl.Resources>
73:
74: <Grid x:Name="LayoutRoot" Background="{StaticResource PageBackgroundBrush}" SizeChanged="LayoutRoot_SizeChanged">
75: <Grid.RowDefinitions>
76: <RowDefinition Height="0.091*"/>
77: <RowDefinition Height="50"/>
78: <RowDefinition Height="0.833*"/>
79: </Grid.RowDefinitions>
80: <Grid.ColumnDefinitions>
81: <ColumnDefinition Width="0.098*"/>
82: <ColumnDefinition Width="0.66*" MinWidth="300" x:Name="imagegridcolumn"/>
83: <ColumnDefinition Width="0.242*"/>
84: </Grid.ColumnDefinitions>
85: <Button
86: Grid.Column="1" Grid.Row="1" Margin="0,10,10,10"
87: MaxWidth="100"
88: Content="Search"
89: Name="btnSearch"
90: HorizontalAlignment="Right"
91: Style="{StaticResource ButtonStyle}"
92: Click="Button_Click"/>
93: <TextBox
94: Grid.Column="1" Grid.Row="1" Margin="40,10,120,10"
95: Text="Silverlight"
96: x:Name="txtSearch"
97: Background="{StaticResource TextBoxBackgroundBrush}"
98: FontFamily="Fontin-Bold.ttf#Fontin" FontSize="20"/>
99: <Canvas Grid.Column="0" Grid.Row="1" Margin="30.1760005950928,5,0,-26" VerticalAlignment="Stretch">
100: <Ellipse HorizontalAlignment="Stretch" Margin="0,0,0,0"
101: VerticalAlignment="Stretch" Stroke="#FF000000"
102: StrokeThickness="4" Width="51" Height="48.682" x:Name="ellipse">
103: <Ellipse.Fill>
104: <LinearGradientBrush EndPoint="0.293000012636185,0.666999995708466"
105: StartPoint="0.5,0">
106: <GradientStop Color="#FF5B5B5B"/>
107: <GradientStop Color="#FFFFFFFF" Offset="1"/>
108: </LinearGradientBrush>
109: </Ellipse.Fill>
110: </Ellipse>
111: <Path Height="28" Width="27" Canvas.Left="36.824" Canvas.Top="38.076"
112: Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000"
113: StrokeThickness="4" Data="M39.823997,41.075745 L59.823997,62.075745"/>
114: <Path Height="22" HorizontalAlignment="Right" Margin="0,0,0,0"
115: VerticalAlignment="Top" Width="21" Grid.Row="2" Fill="#FFFFFFFF"
116: Stretch="Fill" Stroke="#FF000000" StrokeThickness="7"
117: Data="M139.2532,209.79701 L125.25321,194.79701"
118: Canvas.Top="45.374" Canvas.Left="43.402"/>
119: </Canvas>
120: <ScrollViewer
121: HorizontalScrollBarVisibility="Auto"
122: VerticalScrollBarVisibility="Auto"
123: Grid.Column="1" Grid.Row="2"
124: Name="gridscroll"
125: Opacity="0">
126: <control:ImageGridView
127: Name ="imageGrid"
128: VerticalAlignment="Top" Margin="20,0,20,0"
129: TileHeight="100" TileWidth="100" TilePadding="10" />
130: </ScrollViewer>
131: </Grid>
132: </UserControl>
New Page.Xaml are above….
- Added a ScrollViewer so that I can get scrollbar if needed, but I have set the scrollbar visibility to Auto so that it does not show up if it is not needed
- Inside Scrollbar, there is ImageGrid control. I have set the TileHeight/Width to be 100.
- I have also added MakeSearchResultVisible storyboard in resources section so that I can fade in the grid view after the search service call is complete.
Code Behind
1: using System;
2: using System.Windows;
3: using System.Windows.Controls;
4: using System.Windows.Input;
5: using System.Windows.Media.Animation;
6: using Search.SearchService;
7: using System.Collections.ObjectModel;
8: using System.ServiceModel;
9: using System.Net;
10:
11: namespace Search
12: {
13:
14: public partial class Page : UserControl
15: {
16: #region Public Members
17: //public constructor
18: public Page()
19: {
20: InitializeComponent();
21: _searchresultcollectioncollection = new ObservableCollection<SearchResultCollection>();
22: _imagegridview = LayoutRoot.FindName("imageGrid") as ImageGridView;
23: _gridscroll = LayoutRoot.FindName("gridscroll") as ScrollViewer;
24: _imagegridview.DataContext = _searchresultcollectioncollection;
25: _progress = LayoutRoot.FindName("SearchProgressAnimation") as Storyboard;
26: _makesearchresultsvisible = LayoutRoot.FindName("MakeSearchResultVisible") as Storyboard;
27: _progress.Completed += new EventHandler(_progress_Completed);
28: }
29: #endregion
30:
31: #region Public Memebers
32: private SearchResultCollection _imagesearchresultcollection;
33: private ObservableCollection<SearchResultCollection> _searchresultcollectioncollection;
34: private Boolean _searchcompleted;
35:
36: private ImageGridView _imagegridview;
37: private ScrollViewer _gridscroll;
38:
39: private Storyboard _progress;
40: private Storyboard _makesearchresultsvisible;
41:
42: //Rearranges the size of source collection according to size
43: private void LayoutRoot_SizeChanged(object sender, SizeChangedEventArgs e)
44: {
45: int numberofcolumns = GetNumberofColumns();
46: CreateCollectionofCollections(_imagesearchresultcollection, numberofcolumns);
47: }
48:
49: //gets the number of columns depending on the size available and tileheight
50: private int GetNumberofColumns()
51: {
52: return (int)((imagegridcolumn.ActualWidth ) / (_imagegridview.TileHeight + 2 *_imagegridview.TilePadding));
53: }
54:
55: //need to do this because I wanted at least one cycle of animation to be complete
56: private void _progress_Completed(object sender, EventArgs e)
57: {
58: if (_searchcompleted == true)
59: {
60: _progress.Stop();
61: MakeSearchResultsVisible(0, 1);
62: }
63: else
64: {
65: _progress.Begin();
66: }
67: }
68:
69: //starts the search
70: private void Button_Click(object sender, RoutedEventArgs e)
71: {
72: _searchcompleted = false;
73: //starts the animation during webservice call
74: _progress.Begin();
75: //makes the grid view invisible
76: _gridscroll.Opacity = 0;
77: //makes the service call
78: GetLiveData();
79: }
80:
81: //runs the animation when search is complete.
82: private void MakeSearchResultsVisible(int startopacity, int endopacity)
83: {
84: SplineDoubleKeyFrame temp;
85: temp = LayoutRoot.FindName("startopacity") as SplineDoubleKeyFrame;
86: temp.Value = startopacity;
87: temp = LayoutRoot.FindName("endopacity") as SplineDoubleKeyFrame;
88: temp.Value = endopacity;
89: _makesearchresultsvisible.Begin();
90: }
91:
92: //calls the webservice
93: private void GetLiveData()
94: {
95: try
96: {
97: //Initialize web service
98: string searchquery = txtSearch.Text;
99: BasicHttpBinding binding = new BasicHttpBinding();
100: EndpointAddress address = new EndpointAddress("http://soap.search.msn.com/webservices.asmx");
101: MSNSearchPortTypeClient searchservice = new MSNSearchPortTypeClient(binding, address);
102: SearchRequest searchrequest = new SearchRequest();
103: SourceRequest[] sr = new SourceRequest[3];
104:
105: sr[0] = new SourceRequest();
106: sr[0].Source = SourceType.Image;
107: sr[0].ResultFields = ResultFieldMask.Url | ResultFieldMask.Title | ResultFieldMask.Description | ResultFieldMask.Image;
108: sr[0].Count = 50;
109:
110: searchrequest.Query = searchquery;
111: searchrequest.Requests = sr;
112: searchrequest.AppID = "Enter your key here";
113: searchrequest.CultureInfo = "en-US";
114: searchservice.SearchCompleted += new EventHandler<Search.SearchService.SearchCompletedEventArgs>(searchservice_SearchCompleted);
115: searchservice.SearchAsync(searchrequest);
116: }
117: catch (WebException webx)
118: {
119: txtSearch.Text = webx.ToString();
120: _searchcompleted = true;
121: }
122: }
123:
124: //when the search is completed, converts the search results to observation collection
125: private void searchservice_SearchCompleted(object sender, Search.SearchService.SearchCompletedEventArgs e)
126: {
127: Result[] _imageresults;
128:
129: if (e.Error != null)
130: txtSearch.Text = e.Error.Message;
131: else
132: {
133: _imagesearchresultcollection = new SearchResultCollection();
134: _imageresults = e.Result.Responses[0].Results;
135: for (int counter = 0; counter < _imageresults.Length; counter++)
136: {
137: if (_imageresults[counter].Image.ImageURL.Contains("jpg") || _imageresults[counter].Image.ImageURL.Contains("png"))
138: _imagesearchresultcollection.Add(_imageresults[counter]);
139: }
140: int numberofcolumns = GetNumberofColumns();
141: CreateCollectionofCollections(_imagesearchresultcollection, numberofcolumns);
142: }
143: _searchcompleted = true;
144: }
145:
146: //creates the source collection with specified number of columns.
147: private void CreateCollectionofCollections(SearchResultCollection scollection, int columns)
148: {
149: int counter = 0;
150: if (scollection != null)
151: {
152: _searchresultcollectioncollection.Clear();
153: while (counter < scollection.Count)
154: {
155: SearchResultCollection searchresultcollection = new SearchResultCollection();
156: for (int j = 0; j < columns; j++)
157: {
158: if (counter < scollection.Count)
159: {
160: searchresultcollection.Add(scollection[counter]);
161: counter++;
162: }
163: }
164: _searchresultcollectioncollection.Add(searchresultcollection);
165: }
166: }
167: }
168: #endregion
169:
170: }
171: }
Here are the few things about the code that might be interesting in order to understand the code…
- I use “_searchresultcollectioncollection” to represent the 2-D array that I need so that I can set it to be source of nested ItemsControl
- I recalculate that everytime search happens or size of the grid changes
- I think, this part could be made lot more tighter but I just stuck with it.
- I also start the animation to fade in the results once query is complete.
Here is the link to working project.
Yourself is not local, principal 30-40 bubble fashionable establishment, and would exist a non-governmental newspaper of record nearby itself. http://ozefobi.com
britt1981
March 2, 2012 at 4:29 am