I have listbox in my WP7 that uses the below DataTemplete to display the list items
<DataTemplate x:Key="MetaDataTemplate">
<Grid Width="440" Margin="4,12,0,12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="64"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="download" Visibility="{Binding DownloadVisible}" Command="{Binding Download}"/>
<toolkit:MenuItem Header="get link" Command="{Binding GetLink}"/>
<toolkit:MenuItem Header="delete" Command="{Binding Delete}"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<Image Height="64" Width="64" Source="{Binding Thumb}" Stretch="UniformToFill" />
<TextBlock Text="{Binding MetaData.Name, Mode=OneWay}" VerticalAlignment="Center" Margin="12,0,0,0"
Style="{StaticResource MetaDataHeaderStyle}" Grid.Column="1" />
</Grid>
</DataTemplate>
defining the context menu data template makes it quite impossible to close the menu on pressing the back button! Does anyone faced this problem? How did you solve it?
I searched for the solution on internet, but couldn't find one. Any help is appreciated.
A way to do it is to have a ContextMenu variable in the code behind, and have an event handler for the Opened event of the ContextMenu in the template.
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu Opened="ContextMenu_Opened">
<toolkit:MenuItem Header="stuff">
</toolkit:MenuItem>
<toolkit:MenuItem Header="more stuff">
</toolkit:MenuItem>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
In the Opened event handler, set the ContextMenu variable to that instance (i.e. sender).
private void ContextMenu_Opened(object sender, RoutedEventArgs e)
{
menu = sender as ContextMenu;
}
Finally, override OnBackKeyPress, so that if the variable is not null, and the menu is open, then close the menu and cancel the back event.
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
if (menu != null && menu.IsOpen)
{
menu.IsOpen = false;
e.Cancel = true;
}
base.OnBackKeyPress(e);
}
That should do it! Let me know if you have any issues, or need me to paste the full code.
Related
I Have an image within an ItemTemplate of ListBox, on the Tap event I execute an action.
on the SelectionChanged event of the ListBox I navigate to another Page when user does not tap on the image: my problem is the order of the these events: the selectionChanged event occurs before the tap event then the navigation occurs before the event Tap
how can I solve this problem? help please
In selectionChanged I test if it is not refresh (a boolean that I set to true when image Tap event trigged) I navigate to another page and if it is refresh I do not navigate
with a LongListSelector it works well because tap events is trigged first but not with ListBox (ReorderListBox exactly).
My datatemplate contains others controls:
<DataTemplate x:Key="ItemTemplate" >
<Grid Height="150" Width="408" Background="White" Margin="0,0,0,14">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<toolkit:ContextMenuService.ContextMenu >
<toolkit:ContextMenu Height="150" Background="#26AA99" BorderBrush="Transparent" Unloaded="ContextMenu_Unloaded" >
<toolkit:MenuItem Header="{Binding Path=LocalizedResources.PinToStart, Source={StaticResource LocalizedStrings}}" Foreground="#FFFFFF" FontWeight="Normal" FontSize="26" Click="MIPinSchedule_Click" />
<toolkit:MenuItem Header="{Binding Path=LocalizedResources.Delete, Source={StaticResource LocalizedStrings}}" Foreground="#FFFFFF" FontWeight="Normal" FontSize="26" Click="MIDeleteSchedule_Click" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<Border Grid.Row="0" Background="{Binding LineColor}" Height="14" Width="408"/>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center" Margin="12,0,0,0" Foreground="#00418D" FontWeight="SemiBold" FontSize="26"/>
<Grid >
<!--Les deux prochais passages-->
<phone:LongListSelector
ItemsSource="{Binding NextStopCollection, Mode=TwoWay}"
LayoutMode="List"
ItemTemplate="{StaticResource ItemTemplate2}" />
<!--Boutton refresh qui s'affiche au bout de 20 secondes-->
<Image VerticalAlignment="Center" Margin="0,0,12,0" HorizontalAlignment="Right" Height="48" Width="48"
Source="/Assets/Refresh.png" Tap="Refresh_Tap"
Visibility="{Binding ElementName=reorderListBox, Path=DataContext.IsOutOfDate,Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Grid>
</Grid>
</DataTemplate>
private void reorderListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var rlb = sender as ReorderListBox.ReorderListBox;
if (rlb.SelectedItem == null)
return;
if (!viewModel._isRefresh)
{
var selectedItem = rlb.SelectedItem as MyObject;
NavigationService.Navigate(new Uri(MyUri, UriKind.Relative));
}
_isRefresh = false;
rlb.SelectedItem = null;
}
private void Refresh_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
//Do action
_isRefresh = true;
}
I would suggest you discard the SelectionChanged event completely and instead add a Tap event on the Grid in your DataTemplate.
I've a LongListSelector with the following item template:
<DataTemplate x:Key="stopItemTemplate">
<Grid Margin="{StaticResource PhoneTouchTargetOverhang}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<CheckBox VerticalAlignment="Top" HorizontalAlignment="Left" IsChecked="{Binding Checked}" Click="AlarmActivationClicked" />
<StackPanel Grid.Column="1" VerticalAlignment="Top">
<TextBlock Text="{Binding Stop.Name}" Style="{StaticResource PhoneTextLargeStyle}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" TextWrapping="Wrap" Margin="12,-12,12,6"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Distanz:" Style="{StaticResource PhoneTextSmallStyle}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" Width="120"/>
<TextBlock Text="{Binding Distance, Converter={StaticResource MyStringFormatConverter}, ConverterParameter=:1000:\{0:0.0\} km}" Style="{StaticResource PhoneTextSmallStyle}" FontFamily="{StaticResource PhoneFontFamilySemiBold}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Alarm:" Style="{StaticResource PhoneTextSmallStyle}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" Width="120"/>
<TextBlock Text="{Binding Alarm, Converter={StaticResource MyBooleanStringConverter}}" Style="{StaticResource PhoneTextSmallStyle}" FontFamily="{StaticResource PhoneFontFamilySemiBold}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Vibration:" Style="{StaticResource PhoneTextSmallStyle}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" Width="120"/>
<TextBlock Text="{Binding Vibration, Converter={StaticResource MyBooleanStringConverter}}" Style="{StaticResource PhoneTextSmallStyle}" FontFamily="{StaticResource PhoneFontFamilySemiBold}"/>
</StackPanel>
</StackPanel>
</Grid>
</DataTemplate>
And used as followed:
<controls:PivotItem Header="ziele">
<toolkit:LongListSelector x:Name="alarmList" Background="Transparent" IsFlatList="True"
ItemTemplate="{StaticResource stopItemTemplate}" SelectionChanged="AlarmListSelectionChanged" />
</controls:PivotItem>
The SelectionChanged event:
private void AlarmListSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var guid = ((AlarmItem)alarmList.SelectedItem).Id;
NavigationService.Navigate(new Uri(string.Concat("/AlarmDetailPage.xaml?id=", guid), UriKind.Relative));
}
To change details of an item I've subscribed the SelectionChanged event of the LongListSelector. That works as expected.
But if I tap on the checkbox - at first the clicked event of the checkbox is fired, but I don't recognize which checkbox was clicked (the property longlistselector.SelectedItem is old) and just after the SelectionChanged event has fired and try to navigate to the detail page like is happens if I intend to do that.
How can I separate these to events to avoid firing both? And how can I get the corresponding data item?
Thanks a lot...
Kind regards, Danny
You don't need listen to SelectionChanged event. In Click, Checked or Unchecked event handler you can get an item:
private void CheckBox_Checked(object sender, System.Windows.RoutedEventArgs e)
{
var guid = ((sender as CheckBox).DataContext as AlarmItem).Id;
NavigationService.Navigate(new Uri(string.Concat("/AlarmDetailPage.xaml?id=", guid), UriKind.Relative));
}
In your AlarmActivationClicked (event handler on the checkbox) you'll be able to get the dataitem of the item in the list that was click.
private void AlarmActivationClicked(object sender, RoutedEventArgs e)
{
var dataItem = ((FrameworkElement)sender).DataContext;
}
I am not seeing the event AlarmListSelectionChanged method being called after the AlarmActivationClicked method is called. If you click on the checkbox, the checkbox changes to checked, but it doesnt change the selection in the list box.
I am using List to bind a listbox which is as follows:
<ListBox x:Name="ContentPanel" SelectionChanged="onSelectionChanged" Background="LightGray" Grid.Row="2">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Name="{Binding title}" Height="165" Margin="25,5,25,0" Width="430">
<Border BorderThickness="1" Height="165" BorderBrush="Gray">
<toolkit:ContextMenuService.ContextMenu >
<toolkit:ContextMenu IsZoomEnabled="False">
<toolkit:MenuItem Name="Delete" Header="Delete Message" Click="DeleteMessage_Click" >
</toolkit:MenuItem>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<StackPanel Orientation="Vertical">
<StackPanel>
<TextBlock Text="{Binding title}" Margin="5,0,0,0" FontSize="25" Foreground="Black"/>
<TextBlock Text="{Binding msgFrom}" Padding="5" TextWrapping="Wrap" Foreground="Gray" FontSize="20"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Margin="5,13,0,0" FontSize="24" Foreground="WhiteSmoke" Text="{Binding msgReceivedOn}"/>
<toolkit:ToggleSwitch Margin="170,10,0,0" IsChecked="{Binding msgStatus}" Unchecked="UnChecked" Background="LightBlue" Checked="Checked"/>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
At first data is successfully loaded. But when I use the Contextmenu to remove the item and reload the listbox.. it fires an exception. Code to handle the context menu click is:
private void DeleteMessage_Click(object sender, RoutedEventArgs e)
{
MenuItem item = sender as MenuItem;
Message message = (Message)item.DataContext;
MessageBoxResult result = MessageBox.Show("Are you sure to delete the message??", "Confirmation", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.Cancel)
return;
else
{
ContentPanel.Items.Remove(message);
lstMessage.Remove(message);
}
ContentPanel.ItemSource = lstMessage;
}
But it this code is not working. So any suggestions?
You don't need each time to bind collection to a list. Also, when you remove an item from your collection, it should disappear also in the list (if binding setup properly). I think you have not ObservableCollection, so you need manage items manually. Please, consider to use ObservableCollection.
Your code should looks like:
lstMessage.Remove(message); //it must raises CollectionChanged event automatically
And this lines is unnecessary:
ContentPanel.Items.Remove(message);
ContentPanel.ItemSource = lstMessage;
I have a context menu in my app and when I handle the MenuItem clicked, I want to navigate to another page. The issue I am having is that the navigation works but I don't see the other page load since the context menu remains open for the length of the click. When I hit back, the context menu closes and then I see the page I navigated to. What's the correct way to handle this? It's almost like I need to tell the ContextMenu to close when I handle the click and then navigate to the page I want.
updated with my handler code:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem menuItem = sender as MenuItem;
if (menuItem != null)
{
App.appData.URL = menuItem.Header.ToString();
NavigationService.Navigate(new Uri("/BrowserPage.xaml", UriKind.Relative));
}
}
XAML Code:
<local:MyListBox x:Name="messageListBox"
ItemsSource="{Binding ChannelMessages}"
MaxHeight="480"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu ItemsSource="{Binding URLs}">
<toolkit:ContextMenu.ItemTemplate>
<DataTemplate>
<toolkit:MenuItem Header="{Binding}" Click="MenuItem_Click"/>
</DataTemplate>
</toolkit:ContextMenu.ItemTemplate>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock x:Name="MessageTextbox" Text="{Binding MessageFrom}" TextWrapping="Wrap">
<TextBlock.Foreground>
<SolidColorBrush Color="{StaticResource PhoneAccentColor}"/>
</TextBlock.Foreground>
</TextBlock>
<TextBlock x:Name="FromTextBox" Text="{Binding MessageText}" Margin="0,0,0,19" Width="456" FontSize="21.333" TextWrapping="Wrap"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</local:MyListBox>
Any ideas?
Thanks
Does setting e.Handled = true within your click event handler help?
i binded some items to listbox.whenever click on particular listitem i want to display that subitems in another page.
Please tell me how to acheive this.
how to get the listitem id whenever click on particular list item ?
To clarify (in case you don't feel like creating a new DataBound application and just want the answer), instead of depending on the click event for a listbox item, you should be looking at the selectionchanged event for the list box itself.
eg:
<ListBox SelectionChanged="MainListBox_SelectionChanged">
// Handle selection changed on ListBox
private void MainListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// If selected index is -1 (no selection) do nothing
if (MainListBox.SelectedIndex == -1)
return;
// Navigate to the new page
NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" + MainListBox.SelectedIndex, UriKind.Relative));
// Reset selected index to -1 (no selection)
MainListBox.SelectedIndex = -1;
}
Look at the code created as part of a new DataBound application. It does just this.
MVVM solution
Use the following libraries:
xmlns:GalaSoft_MvvmLight_Command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WP71"
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit" <-- (optional, only for the ContextMenu)
XAML Example:
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="categoryListBox" Margin="0,0,-12,0" ItemsSource="{Binding CategoryItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="stackPanel" Margin="0,0,0,17" Width="432" Height="78">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<GalaSoft_MvvmLight_Command:EventToCommand
Command="{Binding Main.OpenCategoryCommand, Source={StaticResource Locator}, Mode=OneWay}"
CommandParameter="{Binding Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Remove" />
<toolkit:MenuItem Header="Show" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding Note}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
For a simple command binding MVVM style to an item in a list, you can also enclose the whole item template in a button and hook up the Command property of the button like this:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Button Command="{Binding Path=SomeCommandOnItemViewModel, Mode=OneWay}">
<Button.Template>
<ControlTemplate>
<!-- Your listitem styling goes here -->
</ControlTemplate>
</Button.Template>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>