WPFToolkitAutoCompleteBoxがListView内で正しくバインドされていません

Aug 21 2020

WPFToolkitのAutoCompleteBoxコントロールを使い始めるのに少し苦労した後、ListView内でAutoCompleteBoxを使用しようとすると、別の問題に直面します。ほぼ完全にバインドされますが、無視する理由で、最初は表示されずValueMemberPath、代わりに試行します。オブジェクトをNamespace.object適切なValueMemberPath値の代わりに与える文字列に変換しますが、AutoCompleteBoxで別の項目を選択すると、完全に機能し、他の項目は表示されませんNamespace.object

これが私のコードです。コピーして貼り付けるだけで同じ結果が得られます DotNetProjects.WpfToolkit.Input NuGetパッケージマネージャーに追加すること忘れないでください

  • 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>
  • コードビハインド(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;
    }
}

回答

thatguy Aug 21 2020 at 17:48

いくつかの理由に応じてテキストを更新選択変更のためのValueMemberPath入れ子にしたときにトリガされませんAutoCompleteBoxでしListViewSelectionChangedイベントでも発生しません。理由を正確に理解できず、これがバグかどうかもわかりませんでした。ただし、パッケージを使用した回避策を紹介できMicrosoft.Xaml.Behaviors.Wpfます。

をリセットしSelectedItem、に再度割り当てるトリガーアクションを作成できます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);
   }
}

このトリガーアクションは、のLoadedイベントに使用できますAutoCompleteBox

<toolkit:AutoCompleteBox ...>
   <b:Interaction.Triggers>
      <b:EventTrigger EventName="Loaded">
         <local:ForceUpdateSelectedItemAction/>
      </b:EventTrigger>
   </b:Interaction.Triggers>
</toolkit:AutoCompleteBox>

これにより、バインドされたプロパティを変更せずに、選択範囲とテキストが更新されます。