Charteris Community Server

Welcome to the Charteris plc Community
Welcome to Charteris Community Server Sign in | Join | Help
in Search

Patrick Long's Blog

All things regarding Microsoft's UI Technologies (no cats)

Binding ContextMenu to its logical Parent

Richard contacted me today because he was having trouble binding a ListBox’s ContextMenu to data belonging to the SelectedItem. My initial thought was binding statement like this

<ListBox.ContextMenu>

  <ContextMenu>

    <MenuItem Header="{Binding Path=SelectedIndex,

                    RelativeSource={RelativeSource FindAncestor,

                    AncestorType={x:Type ListBox}}}" />

  </ContextMenu>

</ListBox.ContextMenu>

RelativeSource tells the binding looking for the source of the binding and the way it does this is dependent on the mode. The four, supported modes are

  • FindAncestor – This will walk up the VisualTree looking for a item of a particular type
  • PreviousData - Allows you to bind the previous data item in the list of data items being displayed. 
  • Self – This refers to the item that you are currently setting the Binding on. In the case of our Context menu it is the MenuItem
  • TemplatedParent – This refers to the item that is currently being templated, by a template, in which this binding statement exists.

The trouble with my use of FindAncestor was the way that ContextMenus are created. They are not part of the same visual tree as the ListBox and therefore any attempts to walk up from the MenuItem to the ListBox fail. Something worth noting is that these kind of binding failures are silent and the only obvious sign is that you bindings do not result in any data. No explanation seems available? not true. Many binding failures are reported a warnings to the debug window. So for instance if you are building your app using VS.NET keep an eye on the Output window for Binding error.

What I needed to do was look a little closer to home. The direct parent of the MenuItem, the ContextMenu. Once I get hold of the ContextMenu in my binding statement I can bind to the PlacementTarget of the ContextMenu which is of course the ListBox.

<ListBox Margin="5" Name="MyListBox" HorizontalAlignment="Left">

  <ListBox.ContextMenu>

    <ContextMenu>

      <MenuItem

        Header="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu},

              Path=PlacementTarget.SelectedIndex}"/>

    </ContextMenu>

  </ListBox.ContextMenu>

 

  <ListBoxItem>

    <TextBlock Text="Item1"/>

  </ListBoxItem>

  <ListBoxItem>

    <TextBlock Text="Item2"/>

  </ListBoxItem>

  <ListBoxItem>

    <TextBlock Text="Item3"/>

  </ListBoxItem>

  <ListBoxItem>

    <TextBlock Text="Item4"/>

  </ListBoxItem>

</ListBox>

Published Feb 01 2007, 12:25 AM by Anonymous
Filed under:

Comments

 

Anthony said:

Hi Patrick

Your example here has helped me to get close to something I'm trying to achieve with a ListBox bound to an ObjectDataSource. The ListBox is multi-select and so SelectedIndex isn't the property I'm after. I'm trying to determine on which ListBoxItem the user displayed the context menu (after they right clicked). LeftClick is reserved for selecting and deselecting items. RightClick will open the Context Menu  - but once they've chosen the command from the ConextMenu - how can I determine which item in the Listbox they opened the ContextMenu over?

Thoughts?

March 31, 2007 10:48 AM
 

Anonymous said:

I've had a look and whether or not my "thoughts" are any help depends on what you want. I have not come up with a XAML only solution and the one I have come up with is not that pretty.

  1. Trap the MouseUp event on the Listbox
  2. Check that e.ChangedButton == MouseButton.Right
  3. Inspect the Mouse.DirectlyOver property

That will get you the Element that the mouse is over which is the case of my Listbox was the TextBlock. I did try the ContextMenu Opened and Opening events but they were no help.

HTH

March 31, 2007 4:36 PM
 

Anthony said:

Thanks Patrick. That would work too. In the end I simply moved the context menu over to the Image control instead of the ListBox - since I'm displaying a list of images inside a WrapPanel - it's the images and their source that matters to me here. I then set the CommandTarget to "{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget}" which gave me quick access to the Image control (and it's source). :-)

Out of curiosity - do you have any idea why WPF performance is so poor? If you use a BitMapEffect in a ListBox - rendering/painting time is very slow. I've read that this is because BitMapEffect (and others?) are currently being rendered in software? Thoughts?

Thanks for the original posting.

Tony

April 2, 2007 12:01 AM
 

Anonymous said:

"WPF performance is so poor". Are you finding that performance on the whole is poor or just when using BitmapEffects. I am not suprised you find it poor with BitmapEffects. Are you repeating them in a ListBox for every item or "just" around the Listbox itself? Either way i would suggest you consider not using them.

I recommend

  • Using them sparingly
  • Use them on small areas such as a button
  • Do NOT use them on areas that change often
  • Do NOT use them on elements you are animating

Currently any object that has a BitmapEffect applied will be rendered in software. One of WPF's key benefits is making use of today's overpowered underused graphics cards so causing stuff to drop back to software rendering is not good.

April 2, 2007 6:37 AM
 

Anthony said:

Yes - in fact it is just the BitMapEffects. The starting point for the app I have been working on was the SDK sample 'WPF Photo Viewer Demo' - which has BitMapEffects on the Image control in the list, as well as several GroupBox controls which are used across the entire surface of the main Window (blurred rounded corners). I removed all the BitmapEffects and the UI of the app is quick and responsive again. I found two good links related to performance at http://blogs.msdn.com/wpfsdk/archive/2007/01/15/maximizing-wpf-3d-performance-on-tier-2-hardware.aspx and http://blogs.msdn.com/wpfsdk/archive/2007/01/15/maximizing-wpf-3d-performance-on-tier-2-hardware.aspx. Apologies for wandering a little off topic on this thread - however I can't help but wonder why the BitMapEffects are rendered this way....
April 2, 2007 8:11 AM
 

Anonymous said:

"why the BitMapEffects are rendered this way..."

See the BitmapEffects docs http://msdn2.microsoft.com/en-us/library/ms743435.aspx

I don't like highlighting stuff without the context of the rest of the document so do go read it but this is the key text.

"As a special case, in WPF, effects can be set as properties on live Visual objects, such as a Button or TextBox. The pixel processing is applied and rendered at run-time. In this case, at the time of rendering, a Visual is automatically converted to its BitmapSource equivalent and is fed as input to the BitmapEffect. The output replaces the Visual object's default rendering behavior. This is why BitmapEffect objects force visuals to render in software only i.e. no hardware acceleration on visuals when effects are applied."

April 2, 2007 8:20 AM
 

Preet said:

Thanks for the solution, I had trouble with exactly the same thing!
April 17, 2007 12:41 PM
 

Paul Reynolds Blog said:

RadGridView and RadContextMenu: Simplifying interaction in XAML

July 7, 2010 3:36 AM

Leave a Comment

(required) 
(optional)
(required) 
Submit
Powered by Community Server (Commercial Edition), by Telligent Systems