英文:
WPF ComboBox - ItemTemplate vs. IsEditable
问题
I created a combo box with an item template. The template includes a status icon and text:
我创建了一个包含项目模板的组合框。模板包括一个状态图标和文本:
Here is the XAML:
以下是XAML代码:
<DataTemplate x:Key="ItemTemplate">
<WrapPanel>
<Image Width="24" Height="24" Stretch="Fill" Source="{Binding StateImage}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,0,15,0"/>
<Label Content="{Binding Address}" VerticalAlignment="Center" HorizontalAlignment="Center" />
</WrapPanel>
</DataTemplate>
<ComboBox x:Name="CbxClients" HorizontalAlignment="Center" VerticalAlignment="Top" ItemTemplate="{StaticResource ItemTemplate}" Width="320" Height="24" IsEditable="False" />
Now I want to make the combo box editable, so that the user can enter new strings, which should then be added to the list. Thus I set "IsEditable" to true. This is the result:
现在我想使组合框可编辑,以便用户可以输入新的字符串,然后将其添加到列表中。因此,我将“IsEditable”设置为true。这是结果:
This change causes a problem: the icon is now no longer shown in the combo box (only in the drop-down area). While editing/entering a string, this would be fine, but after the input has been committed, I would expect the new item to be added to the list and an icon to be shown.
这个更改引发了一个问题:图标现在不再显示在组合框中(只显示在下拉区域)。在编辑/输入字符串时,这是可以接受的,但在输入被提交后,我希望新项目被添加到列表中,并显示一个图标。
Is there any way I can achieve this behavior?
是否有任何方法可以实现这种行为?
英文:
I created a combo box with an item template. The template includes a status icon and text:
Here is the XAML:
<DataTemplate x:Key="ItemTemplate">
<WrapPanel>
<Image Width="24" Height="24" Stretch="Fill" Source="{Binding StateImage}" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="0,0,15,0"/>
<Label Content="{Binding Address}" VerticalAlignment="Center" HorizontalAlignment="Center" />
</WrapPanel>
</DataTemplate>
<ComboBox x:Name="CbxClients" HorizontalAlignment="Center" VerticalAlignment="Top" ItemTemplate="{StaticResource ItemTemplate}" Width="320" Height="24" IsEditable="False" />
Now I want to make the the combo box editable, so that the user can enter new strings, which hould then be added to the list. Thus I set "IsEditable" to true. This is the result:
This change causes a problem: the icon is now no longer shown in the combo box (only in the drop down area). While editing/entering a string this would be fine, but after the input has been committed, I would expect the new item being added to the list and an icon to be shown.
Is there any way I can achieve this behavior?
答案1
得分: 2
以下是翻译好的内容:
-
问题:
ComboBox
不包含简单的string
项目。相反,您已经定义了一个DataTemplate
来呈现更复杂的项目模型。
解决方案: 关键问题在于ComboBox
没有机会知道如何使用输入值构造项目模型的新实例。您必须明确执行这个操作。 -
问题: 由于您的
ComboBox
配置为处于编辑模式,TextBox 将只包含项目模型的文本表示(ComboBox
将在模型上调用object.ToString
以获取文本表示)。
解决方案: 没有现成的解决方案。如果ComboBox.IsEditable
返回false
,ComboBox
将使用ContentPresenter
显示ComboBox.SelectedItem
。如果ComboBox.IsEditable
返回true
,ComboBox
将用TextBox
替换ContentPresenter
以允许编辑。现在,您可以覆盖原始的ControlTemplate
,在两个内容位置之间切换。例如,在GotFocus
时显示TextBox
,在LostFocus
时切换回ContentPresenter
。
您可以使用 XAML 设计器提取ControlTemplate
,或者访问 Microsoft Dos: ComboBox Styles and Templates 并从那里复制模板和引用。
要启用文本搜索,请将 ComboBox.IsTextSearchEnabled
设置为 true
,并将附加属性 TextSearch.TextPath
设置为项目模型上的实际属性。
出于性能原因,当显示的文本是动态的时候,应该将 Label
替换为 TextBlock
。
以下是给定的项目模型:
class NetworkAddress : INotifyPropertyChanged
{
public ImageSource StateImage { get; set; }
public string Address { get; set; }
}
要启用搜索和项目创建,必须将 ComboBox
配置如下:
<ComboBox ItemsSource="{Binding NetworkAddresses}"
IsEditable="True"
IsTextSearchEnabled="True"
TextSearch.TextPath="Address"
PreviewKeyUp="ComboBox_PreviewKeyUp">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type NetworkAddress}">
<WrapPanel>
<Image Source="{Binding StateImage}" />
<TextBlock Text="{Binding Address}" />
</WrapPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
然后处理 ComboBox.PreviewKeyUp
事件以添加新项目:
partial class MainWindow : Window
{
// TODO::Property must be a dependency property because MainWindow is a DependencyObject
public ObservableCollection<NetworkAddress> NetworkAddresses { get; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.NetworkAddresses = new ObservableCollection<NetworkAddress>
{
new NetworkAddress() { ... },
new NetworkAddress() { ... },
};
}
private void ComboBox_PreviewKeyUp(object sender, KeyEventArgs e)
{
if (e.Key is not Key.Enter or not Key.Return)
{
return;
}
var comboBox = sender as ComboBox;
string inputText = comboBox.Text;
if (comboBox.IsTextSearchEnabled
&& comboBox.SelectedItem is null
|| !this.NetworkAddresses.Any(address => address.Address.Equals(inputText, StringComparison.OrdinalIgnoreCase)))
{
AddNewItemToCollection(inputText);
comboBox.SelectedIndex = comboBox.Items.Count - 1;
// Refresh is only required for the ComboBox flyout to render properly
comboBox.Items.Refresh();
}
}
private void AddNewItemToCollection(string text)
=> this.NetworkAddresses.Add(new NetworkAddress() { ... });
}
英文:
You are encountering two problems:
-
Problem: The
ComboBox
is not containing simplestring
items. Instead you have defined aDataTemplate
to render a more complex item model.
Solution: The point is that theComboBox
has no chance to know how to use the input value to construct a new instance of the item model. You have to do it explicitly. -
Problem: Because your
ComboBox
is configured to be in edit mode, the TextBox will only contain a text representation of your item model (theComboBox
will callobject.ToString
on the model to obtain a text representation).
Solution: There is no out-of-the-box solution. IfComboBox.IsEditable
returnsfalse
theComboBox
will display theComboBox.SelectedItem
using aContentPresenter
. IfComboBox.IsEditable
returnstrue
theComboBox
will replace theContentPresenter
with aTextBox
in order to allow the editing. You can now override the originalControlTemplate
and toggle between both content sites. For example, onGotFocus
you show theTextBox
and onLostFocus
you switch back to theContentPresenter
.
You can use the XAML Designer to extract theControlTemplate
or use visit Microsoft Dos: ComboBox Styles and Templates and copy the template and references from there.
To enable text search set ComboBox.IsTextSearchEnabled
to true
and set the attached property TextSearch.TextPath
to the actual property on the item model.
For performance reasons you should replace the Label
with a TextBlock
, especially when the displayed text is dynamic.
Given is the following item model:
class NetworkAddress : INotifyPropertyChanged
{
public ImageSource StateImage { get; set; }
public string Address { get; set; }
}
To enable searching and item creation you must configure the ComboBox
as follows:
<ComboBox ItemsSource="{Binding NetworkAddresses}"
IsEditable="True"
IsTextSearchEnabled="True"
TextSearch.TextPath="Address"
PreviewKeyUp="ComboBox_PreviewKeyUp">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type NetworkAddress}">
<WrapPanel>
<Image Source="{Binding StateImage}" />
<TextBlock Text="{Binding Address}" />
</WrapPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Then handle the the ComboBox.PreviewKeyUp
event to add a new item:
partial class MainWindow : Window
{
// TODO::Property must be a dependency property because MainWindow is a DependencyObject
public ObservableCollection<NetworkAddress> NetworkAddresses { get; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.NetworkAddresses = new ObservableCollection<NetworkAddress>
{
new NetworkAddress() { ... },
new NetworkAddress() { ... },
};
}
private void ComboBox_PreviewKeyUp(object sender, KeyEventArgs e)
{
if (e.Key is not Key.Enter or not Key.Return)
{
return;
}
var comboBox = sender as ComboBox;
string inputText = comboBox.Text;
if (comboBox.IsTextSearchEnabled
&& comboBox.SelectedItem is null
|| !this.NetworkAddresse.Any(address => address.Address.Equals(inputText, StringComparison.OrdinalIgnoreCase)))
{
AddNewItemToCollection(inputText);;
comboBox.SelectedIndex = comboBox.Items.Count - 1;
// Refresh is only required for the ComboBox flyout to render properly
comboBox.Items.Refresh();
}
}
private void AddNewItemToCollection(string text)
=> this.NetworksAddresses.Add(new NetworkAddress() { ... });
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论