Bindable Application Bar (Attached Property) – Windows Phone 7

In a prior blog post I had created a Blend Behavior for creating a bindable ApplicationBar. The problem with the Blend Behavior is it seems to have a static instance that then attaches to multiple objects. It’s very confusing to me and I need to do a lot more research on Blend Behaviors. The result has been the creation of a BindableApplicationBar attached property.

I was able to repurpose the code I had written for the Blend Behavior. The new attached property is very simple. It has a collection of ApplicationBarIconButtonDelegate objects and a collection of ApplicationBarMenuItemDelegate objects. Long names I know, I’ll work on shortening those up. As soon as the BindableApplicationBar’s DataContext is set each button delegate’s and menu item delegate’s DataContext is set as well. At this point bindings kick in and based on visibility the BindableApplicationBar adds the delegates’ ApplicationBarIconButton or ApplicationBarMenuItem objects to the correct collections on the actual ApplicationBar. Confused? Let’s show some code.

Hooking up the attached property looks like this:

<Code:BindableApplicationBar.BindableApplicationBar>
	<Code:BindableApplicationBar>
		<!--NOTE: The ability to add more than 4 buttons and change their state based on binding. 
				If they are visible then they are added to the app bar, otherwise they are removed from the app bar.
			IMPORTANT: Always change the visibilty of the buttons in the app bar before those not in the app bar
				and avoid having 2 buttons bound to the same property for visibility-->
		<Code:BindableApplicationBar.ButtonDelegates>
			<Code:ApplicationBarIconButtonDelegate IconUri="/Images/appbar_button1.png"
													Text="{Binding TextCommandHeader}"
													Command="{Binding ChangeTextCommand}" />
			<Code:ApplicationBarIconButtonDelegate Text="Increase"
													Command="{Binding ChangeNumberCommand}" />
			<Code:ApplicationBarIconButtonDelegate Text="Toggle"
													IsVisible="{Binding CanToggleCanChangeNumber}"
													Command="{Binding ToggleCanChangeNumberCommand}" />
			<Code:ApplicationBarIconButtonDelegate Text="Hide"
													IsVisible="{Binding CanHide}"
													Command="{Binding HideToggleCommand}" />
			<Code:ApplicationBarIconButtonDelegate Text="Show"
													IsVisible="{Binding CanShow}"
													Command="{Binding ShowToggleCommand}" />
		</Code:BindableApplicationBar.ButtonDelegates>
		<Code:BindableApplicationBar.MenuItemDelegates>
			<Code:ApplicationBarMenuItemDelegate Text="Random Number Text"
													Command="{Binding ChangeTextMenuCommand}" />
		</Code:BindableApplicationBar.MenuItemDelegates>
	</Code:BindableApplicationBar>
</Code:BindableApplicationBar.BindableApplicationBar>

Once the DataContext of the BindableApplicationBar and all delegate objects are set this code gets run:

private void Initialize(object dataContext)
{
	if (DataContext == dataContext)
	{
		return;
	}

	DataContext = dataContext;

	foreach (var buttonDelegate in ButtonDelegates)
	{
		buttonDelegate.DataContext = DataContext;
		if (buttonDelegate.IsVisible)
		{
			AddAppBarButton(buttonDelegate.Button);
		}
		buttonDelegate.IsVisibleChanged += AppBarButtonIsVisibleChangedHandler;
	}

	foreach (var menuItemDelegate in MenuItemDelegates)
	{
		menuItemDelegate.DataContext = DataContext;
		if (menuItemDelegate.IsVisible)
		{
			AddAppBarMenuItem(menuItemDelegate.MenuItem);
		}
		menuItemDelegate.IsVisibleChanged += AppBarMenuItemIsVisibleChangedHandler;
	}

	UpdateAppBarVisibility();
}

Something that I CANNOT find anywhere, which then became my motivation for writing my own BindableApplicationBar was the ability to have Visibility of the buttons and menu items bound as well as IsEnabled. I added an IsVisible property that drives whether or not the button or menu item is added to the ApplicationBar. Super simple. Also, the delegate objects subscribe to their object’s click event. So one could using Commands and binding, click handlers, or both just like on a normal button.

ApplicationBar.Opacity Fact

Here is an interesting fact for you. The property ApplicationBar.Opacity is 1.0D by default. What I recently learned is that the ApplicationBar.Height is 72px and if Opacity is 1 then the ApplicationBar actually takes up space in the layout of the page. However, as soon as the Opacity is .999D or not equal to 1 then the ApplicationBar no longer participates in the layout of the page. This was a pretty big bummer to the project I’m on since we need to manage our ApplicationBar quite a bit. Wait animations were starting in one spot then moving down after the ApplicationBar would hide. It was very confusing why this was happening until I read about this behavior in the documentation.

So what does all that have to do with the BindableApplicationBar here? Opacity by default is now .999D in order to have a consistent feel across the app. Why .999 not just .9? If .9 is used the ApplicationBar is noticeably transparent, however .999 looks completely Opaque.

Known Issues

The biggest issue I’m aware of right now is the possibility to try and add more than 4 ApplicationBarIconButtons to the ApplicationBar. If the developer adds 5+ button delegates they need to be very careful about managing the visibility of the buttons. Only 4 buttons can be added at a time. The 5th will throw an exception. I’m working on a solution for this, but for now the current version works well enough.

Another nice to have would be the ability to specify permanent order. Currently if 4 buttons are visible and the first is removed (visibility change) and later is readded it will now be the last button. The solution I’m toying with for the above issue would also allow me to fix this one fairly easy as well.

The last major known issue is that DataContext must be set. This means that if the page does not have a DataContext then the the BindableApplicationBar.DataContext property should be set to a string such as BindableApplicationBar.DataContext=”a”. This will cause the logic in Iinitialize(object dataContext) to run (found above).

Sample Code

Finally, the stuff everyone really cares about. Feel free to do whatever you want with the code. I’d love to hear if you’re using it and any feedback you have about the code. Hopefully this is helpful.

BindableApplicationBar Sample Code

Bindable Application Bar (Blend Behavior) – Windows Phone 7

UPDATE 2/22/2012: After fighting with this for over a week and wasting more hours than I thought possible I made a discovery about Blend Behaviors. It seems that Blend Behaviors have a static lifetime. They attach and detach to objects as they come and go, but the instance seems to be static. I discovered this when the value of my Dependency Property was being reused by all instances. Not to mention I made the mistake of newing up a collection as the default for my Dependency Property, thus creating an undesired singleton. After all has been said and done. I switched over to an Attached Property approach. This approach has worked like a charm. You can read about and get the sample code for the new approach here.

Many have claimed to have created a bindable ApplicationBar for the lacking Windows Phone 7 OS. After hours of searching for one that would fit my needs I discovered that they are all lacking or not implemented in a way that was sufficient for my needs. I set out to create my own implementation with the help of Tim Askins and Travis Schilling. What we came up with was not an ApplicationBar replacement or derivative, but a new behavior that can be added to any page in XAML. Note this is NOT an attached behavior. We are inheriting from Behavior<T>.

Why a behavior?

Behaviors have no UI. If we were to use any kind of Control derivative the created control would allow placement anywhere in the UI tree. It would need to do extra work to traverse the tree and find the page as well. This also means that it could potentially be part of a UI tree that was invalid. This concept did not make sense to us. By using a Behavior we force the developer to place the behavior as a first class citizen of any page, and it is restricted to PhoneApplicationPage, but would work with derived pages as well. The behavior is also not part of the UI tree.

Any problems with using a behavior?

The behavior is designed with a collection of ApplicationBarIconButtonDelegate objects. These objects expose many of the same dependency properties which a normal button would expose such as IsEnabled, Command and CommandParameter. They also expose an IsVisible and an IconUri property. However, when these objects are being bound to in XAML they live inside of the behavior. This seem like a no brainer, one just needs to remember that means there is no DataContext to pass around. This was easily overcome by setting the page’s DataContext as the DataContext of each button in the override of the OnAttach method. Once this was done each button delegate was able to bind as expected. In the current implementation we do not handle the case where a page does not have a DataContext, however this would be easily overcome by adding an optional DataContext DependencyProperty to the behavior.

Where’s the code?

After testing the code a bit more I will post up what we have created. Assuming all goes well that will be next Wednesday or sooner.