UWP:从 FlyoutMenuItem 单击处理程序中查找父级

UWP: find parent from FlyoutMenuItem click handler

使用 UWP,我有一个 ListView,其中每个项目都包含一个 Button。点击按钮打开 MenuFlyout。点击弹出选项之一会触发 Click 事件。

在事件处理程序中,如何找到父 Button 或父 ListView 项?

这是我的 XAML 的摘录。具体来说,我想在 OnItemEdit 处理程序中找到“Note”元素。

<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>

处理程序定义为:

async void OnItemEdit(object sender, RoutedEventArgs e)
{
  ...
}

编辑 - 解决方法

如果没有更好的方法,这里有一个还不错的解决方法。诀窍是使用 FlyoutBase.AttachedFlyout 而不是 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>

还有,代码隐藏

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;
  ...
}

在您的方案中,您可以添加一个成员变量来保存代表 ListViewItemGrid 实例以供以后访问。将 PointerEntered 事件添加到 Grid 面板。当您单击某个项目的按钮弹出弹出窗口时,将首先触发该项目的 GridPointerEntered 事件处理程序。在 PointerEntered 事件处理程序中,将 sender 分配给成员变量。然后您可以在 OnItemEdit 事件处理程序中访问 Grid

您可以检查以下内容作为示例:

//MainPage.xaml
<DataTemplate x:Key="NoteItemTemplate">
  <Grid PointerEntered="Grid_PointerEntered" >
    <Grid.ColumnDefinitions> ... </Grid.ColumnDefinitions>
    ……
  </Grid>
</DataTemplate>

//MainPage.xaml.cs

private Grid myPanel;

private void Grid_PointerEntered(object sender, PointerRoutedEventArgs e)
{
    myPanel = sender as Grid;
}

private void OnItemEdit(object sender, RoutedEventArgs e)
{
    //Change the value of index to adjust your scenario
    var box = VisualTreeHelper.GetChild(myPanel, 0) as TextBox;
}

更新:

请尝试以下代码:

private void OnItemEdit(object sender, RoutedEventArgs e)
{            
    //Find the item be clicked
    object clickedItem=new object();
    foreach(var cur in DetailList.Items)
    {
        if(cur.ToString()==(sender as MenuFlyoutItem).DataContext.ToString())
        {
            clickedItem = cur;
            break;
        }
    }
    //
    var type = DetailList.ContainerFromItem(clickedItem);
    var item = type as ListViewItem;
    var box = VisualTreeHelper.GetChild(item.ContentTemplateRoot as DependencyObject, 0);
    string text = (box as TextBox).Text;
}

Specifically, I want to find the "Note" element while in the the OnItemEdit handler.

假设 DataTemplate 应用于“注释”,这应该像将 sender 参数的 DataContext 转换为 Note 一样简单(或不管你叫什么类型):

private void OnItemEdit(object sender, RoutedEventArgs e)
{
    MenuFlyoutItem menuFlyoutItem = (MenuFlyoutItem)sender;
    var note = menuFlyoutItem.DataContext as YourNoteClass;
    ...
}

如果您需要对“节点”TextBox 元素的引用,您可以在可视化树中找到它:

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;
}