WPFToolkit AutoCompleteBox ne se lie pas correctement dans ListView
Après avoir lutté un peu pour commencer avec le contrôle AutoCompleteBox de WPFToolkit , je suis confronté à un autre problème lorsque j'essaie d'utiliser une AutoCompleteBox dans un ListView , il se lie presque parfaitement, mais pour une raison que j'ignore, il ne s'affiche pas au début ValueMemberPathet essaie à la place pour convertir l'objet en chaîne qui donne Namespace.objectau lieu de la ValueMemberPathvaleur appropriée , cependant lors de la sélection d'un autre élément dans l'AutoCompleteBox cela fonctionne parfaitement et il n'en montre aucun autre Namespace.object.
Voici mon code, vous pouvez simplement le copier et le coller pour obtenir le même résultat (n'oubliez pas d'ajouter DotNetProjects.WpfToolkit.Input dans NuGet Package Manager) :
- Namespace.MainWindow.xaml
<Window x:Class="Namespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Namespace"
mc:Ignorable="d"
xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=DotNetProjects.Input.Toolkit"
Title="AutoCompleteBox in ListView" Height="300" Width="350" WindowStartupLocation="CenterScreen">
<!-- Required Template to show the names of the Items in the ItemsList -->
<Window.Resources>
<DataTemplate x:Key="AutoCompleteBoxItemTemplate">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Background="Transparent">
<Label Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel Margin="5">
<StackPanel Orientation="Horizontal" Margin="0 5 0 0">
<StackPanel Width="{Binding ElementName=FirstColumnWidth, Path=ActualWidth}">
<TextBlock Text="ACB binded to Cart.Item"/>
<!-- ACB that binds correctly -->
<toolkit:AutoCompleteBox
ItemsSource="{Binding Path=ItemsList}"
ValueMemberPath="Name"
SelectedItem="{Binding Path=Cart.Item, Mode=TwoWay}"
ItemTemplate="{StaticResource AutoCompleteBoxItemTemplate}"/>
</StackPanel>
<StackPanel Margin="15 0 0 0">
<TextBlock Text="Value of Cart.Item.Name"/>
<TextBlock Text="{Binding Path=Cart.Item.Name}"/>
</StackPanel>
</StackPanel>
<TextBlock Margin="0 30 0 0" HorizontalAlignment="Center" Text="ListView with CartsList as ItemsListSource"/>
<ListView ItemsSource="{Binding CartsList}">
<ListView.View>
<GridView>
<GridViewColumn x:Name="FirstColumnWidth">
<GridViewColumn.Header>
<TextBlock Text="ACB binded to each Cart.Item"/>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<!-- ACB that doesn't bind correctly -->
<toolkit:AutoCompleteBox
ItemsSource="{
Binding RelativeSource={RelativeSource AncestorType=Window},
Path=DataContext.ItemsList}"
ValueMemberPath="Name"
SelectedItem="{Binding Path=Item, Mode=TwoWay}"
ItemTemplate="{StaticResource AutoCompleteBoxItemTemplate}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn >
<GridViewColumn.Header>
<TextBlock Text="Value of each Cart.Item.Name"/>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Item.Name}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Window>
- Code derrière (MainWindow.xaml.cs)
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace Namespace
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
// INPC Implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// The list that contains Items that will be chosen in a Cart
private ObservableCollection<Item> _ItemsList;
public ObservableCollection<Item> ItemsList
{
get => _ItemsList;
set
{
_ItemsList = value;
OnPropertyChanged();
}
}
// The list that contains Carts that will be shown in the ListView
private ObservableCollection<Cart> _CartsList;
public ObservableCollection<Cart> CartsList
{
get => _CartsList;
set
{
_CartsList = value;
OnPropertyChanged();
}
}
// A signle Cart
private Cart _Cart;
public Cart Cart
{
get => _Cart;
set
{
_Cart = value;
OnPropertyChanged();
}
}
public MainWindow()
{
DataContext = this;
InitializeComponent();
// Populating ItemsList
ItemsList = new ObservableCollection<Item>()
{
new Item("T-shirt"), new Item("Jeans"), new Item("Boots"),
};
// Populating CartsList
CartsList = new ObservableCollection<Cart>()
{
new Cart(ItemsList[0]),
new Cart(ItemsList[2]),
new Cart(ItemsList[1]),
new Cart(ItemsList[0]),
new Cart(ItemsList[1]),
};
// Setting an Item to Cart
Cart = new Cart(ItemsList[2]);
}
}
// Cart Object
public class Cart
{
public Item Item { get; set; }
public Cart(Item item) => Item = item;
}
// Item Object
public class Item
{
// Important to be private set so it cannot be changed
public string Name { get; private set; }
public Item(string name) => Name = name;
}
}
Réponses
Pour une raison quelconque, le changement de sélection qui met à jour le texte en fonction du ValueMemberPathn'est pas déclenché lors de l'imbrication du AutoCompleteBoxdans le ListView. L' SelectionChangedévénement ne se déclenche même pas. Je ne pouvais pas comprendre pourquoi exactement et s'il s'agissait d'un bogue ou non. Cependant, je peux vous montrer une solution de contournement en utilisant le Microsoft.Xaml.Behaviors.Wpfpackage.
Vous pouvez créer une action de déclenchement qui réinitialise le SelectedItemet l'affecte à nouveau AutoCompleteBox.
public class ForceUpdateSelectedItemAction : TriggerAction<AutoCompleteBox>
{
protected override void Invoke(object parameter)
{
var selectedItem = AssociatedObject.SelectedItem;
AssociatedObject.SetCurrentValue(AutoCompleteBox.SelectedItemProperty, null);
AssociatedObject.SetCurrentValue(AutoCompleteBox.SelectedItemProperty, selectedItem);
}
}
Cette action de déclenchement peut être utilisée pour l' Loadedévénement du AutoCompleteBox.
<toolkit:AutoCompleteBox ...>
<b:Interaction.Triggers>
<b:EventTrigger EventName="Loaded">
<local:ForceUpdateSelectedItemAction/>
</b:EventTrigger>
</b:Interaction.Triggers>
</toolkit:AutoCompleteBox>
Cela entraînera une mise à jour de la sélection et du texte sans modifier la propriété liée.