Search
Monday, November 12, 2018 ..:: Home ::.. Register  Login
   Calendar  
     
  
   Search  
     
  
   Blogroll  
     
  
   Disclosure  
All blog entries are the opinions of the author and do not necessarily reflect the opinions of their employer. All the code presented is for explanation and demonstration purposes only. Any damages incurred to your site and/or data are not the responsibility of the author. Every effort is taken to ensure the code properly compiles, however sometimes there are some hiccups and you might be required to do your own debugging.
     
  
   TechTidBits (Blog)  

Playing with WP7 popups and context menus (and so much more I found)

Mar 21

Written by:
Monday, March 21, 2011 8:14 PM  RssIcon

Context Menu Popup from Code Demo 7Are you getting into the WP7?  Have you tried using a Listbox?  What about a Context Menu?  If are confused, check this out, I go from nothing to loading listbox with XML data file, formatted with an image and removing items via a popup Context Menu too!

Last week, I was wondering how Microsoft does the Context Menu popup in their email clients to delete.  Ok, so I have a lot of long red lights on my drive to/from work!  DOH!  (nooooooooooo, I don't make phone calls while driving, don't text'n'drive!)

Well, I figured, how long could it take for me to do that?!  DOH!  A few days later, I have a great blog idea I want to share with you.  To cut to the chase, here's my progression of what I went through to arrive at the context menu popup (yes, I figured I would take this time to learn more about data binding too haha)

  1. Throw up a ListBox with simple data from the MainPage.xaml, handle the selected event
  2. Make the data come from a POCO, starting to use DataTemplates
  3. Add a logo/image to the mix
  4. Use XML file as data source instead of POCO
  5. Add/play around with filtering data/xml
  6. Playing around with design time data and data resources
  7. FINALLY playing around with Context Menus
Data1

Ok, so let's start this off with the basics.  I wanted, well, truth be told, I felt like I should learn how to just populate a ListBox with simple data.  This is my first attempt.        

I apologize for the image instead of the actual XAML, but DNN does NOT like the XML/HTML tags as contents here. I know, I know.

ListBox Contents
    
 

Data2

Next, I want to move my data away from the XAML file, so the natural next step is in a POCO (Plain Ol'C# Object).  To do that, I created a simple Team object (with TeamName and Nickname properties), as well as a Teams object which would let me load my Team data.

public class Team
{
public string TeamName
{
	get;
	set;
}
	public string Nickname
{
	get;
	set;
}
	protected Team()
{
}
	public Team( string teamName, 
                     string nickname )
	: this()
{
	this.TeamName = teamName;
	this.Nickname = nickname;
}
	public override string ToString()
{
	return TeamName;
}
public class Teams
{
List teams = new List();

public List TeamList
{
	get
	{
		return teams;
	}
}
public void LoadTeams()
{
	teams.Add( new Team( "Boston Bruins", "Bruins" ) );
	teams.Add( new Team( "Montreal Canadiens", "Habs" ) );
	teams.Add( new Team( "Buffalo Sabres", "Sabres" ) );
	teams.Add( new Team( "Toronto Maple Leafs", "Leafs" ) );
	teams.Add( new Team( "Ottawa Senators", "Sens" ) );
}
	public Teams()
{
	LoadTeams();
}
} 
Data3

At this point, I figured, XAML's BIG promise is SEXY UI!  Those text labels up there blow chunks!  DOH!  Ya, seriously, let's add some graphics, pics to this sample!

Build propertiesBesides sourcing the pics, copying them into my project, changing the Build properties (Build Action = Content and Copy to Output Directory = Copy if newer), there was/is ONE more change.  I thought I could do like I/you used to do with ASP.NET, just concatenate "stuff" together for the Image tag.  Nope, can't do that with data binding in XAML. 

To support this properly, you need to have a property.  So here goes, add this property to the Team object above, you're gold!  Notice, I'm using the Nickname property here to help save me time/energy.  This is where I do the concatenation trick of gluing paths and extensions together to get one coherant/logic/existing png file.

public string ImageUri
{
	get
	{
		return "Assets/" + Nickname + ".png";
	}
}
Data4

I quickly tired of hard coding my teams (POCO) and wanted to get one step closer to being dynamic.  So I figured the next logical step was loading the teams up from a Teams.XML file. The only thing that had to change there was how the teams were stored (IEnumerable vs List) and the actual loading from the XML file (using XDocument and Linq).

PS  Oh ya, I also had to create the Teams.xml file and tweak the build properties like I did with the images in the previous step.

public class Teams
{
	IEnumerable teams = new List();

	public IEnumerable TeamList
	{
		get
		{
			return teams;
		}
	}
	public void LoadTeams()
	{
		XDocument xmlTeams = XDocument.Load( "Teams.xml" );
		var data = from xmlData in xmlTeams.Descendants( "Team" )
			select new Team
			{
			TeamName = (string)xmlData.Element( "TeamName" ),
			Nickname = (string)xmlData.Element( "Nickname" )
			};
		teams = data;
	}

	public Teams()
	{
		LoadTeams();
	}
}
 Data5

Teams with Division XML ElementsAt this point, I saw some Linq filtering in some blogs/links and I though THAT would be cool to implement, so I went and tried it.  First I added a Division element to the XML.

Then of course I had to add the three radio buttons to give the user the chance to filter (by Division).  And then next I added the filter Linq to the Teams object.  (Ya, ya, I know, I know this COULD be refactored, but this works fine for an introduction to data binding).

public Teams()
{
XDocument xmlTeams = XDocument.Load( "Teams.xml" );
var data = from xmlData in xmlTeams.Descendants( "Team" )
select new Team
{
TeamName = (string)xmlData.Element( "TeamName" ),
Nickname = (string)xmlData.Element( "Nickname" )
};
teams = data;
}

public Teams( string divisionalFilter )
{
XDocument xmlTeams = XDocument.Load( "Teams.xml" );
var filteredTeams = from xmlFilteredData in 
     xmlTeams.Descendants( "Team" )
where xmlFilteredData.
     Element( "Division" ).Value == divisionalFilter
select new Team
{
TeamName = (string)xmlFilteredData.Element( "TeamName" ),
Nickname = (string)xmlFilteredData.Element( "Nickname" )
};
teams = filteredTeams;
}

I know loading the XML over and over is not the best, but I'm just learning and it works for now to illustrate the Linq filtering concept.

private void FilterListBox( string filter )
{
Teams filteredTeams = null;

if( filter == "Both" )
{
filteredTeams = new Teams();
}
else if( filter == "Northeast" || filter == "Atlantic" )
{
filteredTeams = new Teams( filter );
}
else
{
throw new InvalidOperationException( 
              "Unknown divisional filter." );
}

DivisionalListBox.ItemsSource = filteredTeams.TeamList;
}

private void DivisionFilterRadioButton_Click( 
                                  object sender, 
                                  RoutedEventArgs e )
{
RadioButton filterRadionButton = (RadioButton)sender;
if( filterRadionButton != null )
{
FilterListBox( filterRadionButton.Content.ToString() );
}
}

 All three radio buttons call the same DivisionFilterRadioButton_Click which calls the FilterListBox with the filter string. 

 Data6

Design data in blend

Ok, at this point, I was getting a bit frustrated!  All this time and I STILL haven't even touched Context Menus!  Ok, hold on, there's one more cool thing I thought of which I wanted to try out, design data context in Expression Blend!  Hey, I figured I had the XML data, why couldn't I use Blend to show me some of it to help me format/design the UI in Blend?

I scoured the web for "design data" links, and this is what I ended up getting but only in Blend.  If you're expecting this in Visual Studio, sorry, you're like me, out of luck in VS.  You'll only get this "design data experience" in Blend.

Sample data directoryAfter poking around the project, I found the sample/design data in the SampleData directory.  The XML I selected in Blend was sucked into the project (original was unharmed) and it's now available to be played with/tweaked.

 

 Now, once I did this, two things happened that startled me.  First my teams UI moved!  DOH!  It moved up to the top of the MainPage.XAML in a resource section.

Teams DataTemplate

And the next thing was what was the above resources referenced from the newly changed ListBox

Teams ListBox

Also notice, I had to play with the

d:DataContext="{Binding Source={StaticResource DivisionalSampleDataSource}}"

to get the design data thing to work correctly.  Ironic, I need to add something in Blend (I think I added a field from the Data/Design Data to the form, cut'n'pasted the XAML into VS, saved it, then went back to Blend to see it.  Weird.

 Data7

OK, HERE we go, FINALLY!  I think I'm ALL set to try out Context Menus to delete a team from the list.  WHOA!  Not so fast there pal!  DOH!  Oh no, not more delays? 

OK, OK, this isn't too bad.  WP7 doesn't support context menus natively, you can fudge it, hard code it, OR you could just get the Silverlight Toolkit from CodePlex and use theirs.

The hardest part with using the context menu is actually getting the item you "right mouse clicked on."  But I'm getting ahead of myself.

First you need to add the Toolkit namespace to the Page tag.

Context menu setup

The crux here is, I COULD have put the ContextMenu down in the ListBox, but I had read there was a bug with WP7 right now that the best place to put it is on the individual DataTemplate rows.  It worked both ways for me, but I opted to leave it up top for now.

Next we need to code when someone clicks'n'holds their finger on a team (ie like the right mouse click we're used to seeing in Windows).

private void TeamContextMenuItem_Click( object sender, RoutedEventArgs e )
{
string header = (sender as MenuItem).Header.ToString();

//this is one way to find the team to remove, 
//but it's working with ListBoxItem and manually looping over the Team object
//there has to be a better way
//ListBoxItem teamToRemove = 
//       DivisionalListBox.ItemContainerGenerator.ContainerFromItem( 
//                 (sender as MenuItem).DataContext ) as ListBoxItem;
//List teamsToRemoveFrom = DivisionalListBox.ItemsSource.Cast().ToList();
//foreach( Team teamToCheck in teamsToRemoveFrom )
//{
//  if( teamToCheck.TeamName == ((Team)teamToRemove.Content).TeamName )
//  {
//    teamsToRemoveFrom.Remove( teamToCheck );
//    DivisionalListBox.ItemsSource = teamsToRemoveFrom;
//    break;
//  }
//}

//this is the better way IMHO, 
//but yo have to have the other object (Team) support the Contains method, 
//IEquatable.Equals for the team to be found in the the Contains
if( header == "Remove" )
{
	Team teamToRemove = ((sender as FrameworkElement).DataContext as Team);
	List teamsToRemoveFrom = DivisionalListBox.ItemsSource.Cast().ToList();
	if( teamsToRemoveFrom.Contains( teamToRemove ) )
	{
		teamsToRemoveFrom.Remove( teamToRemove );
		DivisionalListBox.ItemsSource = teamsToRemoveFrom;
	}
	else
	{
		MessageBox.Show( "Romeo, Romeo, where art tho my " + teamToRemove.TeamName );
	}
}
}

If at first, you're saying "Uh, Peter, why don't you just use the SelectectItem or the SelectedIndex?  Good question!  The problem is, you don't always have the item selected before you click'n'hold.  Ya, that's right, the act of pressing'n'holding doesn't select the ListBoxItem first.  Surprised me too, but it makes complete sense after you play with it.  So the better way is grab the Team selected via tha DataContext magic up above. 

Oh, and one more little wrinkle with this.  Notice the .Contains up there?!  Ya, that threw me for a loop for a while too!  You have to  tweak Team to implement IEquatable to support this.  Otherwise you'll NEVER find the currently selected team you JUST found!  Weird I know.

bool IEquatable.Equals( Team other )
{
return other != null && (other.TeamName.CompareTo( this.TeamName ) == 0);
}

Once you do that, when you click'n'hold on a team (like the Sens, DOH! hahahaha) and select Remove, POOF, they'll be done.  Hey, hey, don't worry, they'll be back next year, just as quickly as you click on the filters up top and POOF, they're all back!

Ok, now that you know what I know about data binding, loading XML file, design data in Blend and removing items via Contenxt Menus, it's time to grab a coffee and get coding!

 

Resources

Source Code: http://www.pchenry.com:8080/svn/blog/trunk/2011/PopupsWithDataBinding/

CodePlex: Microsoft Silverlight Toolkit

CodePlex: Microsoft Silverlight Toolkit, February 2011 Download

Copyright ©2011 Peter Henry

Tags:
Categories:
Location: Blogs Parent Separator TechTidBits

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Add Comment   Cancel 
     
  
Copyright 1999-2012 by PCHenry.com   Terms Of Use  Privacy Statement