7
votes

I use a datatemplate to visualize some items in a ComboBox, the ItemsSource is bound to an ObservableCollection. To keep it simple, let's say I put persons into the ObservableCollection:

public class Person {
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

My DataTemplate looks like this:

<DataTemplate TargetType="{x:Type Person}">
  <StackPanel Orientation="Horizontal">
    <TextSearch.Text>
      <MultiBinding StringFormat="{} {0} {1}">
        <Binding Path="FirstName"/>
        <Binding Path="LastName"/>
      </MultiBinding>
    </TextSearch.Text>
    <TextBlock Text="{Binding FirstName}" Margin="2,0" />
    <TextBlock Text="{Binding LastName}"/>
  </StackPanel>
</DataTemplate>

Now I want to enable autocompletion for full names in the ComboBox without introducing a third property on my person class. Hence I do not want to use TextSearch.TextPath Property on the ComboBox, but instead I would like to bind the TextSearch.Text-Property of each ComboBoxItem in the DataTemplate. Unfortunately, when I do this (which works with a MultiBinding and StringFormat, tested with Snoop), the bound value is registered for my StackPanel only, but using Snoop (great tool) I found that this stackpanel serves just as content for some further ComboBoxItemTemplate, which puts another border etc. and finally a ComboBoxItem-tag around my outer StackPanel. Therefore, the TextSearch.Text-setting is not effective, because it must be set in the created ComboBoxItem, and not somewhere within.

Question now: How can I propagate this TextSearch.Text-Property from my DataTemplate to the surrounding ComboBoxItem using XAML-Styles and -Control-Templates only? The solution may modify the default-ControlTemplates of ComboBox and ComboBoxItem and my custom (Item-)DataTemplate, but not use any Code-Behind, or at least not too much. Maybe an attached behaviour would be ok, too. But I'm almost sure there must be a way to make it work without, TemplateBinding or RelativeSource-stuff... And of course the solution must make my keyboard-selection and textcompletion work, s.th. when the list contains Hans Josef and Hans Peter, then entering 'Hans ' should autosuggest Hans Josef, whereas entering 'Hans P' quickly enough should autosuggest Hans Peter.

Any solutions?

2
At least by now I feel quite confident, that the task is indeed not too easy. Or the long text is just making people run away in panic...Simon D.
I am close to giving it up. I managed to get the ComboBoxItem to carry the correct TextSearch.Text-Property by setting it in the ComboBox.ItemContainerStyle (instead of the ItemTemplate, as I did before). Now it looks quite nice in the visual tree (Snoop again), but unfortunately it does not have any effect. There is no selection taking place after typing 'Hans' or whatever.Simon D.

2 Answers

2
votes

That stuff surrounding your panel is the default container. You need to apply the TextSearch.Text property to the container. You should be able to do this by setting the property via the ItemContainerStyle like so:

<ComboBox.ItemContainerStyle>
    <Style TargetType="{x:Type ComboBoxItem}">
        <Setter Property="TextSearch.Text">
            <Setter.Value>
                <MultiBinding StringFormat="{} {0} {1}">
                    <Binding Path="FirstName"/>
                    <Binding Path="LastName"/>
                </MultiBinding>
            </Setter.Value>
        </Setter>
    </Style>
</ComboBox.ItemContainerStyle>
9
votes

Short answer: what you want to do cannot be done directly in XAML, but there are other ways to do it.

Long answer: the ComboBox looks for the TextSearch.Text property directly on the data items stored in the Items or ItemsSource collection. Therefore, you can't set the property in the data template or style because they apply to the objects used to display the data items, and not the data items themselves.

In particular, if you look at the examples on the TextSearch class page, you will see that they attach the TextSearch.Text property to the Image objects which go into the ComboBox.Items collection. You could do this in your program by making Person a DependencyObject, but I don't think you want to set the property on every single object like that.

You have several options here:

If you can modify the Person class, you can either define the ToString() method to return the text to autocomplete on or define an arbitrary property like Fullname and set Textsearch.TextPath on the ComboBox. For example:

public class Person
{
     string FirstName { get; set; }
     string LastName {get; set; }
     string FullName { get { return String.Format("{0} {1}", FirstName, LastName); } }
}

and

<ComboBox TextSearch.TextPath="FullName" ItemsSource="collectionOfPersons"/>

Altenatively, if you don't want to touch Person, you can create a wrapper class that exposes these properties.