【问题标题】:UWP: find parent from FlyoutMenuItem click handlerUWP:从 FlyoutMenuItem 单击处理程序中查找父级
【发布时间】:2021-05-09 03:29:35
【问题描述】:

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

【问题讨论】:

    标签: uwp flyout


    【解决方案1】:

    具体来说,我想在 OnItemEdit 处理程序中找到“Note”元素。

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

    【讨论】:

    • 抱歉,还没有。我肯定会的。可能是星期四。
    • 如果我按照您的建议进行操作,DataContext 指向注释数据(一串文本),而不是注释文本框。我一直在研究这个并进行了一些实验,但不知道如何将DataContext 设置为音符控件。我尝试将DataContext="{StaticResource Item}" 添加到“编辑”MenuFlyoutItem,但这会导致运行时异常。这种情况在网上搜索一无所获。
    • 我想我可以使用 ListView 搜索包含引用的笔记数据的项目。很简单,但如果没有更直接的解决方案,我只想这样做。
    • @PeriHartman:查看我的编辑。它应该回答你的问题。
    • OnItemEdit 中为container 返回null。
    【解决方案2】:

    在您的场景中,您可以添加一个成员变量来保存代表ListViewItemGrid 实例以供以后访问。将PointerEntered 事件添加到Grid 面板。当你点击一个item的按钮弹出一个flyout时,item的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;
    }
    

    【讨论】:

    • 我一定是误解了你的解释是如何工作的。如果列表有 10 个项目,我如何确定哪个项目(以及哪个 T​​extBox)触发了 OnItemEdit 事件?看来您是在任意选择一个(可能是第一个)。
    • 这不是任意的。在单击某个 Button 以弹出 Flyout 之前,您必须将指针移到包含 Flyout 的 ListViewItem 上。此时会调用PointerEntered事件处理函数将ListViewItem的Grid赋值给成员变量,在Flyout隐藏之前不会调用其他ListViewItem的PointerEntered事件处理函数。当你点击一个ListViewItem的Button时,这样可以保证存储在成员变量中的Grid总是那个Button被点击的那个。
    • 啊,抱歉,我显然不明白 'PointerEntered' 的作用。不幸的是,我认为这种方法行不通。我没有提到(因为我认为会有一个直截了当的解决方案)它需要与触摸屏一起使用,而且我认为“PointerEntered”不会做任何事情(我会对此进行测试并查看)。正确的解决方案是处理“按钮”单击事件,缓存按钮(或父项)的引用,然后将单击事件转发给原始处理程序。我找不到办法做到这一点。此外,“PointerEntered”方法有些脆弱。
    • 我已经更新了答案,你可以试试看更新是否能满足你的要求。
    • 我已经有一个解决方法,我认为这更好。我会把它添加到 OP 中以供参考。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-29
    • 1970-01-01
    相关资源
    最近更新 更多