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.