Can I make WPF ComboBox treat substring as new value? - c#

I'm using a ComboBox defined by:
<ComboBox Grid.Column="2" Height="29" HorizontalAlignment="Left" Margin="137,192,0,0" Name="componentsComboBox" VerticalAlignment="Top" Width="224"
IsEditable="True"
TextSearch.TextPath="Name">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
..to display a list of objects by their "Name" properties. I'm observing the following behaviors:
Click an item in the dropdown, and componentsComboBox.SelectedValue (and .SelectedItem) correspond to the clicked item. OK!
Start typing the name of an item, autocomplete fills in as you type, .SelectedValue (and .SelectedItem) correspond to the autocompleted item. GREAT!
Start typing the name of an item, autocomplete fills in as you type, hit delete to truncate to only what you have actually typed, .SelectedValue and .SelectedItem STILL correspond to the autocompleted item. NO! BAD WPF! BAD!
Similar behavior to 3 if you delete characters from the end of the textbox portion
In essence, if I have a List containing two objects defined by, e.g.,
{ new Component() { Name = "COMPONENT1"},
new Component() { Name = "COMPONENT2"} }
I want the values:
COMPONENT1
COMPONENT2
to appear in the drop-down portion, and if the user enters "COMP" I would like to recognize that they have entered a new value, but as it stands right the control makes it look like they selected COMPONENT1.
What am I missing here?

You can do this in WPF easily by setting the IsEditable and IsReadOnly properties to True & False respectively.
Ref.: http://msdn.microsoft.com/en-us/library/system.windows.controls.combobox.iseditable.aspx
e.g. How can I allow users to edit text in a ComboBox in WPF?
<Window x:Class="LearnWPF.EditableComboBox.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="LearnWPF.EditableComboBox" Height="300" Width="300"
>
<Window.Resources>
<XmlDataProvider x:Key="items" XPath="//item">
<x:XData>
<items xmlns="">
<item>01</item>
<item>02</item>
<item>03</item>
</items>
</x:XData>
</XmlDataProvider>
</Window.Resources>
<Grid>
<ComboBox IsEditable="True" DataContext="{StaticResource items}"
ItemsSource="{Binding}"/>
</Grid>
</Window>

I experienced this behavior today and agree that on the surface, it appears to be a bug. Digging a little deeper though reveals a bit of a gray area.
What I think is going on here is that the control is running a prefix matching algorithm every time the text box is updated, regardless of the keystrokes that cause the text box to be updated. So when you delete the auto-completed part of the text, the prefix matching algorithm still matches an item in the control's ItemsSource; the control isn't attempting to deduce that the end-user just deleted the auto-completed portion, so the algo shouldn't run.
Re: your comment to akjoshi's response, when you delete the "2", leaving just the "0" in the combo box's text box, the prefix matching algorithm correctly matches the "01" item.
The work-around I used was to only bind to the Text property of the ComboBox and build all view model logic off of that. SelectedItem, SelectedIndex, and SelectedValue are not bound at all.

Related

How to bind dynamically generated TextBoxes to DataGrid values in WPF?

My problem is basically described in the title of this topic.
I have a datagrid which is dynamically filled with various entries:
<DataGrid
x:Name="searchResult"
AutoGenerateColumns="True"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=SearchResult}" />
Next to it is a bunch of TextBoxes, also dynamically generated:
<ItemsControl
x:Name="searchForm"
ItemTemplate="{StaticResource formInputTest}"
ItemsSource="{Binding SearchForm, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
The template I use in the ItemsControl looks basically like this:
<TextBox
Tag="{Binding Index}"
Text="{Binding ElementName=searchResult, Path=SelectedValue.a, UpdateSourceTrigger=PropertyChanged}" />
Now lets asume my datagrid has 2 columns (a and b) and I have 2 TextBoxes with identical names (a and b).
Now I want to select a datagridrow and like the two text boxes to be automatically filled with the values from the corresponding row.
Unfortunately, I don't know how to get this dynamically Binding.
If I bind the TextBox's Text-property statically to searchResult->SelectedValue.a it works fine, but as you can see, only static.
Thats what I've done in the third code-box above.
The ItemsControl, which contains the TextBoxes, has it's binding to "SearchForm". SearchForm has the property "Index", which is the name of the Column.
So, the TextBox knows the name of the column it has to be binded to. To clarify this, I binded the TextBox.Tag to Index, which works fine.
Basically, I want my TextBox-Binding to look like this:
<TextBox
Text="{Binding ElementName=searchResult, Path=SelectedValue.{Binding Index}, UpdateSourceTrigger=PropertyChanged}" />
But, as you might know, a binding inside a binding does not work.
Does anybody know how to bind these to dynamically generated objects together?
Thanks for any hint!
UPDATE 2019/03/07
Thanks dymanoid!
This has got me some progress but I'm still not at the finish line.
I created the view-model property and binded both the Datagrid-SelectedItem and the TextBox-Text property to it.
Since the SelectedItem is a kind of a dictionary (BindablePublicDictionary) I use a converter (IMultiValueConverter) at the TextboxBinding, to convert the dictionary entry to both the index of the textbox and the text/value.
This is working fine, I select a datagrid-row and the 3 dynamically generated inputfields are beeing filled.
Unfortunatelly, if I edit one of these filled textboxes, the other 2 are emptied and my datagrid is unchanged.
I implemented OnPropertyChanged, my bindings are TwoWay and the ConvertBackFunction is called and returns the data in compliance with the targetType Type from the function itself.
Does anybody have experience with the bindings/converter between a datagrid/dictionary and textboxes, especially regarding the convertBack function?

Highlight part of text in textblock not working on all listboxItems

I'm trying to highlight part of text in a textblock from a listbox datatemplate which in turn is bounded to a property of a custom class by using a textbox to search the list for input text. But the problem is that only part of the items are highlighting (most of the ones visible) but when i maximize the window and try to input another character then suddenly all of them gets highlighted my guess where the problem might be is in this piece of code:
ListBoxItem listboxItemFound= (ListBoxItem)this.listBox1.ItemContainerGenerator.ContainerFromItem(TItem);
Since this method is returning a null when the items are not visible but the items are currently in the listbox. Somehow I guess the items listboxItem instances are not yet created until you scroll down or maximize to view more items.
XAML DataTemplate:
<DataTemplate>
<Grid Name="gridOfListbox" Height="25" Margin="0,2">
<DockPanel Name="dockpanelWithTxtBlock">
<TextBlock Name="textbloxk" DockPanel.Dock="Left" FontSize="15" TextAlignment="Center">
<Run Text="" /><Run Background="Yellow" Text="" /><Run Text="{Binding ProductID}" />
</TextBlock>
</DockPanel>
</Grid>
</DataTemplate>
If more code is needed just let me know.
Any help would be greatly appreciated!!
Also if there is any other better way of finding the listboxItem bounded to the custom Item just let me know. Thank you very much!
[Pic of problem] http://i.stack.imgur.com/HViag.png
One way to fix this is to set VirtualizingStackPanel.IsVirtualizing to false for your ListBox. This will cause all of the items to be created right away. The downside to this is if your ListBox has many items, your program will use more memory (since more items will be created), and could potentially run slower depending on the number of items.
A better solution to consider would be to have multiple DataTemplates for this - one without the highlight, and one with. You can set a DataTemplateSelector for your ListBox (using the ItemTemplateSelector property). The selector can choose which template to use based on if the item matches the search term or not.
The tricky part would be writing the template with the highlighted text. You could probably achieve that by having properties on the object the ListBoxItem is bound to for the text before the highlighted text, the highlighted text, and then the remaining text.

How to get rid of double selection thing in ListView (XAML)

I have a ListView and I bind to an ObservableCollection<Folder> and when I hover over a ListViewItem, there is a second selection thing appearing underneath (or ontop of) the item text which prevents me from being able to "activate" the selected item because it doesn't appear to be receiving my click.
As you can probably see, the structure is:
ListView > ItemTemplate > DataTemplate > ListViewItem. But I'm guessing I have that double-selection thing because there are basically 2 "item templates" (DataTemplate and ItemTemplate). But if I get rid of DataTemplate, it throws a runtime error. If I get rid of ItemTemplate, it throws an error. I can't win. How do I get rid of this thing?
Update:
This gives me the desired effect:
<StackPanel x:Name="folderContainer" HorizontalAlignment="Left" VerticalAlignment="Stretch" Width="175" Background="Khaki" Margin="0, 18, 0, 0">
<ListView x:Name="folderList" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding Folder}" BorderBrush="{x:Null}" BorderThickness="0">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="folderItem" Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
With the ListViewItem you where including a specialized wrapper primarily used the ListView to contain unknown item(s) presented by the binding of the ItemsSource when there is default template used. It helps to show that unknown item on the screen, its the purpose of the ListViewItem. For example if someone bound a list of strings, the strings have no xaml style on hover, hence there needs to be a container control to achieve all things graphical in those situations.
Why the two Shadows?
The actual ListViewItem is at its heart (or actually its property), a content control containing what I surmise you had was a textbox control. That is now two controls thanks to the DataTemplate you provided.
Hence you had a wrapper, with a content containing a texbox control. So there are two things which take up space, one is the ListViewItem and the other is the textbox. By hovering over each item, each's style kicked off to show the padding of the control as a selectable region in the Zorder fashion whereas both are seen.
Nothing more nothing less.
Since you knew what is to go into the content control, there was no need to use the ListViewItem wrapper, it was redundant, and you used the actual textbox; hence with only it's style to show the padding of the single control on hover.

ComboBox. Is there a way to display a selected value that isn't in the list

Say I have a ComboBox which has an array of doubles as the ItemsSource. In that array are the numbers "1.0" and "2.5". If I change the SelectedValue to "3.0" the ComboBox goes blank. How do I get the ComboBox to display "3.0" without having to add it to the list of possible values that would appear in the drop-down box?
I guess what I'm really asking is do I need some sort of customised combobox to display an item that is not in the drop down list?
The simplest (but not best) way to achieve your requirements (gathered from the comments because you didn't explain them properly in your question) is to add a TextBlock in front of your ComboBox:
<Grid>
<ComboBox ItemsSource="{Binding Items}" ... />
<TextBlock Text="{Binding Output} Visibility="{Binding IsOutputVisible, Converter=
{StaticResource BooleanToVisbilityConverter}}" />
</Grid>
You could then add a bool IsOutputVisible property to make it visible or hide it whenever you need to... you'd need to use a BooleanToVisbilityConverter to make this work.

Why does my comboBox not allow me to add values to it?

I have this WinRT XAML:
<ComboBox x:Name="comboxGroupName" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Margin="4" Width="200" Height="36" HorizontalAlignment="Left" ></ComboBox>
When I click in it to enter a new value, though, it seems to convert itself into a readonly textbox (it loses its down arrow and disallows any typing into it). What do I need to do to allow adding values into the comboBox? Or do I need to use a separate TextBox to do that (I reckon so, but I'd like to avoid that if reasonably possible)?
It looks like your only option will be to use a seperate TextBox. There is an IsEditable property, but it states:
Gets a value that indicates whether the user can edit text in the text box portion of the ComboBox. This property always returns false.
and the ComboBox Page states:
You populate the ComboBox by adding objects directly to the Items collection or by binding the ItemsSource property to a data source. Items added to the ComboBox are wrapped in ComboBoxItem containers.

Categories

Resources