Using UWP, I have a ListView
where each item contains, among other things, a Button
. Tapping the button opens a MenuFlyout
. Tapping one of the flyout options triggers a Click
event.
In the event handler, how do I find the parent Button or, alternatively, the parent ListView
item ?
Here's an extract of my XAML. Specifically, I want to find the "Note" element while in the the OnItemEdit
handler.
<Page.Resources>
<ResourceDictionary>
<DataTemplate x:Key="NoteItemTemplate">
<Grid>
<Grid.ColumnDefinitions> ... </Grid.ColumnDefinitions>
<TextBox Name="Note" />
<Button>
<Image Source="..." />
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem Text="Edit" Click="OnItemEdit" />
<MenuFlyoutItem Text="Delete" Click="OnItemDelete" />
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid>
</DataTemplate>
...
<local:DetailItemSelector x:Key="DetailItemSelector"
NoteItemTemplate="{StaticResource NoteItemTemplate}"
...
/>
</ResourceDictionary>
</Page.Resources>
<ListView
x:Name = "DetailList"
ItemsSource = "{x:Bind DetailListItems}"
ItemTemplateSelector = "{StaticResource DetailItemSelector}">
</ListView>
The handler is defined as:
async void OnItemEdit(object sender, RoutedEventArgs e)
{
...
}
EDIT - work around
If there's no better way, here's a work around that is not too bad. The trick is to use FlyoutBase.AttachedFlyout
instead of Button.Flyout
.
<DataTemplate x:Key="NoteItemTemplate">
<Grid>
<Grid.ColumnDefinitions> ... </Grid.ColumnDefinitions>
<TextBox ... Name="Note" />
<Button ... Click=="onMoreClicked">
<Image Source="..." />
FlyoutBase.AttachedFlyout
<MenuFlyout>
<MenuFlyoutItem Text="Edit" Click="OnItemEdit" />
<MenuFlyoutItem ... />
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
</Button>
</Grid>
</DataTemplate>
And, code behind
FrameworkElement lastItemTapped = null;
public void onMoreClicked (object sender, RoutedEventArgs e)
{
Button button = sender as Button;
// find parent list item containing button
DependencyObject element = button;
while (element != null)
{
if (element is FrameworkElement && (element as FrameworkElement).Name.Equals("Item"))
break;
element = VisualTreeHelper.GetParent(element);
}
if (element != null)
lastItemTapped = element as FrameworkElement;
// show flyout menu
FlyoutBase.ShowAttachedFlyout(button);
}
public void OnItemEdit(object sender, RoutedEventArgs e)
{
if (lastItemTapped == null)
return;
...
}
Specifically, I want to find the "Note" element while in the the OnItemEdit handler.
Assuming the DataTemplate
is applied to a "Note", that should be as easy as casting the DataContext
of the sender
argument to a Note
(or whatever your type is called):
private void OnItemEdit(object sender, RoutedEventArgs e)
{
MenuFlyoutItem menuFlyoutItem = (MenuFlyoutItem)sender;
var note = menuFlyoutItem.DataContext as YourNoteClass;
...
}
If you need a reference to the "Node" TextBox
element, you can find it in the visual tree:
private void OnItemEdit(object sender, RoutedEventArgs e)
{
MenuFlyoutItem menuFlyoutItem = (MenuFlyoutItem)sender;
DependencyObject container = DetailList.ContainerFromItem(menuFlyoutItem.DataContext);
TextBox note = FindVisualChild<TextBox>(container);
...
}
private static T FindVisualChild<T>(DependencyObject visual) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(visual, i);
if (child != null)
{
T correctlyTyped = child as T;
if (correctlyTyped != null)
return correctlyTyped;
T descendent = FindVisualChild<T>(child);
if (descendent != null)
return descendent;
}
}
return null;
}