通过DataTemplate设置WPF-ComboBoxItem的TextSearch.Text

7

我使用一个数据模板(datatemplate)来将ComboBox中的一些项目可视化, ItemsSource绑定到ObservableCollection。为了简单起见,假设我将人员放入ObservableCollection中:

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

我的数据模板看起来像这样:
<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>

现在我想在ComboBox中启用全名自动完成,但不想在我的人员类上引入第三个属性。因此,我不想在ComboBox上使用TextSearch.TextPath属性,而是想将每个ComboBoxItem的TextSearch.Text属性绑定到DataTemplate中。 不幸的是,当我这样做时(使用MultiBinding和StringFormat,通过Snoop测试),绑定值仅为我的StackPanel注册,但使用Snoop(很棒的工具)我发现这个stackpanel只是一些进一步的ComboBoxItemTemplate的内容,它放置了另一个边框等等,最后在我的外部StackPanel周围放置了一个ComboBoxItem标签。因此,TextSearch.Text设置无效,因为它必须在创建的ComboBoxItem中设置,而不是在其他地方设置。
现在的问题是:如何仅使用XAML样式和控件模板将DataTemplate中的TextSearch.Text属性传播到周围的ComboBoxItem?解决方案可能修改ComboBox和ComboBoxItem的默认ControlTemplates以及我的自定义(Item-)DataTemplate,但不使用任何Code-Behind,或者至少不要太多。也许附加行为也可以,但我几乎确定必须有一种方法使其正常工作,而不需要TemplateBinding或RelativeSource-stuff……当然,解决方案必须使我的键盘选择和文本完成工作,例如,当列表包含Hans Josef和Hans Peter时,输入“Hans”应该自动建议Hans Josef,而输入“Hans P”足够快时应该自动建议Hans Peter。
有什么解决方案吗?

至少到现在,我感到相当自信,这项任务确实不太容易。或者这篇长文只是让人们惊慌失措地逃跑... - Simon D.
1
我快要放弃了。我设法通过在ComboBox.ItemContainerStyle中设置它(而不是之前的ItemTemplate)来使ComboBoxItem携带正确的TextSearch.Text属性。 现在在可视树中看起来相当不错(再次使用Snoop),但不幸的是它没有任何效果。在键入“Hans”或其他内容后,没有进行任何选择。 - Simon D.
2个回答

10

简短回答:你想要做的不能直接在XAML中完成,但有其他方法可以实现。

长篇回答:ComboBox 直接在 Items 或 ItemsSource 集合的数据项上查找 TextSearch.Text 属性。因此,你不能在数据模板或样式中设置该属性,因为它们应用于用于显示数据项的对象,而不是数据项本身。

特别地,如果你查看 TextSearch class 页面上的示例,你会发现它们会将 TextSearch.Text 属性附加到放入 ComboBox.Items 集合中的 Image 对象上。你可以通过使 Person 成为 DependencyObject 来在程序中实现这一点,但我认为你不想这样对每个对象都设置该属性。

你有几个选项:

如果你可以修改 Person 类,你可以将 ToString() 方法定义为返回自动完成文本或者定义一个任意属性(如 Fullname),并在 ComboBox 上设置 Textsearch.TextPath。例如:

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

并且

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

另外,如果您不想触碰Person类,可以创建一个包装类来公开这些属性。


3
恐怕你是正确的,目前没有不添加一个额外属性到类中或者甚至包装和扩展旧类的新类的解决方案。由于问题说明了已知这些可能性但不想采用,所以我不能将其标记为“答案”——除非你通过一些证明或解释来扩展你的简短回答并说明为什么这是不可能的。 - Simon D.

2

您面板周围的那些东西是默认容器。您需要将TextSearch.Text属性应用于该容器。您可以通过在ItemContainerStyle中设置属性来实现此操作:

<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>

听起来非常合理,但不幸的是并没有奏效。 键盘选择的行为不如预期,可能是输入仍然与人员的ToString值进行比较,而textsearch.text根本没有任何影响。 我想绑定确实起作用了,但ComboBox却忽略了它。 - Simon D.
对我没用,你无法从样式中针对这个TextSearch.Text。 - Hisham

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接