【问题标题】:How to set focus to a bound ListboxItem by pressing its child element?如何通过按下子元素将焦点设置到绑定的 ListboxItem?
【发布时间】:2013-05-21 05:59:48
【问题描述】:

我正在开发一个小型 WPF 应用程序,主要包括在其他人 ObservableCollection<> 中显示 ObservableCollection<>,等等。

这是我的应用程序的代码示例:

<Listbox Name="MainList" ItemsSource={Binding}>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>      

                <Textblock Text={Binding MainName} />
                <Button>Add item</Button>
                <Button>Delete item</Button>

                <Listbox Name="ChildList" ItemsSource="{Binding Path=ChildItem}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <Textblock Text={Binding ChildName} />
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </Listbox>

            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</Listbox>

在视觉上它几乎看起来像这样:

编辑:

我会重新解释我想要做什么。

  • 每当我单击Button AButton B 时,我都想选择它们所在的MainList ListBoxItem(即:A Item

  • 当我第二次点击Button B时:

    • 我想确保在ChildList(图片中的第二个列表框)中选择了ListBoxItem
    • 如果是这样,我想在代码隐藏中将其删除。

但我的主要问题是,由于所有内容都是由我的绑定生成的,因此到目前为止,我无法从我的ChildList 中获取一个元素,因为ChildList 在我的任何MainList ListBoxItem 中都存在重复。

【问题讨论】:

  • 你必须使用DataTemplate 吗? A itemB item 可以是 UserControl。这样一来,应该会容易很多。
  • Button被点击时,你想选择MainItemListBoxItem容器,给它焦点还是两者都选择?
  • @NicolasVoron 到目前为止,我的整个代码都使用 DataTemplates 来绑定我不同的 ObservableCollection。所以我真的很想坚持下去。
  • @dkozl 单击两个按钮中的任何一个时,我想选择包含它们的 MainItem ListBoxItem。

标签: c# wpf xaml data-binding listboxitem


【解决方案1】:

如果我理解得很好,问题是您希望首先单击未选中项目的按钮以选择 MainItem,然后在下次单击时,当 MainItem 已被选中时,执行单击操作。单击按钮时尝试此操作:

private ListBoxItem FindItemContainer(DependencyObject obj)
{
   while (obj != null && !(obj is ListBoxItem))
   {
       obj = VisualTreeHelper.GetParent(obj);
   }

   if (obj != null)
       return obj as ListBoxItem;
   else
       return null;
}

private void Button_Click(object sender, RoutedEventArgs e)
{
   var lbi = FindItemContainer(sender as DependencyObject);
   if (lbi != null)
   {
       if (lbi.IsSelected)
       {
           //do click event
       }
       else
           lbi.IsSelected = true;
   }
}

当然你也可以通过将ListBoxItem.IsSelected绑定到bool MainItem.MyItemIsSelected来做更多的MVVM方式

<ListBox.ItemContainerStyle>
    <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="IsSelected" Value="{Binding Path=MyItemIsSelected, Mode=TwoWay}"/>
    </Style>
</ListBox.ItemContainerStyle>

Button.Command 到您的ICommand MainItem.DeleteCommand,然后在执行命令时执行以下操作:

if (MyItemIsSelected)
{
    //do command body
}
else
    MyItemIsSelected = true;

从长远来看,这会更好,因为您可以在ChildList 对象中复制SelectedItem 行为(添加MyItemIsSelected 并将其绑定到内部'ListBoxItem.IsSelected,如上所述)并将MySelectedItem 属性添加到ChildList

ChildItem MySelectedItem
{
   get
   {
      return Items.FirstOrDefault(n=>n.MyItemIsSelected);
   }
}

您的删除命令将如下所示:

if (MyItemIsSelected)
{
    ChildItem selItem = ChildItems.MySelectedItem;
    if (selItem != null) ChildItems.Items.Remove(selItem);
}
else
    MyItemIsSelected = true;

如果所有内容都是数据绑定的并且列表是ObservableCollections,那么您可以在对象中执行所有这些操作,UI 将随之而来。实际上你只能做这个子选择绑定位并且仍然使用第一个解决方案并且在Button_Click看起来像这样:

private void Button_Click(object sender, RoutedEventArgs e)
{
   var lbi = FindItemContainer(sender as DependencyObject);
   if (lbi != null)
   {
       if (lbi.IsSelected)
       {
           MainItem mainItem = lbi.Content as MainItem;
           ChildItem selChild = mainItem.ChildItems.MySelectedItem;
           if (selChild != null) mainItem.ChildItems.Items.Remove(selChild);
       }
       else
           lbi.IsSelected = true;
   }
}

这是Dropbox 上的简单工作示例

【讨论】:

  • 非常感谢您在此答案中付出的努力。您的解释帮助我以其他方式解决了我的问题。我在 ObservableCollections 中更改了 50% 的类。感谢您,代码重构比我预期的要容易得多!
【解决方案2】:

你可以在后面的代码中做任何你想做的事情:

  • 查找按钮被按下的项目:在点击事件中,将 sender 参数强制转换为 Button 类型。它的 DataContext 属性将包含您要选择的项目。
  • 选择项目:将 MainList.SelectedItem 设置为项目。
  • 焦点将在按钮上,但这应该没问题,因为它在项目内部。
  • 在第二个列表框中查找选定项:在 DataTemplate 中定位列表框很棘手,但您可以将其 IsSynchronizedWithCurrentItem 属性设置为 True,然后使用底层子集合的默认 CollectionView。您会像上面一样找到 MainList 的当前项目。然后你会使用:

    itemToDelete = CollectionViewSource.GetDefaultView(item.ChildItems).CurrentItem; item.ChildItems.Remove(itemToDelete);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-27
    • 2018-03-25
    • 1970-01-01
    • 1970-01-01
    • 2011-06-08
    • 1970-01-01
    • 2012-01-05
    相关资源
    最近更新 更多