2
votes

I am trying to create a wpf combobox containing a list of World of warcraft items. I am using a item template for the combo box so that the items icon and item name are shown in the combobox drop down list. It works as is and I can start typing a item name and the list of items in the popup are automatically filtered.

The problem is that when I click an item I want the item name to be shown in the comboboxes text area. Instead what I get is a very brief ItemDisplayModel.ToString() then a empty string.

The ItemDisplayModel.ToString() being the ItemDisplayModel type that I selected from the list. Which makes sense cause the combobox control ItemsSource property contains an array of ItemDisplayModel types.

But in my AuctionHouseFilterItemNameModel.Value property I am detecting weather the value has changed and if so rebuild the list of items to be displayed in the combobox. So because the Text in the combobox was changed to ItemDisplayModel.ToString() when I click an item in the popup list it again rebuilds the list of items only this time there is no match because ItemDisplayModel.ToString() does not match any known item names.

All I am trying to do is when I click an item in the popup list I want the ItemDisplayModel.Name to be mapped to the AuctionHouseFilterItemNameModel.Value property and have the combobox text area to display AuctionHouseFilterItemNameModel.Value as well.

Does that make sense? I've tried numerous different ways and I am at a loss. Looking around the web did'nt give me the answer either. I'm sure it's something simple to fix but the solution eludes me.

Here is my XAML for the control

<UserControl
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:AuctionHouseSearchFilters="clr-namespace:Codefarts.WowTracks.DataMinerWPF.Models.AuctionHouseSearchFilters" 
            xmlns:Models="clr-namespace:Codefarts.WowTracks.DataMinerAppCore.Models;assembly=Codefarts.WowTracks.DataMinerAppCore"
            xmlns:DataMinerAppCore="clr-namespace:Codefarts.WowTracks.DataMinerAppCore;assembly=Codefarts.WowTracks.DataMinerAppCore"
            xmlns:dataMinerWpf="clr-namespace:Codefarts.WowTracks.DataMinerWPF"
            x:Name="userControl"
            x:Class="Codefarts.WowTracks.DataMinerWPF.Controls.AuctionHouseSearchFilters.AuctionHouseItemNameControl" 
             mc:Ignorable="d" 
             d:DesignHeight="51" d:DesignWidth="283">
    <UserControl.Resources>
        <dataMinerWpf:UriToBitmapImageConverter x:Key="UriToImageConverter" />
        <BitmapImage x:Key='defaultImage' UriSource='/Resources\118.png' />
        <DataTemplate x:Key="ItemTemplate" DataType="Models:ItemDisplayModel" >
            <StackPanel Orientation="Horizontal" Margin="0 5 0 5">
                <Image Width="50" Height="50" Stretch="Fill" Source="{Binding IconUrl, Converter={StaticResource UriToImageConverter}, TargetNullValue={StaticResource defaultImage}}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                <Label Content="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
            </StackPanel>
        </DataTemplate>

    </UserControl.Resources>
    <UserControl.DataContext>
        <AuctionHouseSearchFilters:AuctionHouseFilterItemNameModel/>
    </UserControl.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
        </Grid.RowDefinitions>
        <ComboBox x:Name="ItemList" MaxDropDownHeight="592" Grid.Row="0" ItemsSource="{Binding ItemDisplayModels}"   ItemTemplate="{StaticResource ItemTemplate}" IsEditable="True" IsTextSearchEnabled="True" IsTextSearchCaseSensitive="False" Text="{Binding Value}"    >

        </ComboBox>
    </Grid>
</UserControl>

Here is my code behind

  public partial class AuctionHouseItemNameControl : UserControl, IAuctionHouseFilterControl
    {
        private Application app;

        public AuctionHouseItemNameControl()
        {
            InitializeComponent();
        }

        public void SetModel(IAuctionHouseSearchFilter model)
        {
            this.DataContext = model;
        }

        public void SetApplication(Application app)
        {
            this.app = app;
        }  

 private void ItemList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

            //if (model.Handled)
            //{
            //    return;   
            //}

            // model.Handled = true;
            if (e.AddedItems != null && e.AddedItems.Count > 0)
            {
                var model = this.DataContext as Models.AuctionHouseSearchFilters.AuctionHouseFilterItemNameModel;

                var item = e.AddedItems[0] as ItemDisplayModel;
                // this.ItemList.SelectedValue = item.Name;
                model.Value = item.Name;

                // this.ItemList.SelectedItem = item.Name;
            }

            //   model.Handled = false;
        }
    }

and here is my data models

 [Export(typeof(IAuctionHouseSearchFilter))]
    public class AuctionHouseFilterItemNameModel : IAuctionHouseSearchFilter, INotifyPropertyChanged
    {
        private string value;

        private bool connected;

        private Application app;

        private ItemDisplayModel[] displayItems;
        private CancellationTokenSource backgroundCancel;

        private bool buildingDisplayItems;

        private readonly object lockObject = new object();

        private ItemDisplayModel displayValue;

        //  private bool Handled { get; set; }
        public ItemDisplayModel DisplayValue
        {
            get
            {
                return this.displayValue;
            }
            set
            {
                if (value == null)
                {
                    return;
                }
                this.Value = value.Name;
                this.displayValue = value;
                this.OnPropertyChanged();
            }
        }

        public string Value
        {
            get
            {
                return this.value;
            }

            set
            {
                //if (this.Handled)
                //{
                //    return;
                //}

                if (value == this.value)
                {
                    return;
                }

                this.value = value;
              //  this.Handled = true;
                this.OnPropertyChanged();
                var cancellationTokenSource = this.backgroundCancel;
                if (cancellationTokenSource != null)
                {
                    cancellationTokenSource.Cancel();
                }

                this.BuildDisplayItems();
             //   this.Handled = false;
            }
        }

        public IEnumerable<AuctionDataModel> Filter(MainFilterModel model, IEnumerable<AuctionDataModel> items)
        {
            if (!this.connected)
            {
                return items;
            }

            // get item id from it's name
            var list = this.app.ItemNames[model.SelectedRegion];
            var id = list.FirstOrDefault(x => x.Name.ToLowerInvariant().Contains(this.value.Trim().ToLowerInvariant()));

            return items.Where(x => id != null && x.ItemId == id.Id);
        }

        public IEnumerable<ItemDisplayModel> ItemDisplayModels
        {
            get
            {
                return this.displayItems;
            }
        }

        public async void BuildDisplayItems()
        {
            if (this.buildingDisplayItems)
            {
                return;
            }

            if (!this.connected)
            {
                this.displayItems = null;
            }

            //  return this.GetDisplayItems();     
            this.buildingDisplayItems = true;
            this.backgroundCancel = new CancellationTokenSource();
            this.OnPropertyChanged("ItemDisplayModels");
            await Task.Factory.StartNew(
                    () =>
                    {
                        var originalItems = this.displayItems;
                        this.displayItems = new[] { new ItemDisplayModel() { Name = "Rebuilding list..." } };
                        var correctedSearchValue = this.value.Trim().ToLowerInvariant();
                        //lock (this.lockObject)
                        //{
                        this.displayItems = (string.IsNullOrWhiteSpace(this.value) ?
                                            this.app.DisplayItemModels :
                                            this.app.DisplayItemModels.Where(x => x.Name.ToLowerInvariant().Contains(correctedSearchValue))).Take(100).AsParallel().ToArray();

                        this.buildingDisplayItems = false;
                        this.OnPropertyChanged("ItemDisplayModels");
                    },
                    this.backgroundCancel.Token);
        }

        public string Name
        {
            get
            {
                return "Item Name";
            }
        }

        public Type Control
        {
            get
            {
                return typeof(AuctionHouseItemNameControl);
            }
        }

        public virtual IAuctionHouseSearchFilter Clone()
        {
            return new AuctionHouseFilterItemNameModel()
                       {
                           Value = this.Value
                       };
        }

        public void Connect(Application app)
        {
            if (this.connected)
            {
                return;
            }

            this.app = app;

            this.connected = true;
        }

        public void Disconnect()
        {
            if (!this.connected)
            {
                return;
            }

            this.app = null;
            this.connected = false;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

   public class ItemDisplayModel : INotifyPropertyChanged
    {
        private string iconUrl;

        private string region;

        private string name;

        private int itemId;

        public int ItemId
        {
            get
            {
                return this.itemId;
            }
            set
            {
                if (value == this.itemId)
                {
                    return;
                }
                this.itemId = value;
                this.OnPropertyChanged();
            }
        }

        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                if (value == this.name)
                {
                    return;
                }
                this.name = value;
                this.OnPropertyChanged();
            }
        }

        public string Region
        {
            get
            {
                return this.region;
            }
            set
            {
                if (value == this.region)
                {
                    return;
                }
                this.region = value;
                this.OnPropertyChanged();
            }
        }

        public string IconUrl
        {
            get
            {
                return this.iconUrl;
            }
            set
            {
                if (value == this.iconUrl)
                {
                    return;
                }
                this.iconUrl = value;
                this.OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
1

1 Answers

1
votes

The problem is that when I click an item I want the item name to be shown in the comboboxes text area. Instead what I get is a very brief ItemDisplayModel.ToString() then a empty string.

To get the Name property displayed in the editable text area of the ComboBox use TextSearch.TextPath so your ComboBox definition would look like:

<ComboBox x:Name="ItemList"
          Grid.Row="0"
          IsEditable="True"
          IsTextSearchCaseSensitive="False"
          IsTextSearchEnabled="True"
          ItemTemplate="{StaticResource ItemTemplate}"
          ItemsSource="{Binding ItemDisplayModels}"
          MaxDropDownHeight="592"
          TextSearch.TextPath="Name" />

Now when an item is selected from the dropdown instead of seeing ItemDisplayModel.ToString(), you will see the Name property.