tobi.at
tobi.at

Reputation: 1286

Custom context menu XAML for WP8

I try to implement a custom ContextMenu in a LongListSelector.

I'm not using the ContextMenu from Microsoft.Phone.Controls.Toolkit, it's basically the same as in the Rowi App:


(source: hiddenpineapple.com)

Approach 1

My list item toggles a VisualState on hold and an overlay is shown with controls in it.

The problem

I can't find a way to go back to the default state when the user clicks outside of the list item (as in the default ContextMenu).

Approach 2

I've implemented a custom template for the toolkit ContextMenu which looks exactly the same. I had to move its margin top to -itemHeight, as by default it is below the item.

The problem

The problem with this solution is, that it automatically closes itself when opening and I couldn't figure out how to avoid this.
Another problem was that it didn't work well with TiltEffect.IsTiltEnabled from the Toolkit (visual problems).

I need your help

Any suggestions on how to get this working?


Answer

Thanks to Cheese, now I know how to properly close the menu when the user clicks outside. His suggestion was to get the coordinates of a Tap event on the current page, and check if it's inside the menu. When not, close the menu.

So I added a Tap listener to the page when the menu opens, and removed it when the menu closes. From the page listener I got the event coordinates and could check if it's inside the control which holds the menu (same size and position). I received the position of the control with Point leftUpperPoint = control.TransformToVisual(page).Transform(new Point(0, 0)) and the rightLowerPoint by adding the ActualWidth and ActualHeight.

But then I realized:

Why should I even calculate if the tap is inside the menu? I always want to close the menu when the user taps anywhere on the screen. If it's outside, yes. If it's on a menu button, yes.

Another modification I made was to listen for MouseLeftButtonDown instead of Tap as it also triggers when the user swipes.

So I removed this code and came up with the following:

private void ToggleMenu(object sender, System.Windows.Input.GestureEventArgs e)
{
  PhoneApplicationFrame frame = ((PhoneApplicationFrame)Application.Current.RootVisual);
  VisualState state = this.States.CurrentState;

  if (state == null || state.Name == "DefaultState")
  {
    frame.MouseLeftButtonDown += MouseDownDelegate;
    this.State = "MenuState";
  }
  else
  {
    frame.MouseLeftButtonDown -= MouseDownDelegate;
    this.State = "DefaultState";
  }
}

private void MouseDownDelegate(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
  ToggleMenu(sender, null);
}

This works perfectly!

Thanks to Cheese for the hint.

Upvotes: 0

Views: 774

Answers (3)

Developer
Developer

Reputation: 4321

Approach 1 problem

The best solution would be:

Get the menus coordinates, when user makes a tap - you check are tap coordinates on menu or not, if not - dissmiss - simple.

Approach 2 problem

I guess you had some button in a corner and when you tapped on it - nothing happened? And when you dissmissed the Tilt all worked. It seems that tilt works faster than a click, so, tilt changes the button coordinates, and device thiks you have missed/or dragged off

Upvotes: 1

atomaras
atomaras

Reputation: 2568

You can use what @ScottIsAFool suggested and maybe create another Dependency Property on your TapMenu control of type UIElement named CloseWhenTappedElement and automatically listen for Tap events inside your control once set. For example

<Grid x:Name="TapArea"/>
<TapMenu CloseWhenTappedElement="{Binding ElementName=TapArea"}/>

Upvotes: 0

ScottIsAFool
ScottIsAFool

Reputation: 163

Something like this by @denniscode http://dotnet.dzone.com/articles/rowi-show-tap-menu

Upvotes: 2

Related Questions