Banana SplitButton [A WPF-specific fix for SplitButton and some code analysis improvements for the Silverlight version, too]


This blog has moved to a new location and comments have been disabled.

All old posts, new posts, and comments can be found on The blog of dlaa.me.

See you there!

Comments (20)
  1. Shemesh says:

    i have achieved these functionalities by using a ComboBox.

    i have edited its default template and added couple of properties.

    but the logic/events/data is already inside the ComboBox, so why duplicate?

    the only reason i think of (using the ContextMenu) is if in the future it will have sub-menus.

    otherwise… it is much simpler modifying the ComboBox.

  2. Delay says:

    Shemesh,

    I started SplitButton as a way to test my ContextMenu implementation on Silverlight, so using ComboBox wasn't something I really considered. 🙂 That said, having Button as the base class seems slightly preferable to me – as does using an actual Menu subclass for the menu. But it's cool to hear this can be done simply with ComboBox and if that's working well for you, it's great to hear! Thanks for sharing!

  3. Shemesh says:

    hmmmm, got it.

    i was looking at it from the functionality point of view.

    you were coming from a different one.

    my work was done from up downwards, yours was done from down upwards.

    🙂

  4. KierenH says:

    Vote for Menu & MenuButton to be added to the toolkit

  5. Delay says:

    KierenH,

    Noted, thanks! 🙂

  6. Anders says:

    I created another version of this control called SelectButton http://gist.github.com/580405

  7. jpierson says:

    I seem to be having an issue with this control where the menu shows up when the screen loads. I'm assuming this has to do with line of code in ApplyTemplate where _contextMenu.IsOpen is set to true and then false a short time later while the logic child is hooked up. If this is the case, I think a more reasonable workaround is needed that doesn't flash the context menu initially. If I find something in my experimentation I'll share it.

    Also, have you considered placing these controls on CodePlex? It would be nice to have a single place to check for the current implementation version with latest bug fixes and all. Also it would be easier to contribute that way.

  8. Delay says:

    jpierson,

    The comment for the code you're referring to is: "// Add the ContextMenu as a logical child (for DataContext and RoutedCommands)". It's WPF-only and if that scenario's not relevant to you, you might consider removing that block entirely. As you say, though, that's not a general-purpose solution – at the time I was porting SplitButton to WPF, what I implemented there worked well in the scenarios I tried – but if you find a better solution, I'd love to hear it!

    Regarding getting the latest version of stuff I post to my blog, I almost always point to a single location for my ZIP downloads and update that ZIP as I make improvements over time. So clicking the download link should always get you the latest bits. 🙂 I've considered moving to something like CodePlex, but haven't made the plunge yet (there are actually rules for using CodePlex as a Microsoft employee that might prevent me from using it for the purpose you suggest). If I do switch to something like CodePlex or GitHub, I'll be sure to post about it.

    Thanks for your note – and good luck finding a better solution!

  9. jpierson says:

    Delay,

    Thanks for your input. In my case I'm actually using your control in a WPF application not Silverlight and that's why I noticed this issue, I should have mentioned that at first. Unfortunately I haven't found an alternative way to get the ContextMenu's Popup to be set as a Logical Child of the MenuButton. I've tried setting the Opacity  to 0 and also the Height and Width to 0 to try to get the ContextMenu to not show during the open/close logic in ApplyTemplate but that doesn't seem to have any effect. I have found by looking at the ContextMenu's code through Reflector that there is a method called HookupParentPopup() that seems to be exactly what is needed but unfortunately it's private…. Right now I'm wondering how this works for regular contextmenus on controls set through the ContextMenu attached property.

  10. jpierson says:

    Delay,

    I've confirmed that calling the private HookupParentPopup of the ContextMenu through reflection before attempting to do a parent lookup through the logical tree that everything seems to workout great. Even though it's not recommended to call private methods through reflection, at this point this seems to be the only workaround so I'm going to implement it for my case.

    var hookupParentPopupMethod = _contextMenu.GetType().GetMethod(

       "HookupParentPopup",

       System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

    hookupParentPopupMethod.Invoke(_contextMenu, null);

    While looking over the internal code for ContextMenu one thing that I did also learn is that this method gets called when the IsOpen is set to true meaning that there is no reason to wait until after the logical tree lookup to set IsOpen to false. This means that one could set the IsOpen to true and then set it false directly after that in order to minimize how long the ContextMenu will flash on the screen. I still opt for the reflection call to HookupParentPopup myself until something better comes along.

  11. Delay says:

    jpierson,

    Great stuff – thank you very much for that detailed write-up! I don't have specific plans to update SplitButton right now, but I've added a note to my TODO list referencing the issue you report and your proposed fix. For what it's worth, I'm also not a big fan of private reflection – but maybe when I (or you) revisit this at some future date, an alternate solution will present itself… 🙂

  12. jpierson says:

    I've just run into another issue this time with RelativeSource binding defined on properties of the MenuItems used in my sample application.

    In my case I need to access a property on one of the parent controls such as a Window or UserControl. To bind to the property I'm using a binding that looks like the example below.

    <MenuItem Background="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}, Path=Background}" />

    In my real project I'm binding something a little more specific than just the Background of the Window but the point is still the same. Also I'm not sure whether this is a problem with SplitButton implementation yet or just something that would occur with all bindings specified to items within a ContextMenu. If I find any more details I'll follow up here to help others that may need to do something similar.

  13. Delay says:

    jpierson,

    If I had to guess at why RelativeSource wouldn't work here, my first thought would be that it might be due to the ContextMenu-imposed Popup sitting between the MenuItems and the rest of the page. If so, one possible workaround might be to use something like "{Binding ElementName=MyWindowName, Path=Background}" instead – though that might suffer from the same root problem…

    Big thanks for the detailed information and for sharing your findings!

  14. Helge Klein says:

    I just implemented the buttons in my project. Works like a treat! Thank you very much for publishing this.

  15. Delay says:

    Helge Klein,

    Glad it worked for you – thanks for the message! 🙂

  16. Aleks says:

    Hi David,

    Don't understand why you are adding the contextmenu as a logical child.

    I just put it in the button context Menu and it works : this.ContextMenu = _contextMenu;

  17. Aleks says:

    Humm in fact it didn't work.

    I'm trying to understand because the context menu appear in the window while the code is parsing the logical tree wich is not really nice.

  18. Delay says:

    Aleks,

    I explain why the ContextMenu is set as a logical child in the second paragraph of the post above. As you've also discovered, certain things (ex: DataContext, RoutedCommands) don't work otherwise. There may be another way to get this all working, but that what I found worked at the time. 🙂

  19. Femke says:

    Hi Kainhart,

    In addition to my previous question, I found a workaround for context menu showing when loading the SplitButton, with this workaround the Command binding will still work when using the SplitButton on a DataTemplate:

    On the OnApplyTemplate hide the contextmenu:

    _contextMenu.Visibility = System.Windows.Visibility.Hidden;

    On OpenButtonMenu make the contextmenu visible again:

    _contextMenu.Visibility = System.Windows.Visibility.Visible;

  20. Greg says:

    Unfortunately opening a popup and adding it as logical child has huge impact on performance. i tried putting SplitButton as a grid cell and it gave up pretty quickly. Somehow all the things you attempted to solve (RoutedCommands, DataContext) work in ComboBox which also does use Popup (ContextMenu does use popup).

Comments are closed.