SelectionChanged fired also on nested controls? - c#

Sorry for misleading title, I'll try to explain better.
I've a TabControl like this:
<dragablz:TabablzControl SelectionChanged="MainTabs_SelectionChanged" x:Name="MainTabs">
where inside I've different TabItems, I need to fire the event MainTabs_SelectionChanged each time the user change the TabItem, this working but the event is fired also when the selection of a combobox, available inside the tabitem, change.
This is the ComboBox:
<ComboBox Grid.Column="1" Grid.Row="1" ItemsSource="{Binding Groups}"
Margin="8,0,8,16" DisplayMemberPath="Name" SelectedItem="{Binding SelectedGroup}" />
why happen this?

why happen this?
Because SelectionChanged is a routed event.
Routed Events Overview: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/routed-events-overview
You could use the OriginalSource property to determine whether a tab was selected:
private void MainTabs_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.OriginalSource == MainTabs)
{
//do your thing
}
}

Related

Prevent SelectionChanged event during DragDrop WPF

I'm working on a WPF application with MVVM pattern using Telerik controls.
Functionality:
I'm using telerik:RadListBox for which a collection is bind at runtime. I can ReOrder the items in the RadListBox.
Issue:
When i DragDrop items within RadListBox after DragLeave event the SelectionChanged event gets fired.
XAML:
<telerik:RadListBox x:Name="lstMarketSeries" ItemsSource="{Binding MarketSeriesCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True, ValidatesOnDataErrors=True}" ItemContainerStyle="{StaticResource DraggableListBoxItem}" DragLeave="lstMarketSeries_DragLeave" SelectionMode="Extended" telerik:StyleManager.Theme="Windows8" SelectionChanged="MarketSeriesCommit_SelectionChanged">
</telerik:RadListBox>
XAML.cs:
private void MarketSeriesCommit_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
private void lstMarketSeries_DragLeave(object sender, DragEventArgs e)
{
}
Is there any way that i can restrict the SelectionChanged event getting fired after DragLeave event?
I don't think that this is a good idea to prevent the selection event hadler to fire. But you can try to add the flag IsInDrag(boolean) and manage this during the darag/drop action(true on start dragging and false when you enter the final selection changed handler), in addition when you entering the SelectionChanged event handler and the flag is true you set this false and leave the handler(in addition you can set the handle property of the SelectionChangedEventArgs to true) that's all.
Regards.

How to determine which child element of a ListView Item was clicked?

I'm developing a windows phone 8.1 app in XAML and C#. I have a ListView getting its Items from a bound list and displaying them through a DataTemplate. Now, in this DataTemplate there are multiple child elements, and when the user taps on an item in the list, I want to be able to determine what child element he actually touched. Depending on that, the app should either expand a view with more details inside the Item, or navigate to another page.
The ItemClick event handler of the ListView is ListView_ItemClick(object sender, ItemClickEventArgs e), and I thought e.OriginalSource would maybe give me the answer, but this just gave me the clicked ListItem.
I have yet to try if encapsulating the children with buttons and intercepting their click events would work, but I'm happy to try any alternative there might be for this.
I just found the solution myself. I set the ListView to SelectionMode="None" and IsItemClickEnabled="False", and then I added Tapped handlers for the individual child elements. Works just as I wanted.
I've got a TextBlock and an Image in one ListViewItem and have just used the Image_PointerPressed event. Doing that also fires the ItemClick event for the ListView so I disable it first, do the stuff I want, then re-enable the ItemClick event so that still fires when the TextBlock is pressed.
Code behind:
private async void imgDone_PointerPressed(object sender, PointerRoutedEventArgs e)
{
// disable click event so it won't fire as well
lvwCouncils.IsItemClickEnabled = false;
// do stuff
MessageDialog m = new MessageDialog("User Details");
await m.ShowAsync();
// Re-enable the click event
lvwCouncils.IsItemClickEnabled = true;
}
Xaml:
<ListView x:Name="lvwCouncils" ItemClick="lvwCouncils_ItemClicked" IsItemClickEnabled="true" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock
Grid.Column="1"
Text="{Binding council_name}"
FontSize="24"
Margin="10,10,30,10"
/>
<Border Height="20" Width="20" Margin="10,10,0,10" >
<Image x:Name="imgDone"
Source="Assets/user_earth.png" Stretch="UniformToFill" PointerPressed="imgDone_PointerPressed"/>
</Border>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Use the SelectionChanged event.
Cast the sender object to ListView type and then retrieve the item from the SelectedItem property.
Similar question here but for a different control :
Get the index of the selected item in longlistselector

How to have a ListBox child item event not call parent event?

Problem
When I click on a ListView item, it calls the "Tapped" event to navigate to another page. I have an Up Vote event within the ItemTemplate and when they call that specific event, I DO NOT want to call the ListView's tapped event. Any idea how I might do that?
ListView XAML:
Parent event, "listboxFeedbackItem_Tapped", occurs anytime any part of the listview is clicked
<Grid x:Name="gridMainData" Grid.Row="2">
<ProgressBar x:Name="prgBar" IsIndeterminate="True" VerticalAlignment="Top" Visibility="{Binding Path=FeedbackVM.IsLoading, Mode=TwoWay}"/>
<ListView ItemTemplate="{StaticResource FeedbackTemplate}" ItemsSource="{Binding FeedbackVM.FeedbackCollection}" Tapped="listboxFeedbackItem_Tapped"/>
</Grid>
ItemTemplate Xaml:
Event "UpVoteItem_Tap" should not trigger "listboxFeedbackItem_Tapped"
<DataTemplate x:Key="FeedbackTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="0,0,30,0" Text="{Binding UpVotes}" Tapped="UpVoteItem_Tap"/>
</StackPanel>
</DataTemplate>
Perhaps there's a method in C# to prevent subsequent events from occurring?
Thanks, I'm still trying to wrap my head around XAML.
When you receive the UpVote tapped event, you can tell it not to pass the event to the parent listview by setting e.Handled=true:
void UpVoteItem_Tap(object sender, TappedRoutedEventArgs e)
{
// Processing here
...
// don't send event to parent
e.Handled = true;
}

Bubbling events in WPF? Simple Question

This is probably a really easy fix, but I am inexperienced in working with events so I am just going to ask it.
I have a ListBox with ListBoxItems in it. These ListBoxItems will be binded to a data source so they will change. I need a MouseDown event to be raised when a MouseDown action is performed on any of these ListBoxItem (because I am doing drag and drop). Since the values are changing, I cannot expect to wire the events together in the XAML like the following
<ListBox Name="ListBox1">
<ListBoxItem MouseDown="MouseDownEventName">Item A</ListBoxItem>
<ListBoxItem MouseDown="MouseDownEventName">Item B</ListBoxItem>
<ListBoxItem MouseDown="MouseDownEventName">Item C</ListBoxItem>
</ListBox>
This would be easy if I had static values, but since the values in the ListBox will change, I would prefer to write the following XAML
<ListBox Name="ListBox1" MouseDown="MouseDownEventName">
//Binded Values
</ListBox>
Then, when the ListBoxItem is selected, it would Bubble the event up to this MouseDownEventName, and I can grab ListBox1.SelectedItem at that time, the problem is, I am trying this right now, but it is not working. I have the following Code to handle the MouseDown, which is only rewriting label content at the moment to signify that the item has been MouseDown'ed.
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void ListBox_MouseDown(object sender, RoutedEventArgs e)
{
ListBox box = (ListBox)sender;
if (box != null && box.SelectedItem != null)
{
DragDrop.DoDragDrop(box, ItemListBox.SelectedItem, DragDropEffects.Move);
label1.Content = "MouseDown Event Fired";
}
}
}
Using XAML, you can provide a template for different types contained within the form. For example, in this case, you can specify that ListBoxItem's fire off a certain event handler. This is the bulk of the XAML markup code (details found here: How to catch Click on ListboxItem when the item is templated?)
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListBoxItem_PreviewMouseLeftButtonDown"/>
</Style>
</ListBox.ItemContainerStyle>
...
</ListBox>
Another thing to check before is to try and change your DragDrop.DoDragDrop() method call to something else to see if the issue has to do with that method. Since the Label's content is changing, I would imagine it has something to do with that method.

In Silverlight, how to bind ListBox item selection to a Navigate event?

I am writing a windows-phone 7 application. I've got a page with a list of TextBlock(s) contained in a ListBox. The behavior I want is that upon clicking one of those TextBlock(s) the page is redirected to a different one, passing the Text of that TextBlock as an argument.
This is the xaml code: (here I am binding to a collection of strings, and the event MouseLeftButtonDown is attached to each TextBlock).
<ListBox x:Name="List1" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock MouseLeftButtonDown="List1_MouseLeftButtonDown" Text="{Binding}"
FontSize="20"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But this has been unsuccessful for me. I have tried attaching MouseLeftButtonDown event to either the individual TextBox(es) or to the ListBox. And I have had exceptions raised as soon as I use NavigationService.Navigate(uri). Which event should be attached? Should the event be attached to the individual items or to the list as a whole?
I have found a way to work around this problem by populating ListBox with HyperlinkButton(s). However, I would like to understand why the TextBox approach did not work.
This is my first attempt with Silverlight, so I might be missing something basic here.
There are a few ways to do this but I'll walk you through one of the the simplest (but not the purest from an architectural perspective).
Basically you want to find out when the selection of the ListBox changes. The ListBox raises a SelectionChanged event which can be listened to in the code behind.
<ListBox x:Name="List1" ItemsSource="{Binding}" SelectionChanged="SelectionChangedHandler" SelectionMode="Single" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontSize="20"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Then have a handler something like:
private void SelectionChangedHandler(object sender, SelectionChangedEventArgs e)
{
IList selectedItems = e.AddedItems;
string val = selectedItems.OfType<string>().FirstOrDefault();
NavigationService.Navigate(new Uri(val));
}
One thing you'll need to be aware of is that ListBoxes support multiple selection. For this reason, the event arguments give you back a list of the selected items. For simplicity, all I've done is taken the first value from this list and used that as the navigation value. Notice how I've also set the SlectionMode property of the ListBox to Single which will ensure the user can only select one item.
If I were doing this for real I'd look into creating an TriggerAction tat can be hooked up to an event trigger through xaml which will remove the for code behinds. Take a look at this link if you're interesetd.
In addition to Chris' and James' replies, I'd add that you will also need to clear the listbox selection in the event handler, otherwise the user won't be able to tap the same item twice on the listbox (because the item will already be selected).
Using James' approach, I would change the SelectionChangedHandler() implementation as follows:
private void SelectionChangedHandler(object sender, SelectionChangedEventArgs e)
{
// Avoid entering an infinite loop
if (e.AddedItems.Count == 0)
{
return;
}
IList selectedItems = e.AddedItems;
string val = selectedItems.OfType<string>().FirstOrDefault();
NavigationService.Navigate(new Uri(val));
// Clear the listbox selection
((ListBox)sender).SelectedItem = null;
}
What I would recommend is binding the SelectedItem property of the ListBox to a property in your ViewModel. Then, on the ListBox's SelectedItemChanged event, navigate to to the appropriate URL passing the data key on the QueryString, or upgrade to something like MVVM Light and put the actual SelectedItem object on the message bus for the child window to pick up. I have a sample of this second method on my Skydrive that you can check out.
HTH!
Chris

Categories

Resources