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.
Hi,
It seems like you used a similar approach to the version I had written. You can get it from CodePlex (http://bindableapplicationb.codeplex.com/) or NuGet (search for appbar) or read about my process on my blog (http://blog.xyzzer.me/tag/applicationbar/).
Bindable application bars are becoming really popular these days. 🙂
I also decided against putting a built-in limit to the number of buttons or menu items displayed – I thought it was more discoverable to leave the exceptions thrown than do a hidden limit that would leave users wondering why their buttons don’t show up.
I do not have the IsVisible property for the buttons or menuitems yet – my current solution allows to bind to a ButtonsSource collection though, so a ViewModel can control visibility of a button by adding/removing it to/from the collection. I looked into implementation of the IsVisible property, but it turned out that with my current code – it would get pretty complicated and it would require a lot of time to get it to work.
Another version of the wrapper I saw was interesting in that it allowed to specify the buttons in PanoramaItems – making buttons show up or disappear depending on which was the currently active – I might add it to my wrapper if I get some time and/or feedback. See – http://www.windowsphonegeek.com/articles/Advanced-ApplicationBar-for-Windows-Phone .
I do have a solution for the DataContext issue if you want to check it – I simply bind the DataContext of my attached property value to the DataContext of the parent (page).
It’s an interesting trick – setting the Opacity=”0.999″ – I will have to remember that.
Filip,
Thanks for the comment. I checked out your app bar and the app bar from windows phone geek and a few others before I had written my own. Both are very well done. The two biggest motivations for writting my own were one the desire to not have any UI objects in my ViewModels. By including a button source in my VM I would be including UI objects in my VM. The second was I need the ability to change whether or not an app bar button is in the app bar (visible) for a single page, pivot item, etc. By using the IsVisible property I was able to achieve this behavior. So using windows phone geek’s app bar is still not sufficient. I feel like the app bar I’m working on is a hybrid solution of the many bindable app bars that exist today.
The DataContext issue I’m facing is not a problem of binding to the parent page, but rather a problem with the DataContext of the parent page is null. If it is null then the app bar never initializes. A bug I’m working on. I can’t initilize immediatly after attaching because the developer has the ability to have more than the allowed limit of buttons and without a DataContext in that situation the app bar would throw an exception of too many buttons because they would all be visible due to failed bindings. I’m thinking I might add a boolean dependency property that would need to be specified if the developer wanted to support a null DataContext. That has it’s down sides too though. Arguably if the developer is not using a DataContext then they could just use the built in app bar because bindings wouldn’t be used anyway.
Pingback: WP Simplified: Wireframes and Information Map | InterKnowlogy Blogs
Great work on this! I’ve been looking at different solutions and this has been the best I’ve found so far.
I tried porting this windows phone 8, and I’ve almost got it working, but I’m having problems updating the DataContext. Basically, if the DataContext of the page is set during the OnNavigatedTo method, than the app bar doesn’t pick it up.
Any thoughts on what might be going on?
So I think I found the problem, but I’m not sure how to fix it. I think something is wrong with this binding operation in the constructor,
BindingOperations.SetBinding(this, DataContextWatcherProperty, new Binding());
because the OnDataContextWatcherChanged method is no longer getting called, and so the datacontext isn’t being updated.
Any help on this would be appreciated.
Awesome! I’m glad you’ve found it helpful. What you’ve uncovered is certainly the hardest problem to overcome. Attached Properties don’t always agree with the DataContext of the page. I’m not entirely sure why you’re not getting notified, but with Windows Phone 8 based on WinRT and WP7 based on Silverlight there are some subtle differences. I think I’ll have some time tomorrow to look at this. Seeing as there isn’t a codeplex release of the source that you could push changes to could you email me your source for just the Bindable Application Bar? That’ll help me get it integrated into a WP8 project quicker. My email is dwarren at InterKnowlogy dot com.