Enable text searching on DynamicStringValue builtin editor

Grids for WPF Forum

Posted 10 years ago by Evan Phillips
Version: 14.1.0601
Avatar

I am trying to provide the user a more efficient way of navigating to their desired selection in some of our large dropdowns (>2k items). I was able to use some xaml to get the underlying ComboBox to virtualize so the load times are managable, but I havent been able to get the IsTextSearchEnabled property to work. My suspicion is that it is because the items in the ComboBox are somehow wrapped but I am not sure. Here is an example of what I have been trying to do: 

<propgrid:PropertyGrid VirtualizingStackPanel.IsVirtualizing="True" SelectedObject="{Binding SelectedInstance}" >
            <propgrid:PropertyGrid.PropertyEditors>
                <propgrid:PropertyEditor ValueTemplateKey="{x:Static propgrid:BuiltinEditors.DynamicStringValueTemplateKey}" >
                    <propgrid:PropertyEditor.ValueStyles>
                        <propgrid:PropertyEditorStyle Key="{x:Type ComboBox}" >
                            <propgrid:PropertyEditorStyle.Style>
                                <Style TargetType="{x:Type ComboBox}">
                                    <Setter Property="ItemsPanel" Value="{StaticResource virtualStackPanel}" />
                                    <Setter Property="IsTextSearchEnabled" Value="True" />
                                </Style>
                            </propgrid:PropertyEditorStyle.Style>
                        </propgrid:PropertyEditorStyle>
                    </propgrid:PropertyEditor.ValueStyles>
                </propgrid:PropertyEditor>
            </propgrid:PropertyGrid.PropertyEditors>
        </propgrid:PropertyGrid>

 The properties that have TypeConverters of the object that is being set to SelectedObject all return true for GetStandardValuesExclusive, all convert between some other data type and a string representation, and all have their list of values returned by GetStandardValues.

Any ideas as to how I can get this working or how I could otherwise allow my users to search through the dropdown by typing without creating my own custom editor?

Comments (12)

Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Evan,

The default ComboBox in the property editor has this as an ItemsSource:

ItemsSource="{Binding StandardValuesAsStrings, RelativeSource={RelativeSource AncestorType={x:Type propgridPrimitives:IPropertyDataAccessor}}}"

I believe the RelativeSource ends up resolving to a PropertyGridDataAccessorItem and the ItemsSource is therefore PropertyGridDataAccessorItem.StandardValuesAsStrings.  That collection is just an IEnumerable<string>, so there isn't really any wrapping going on.

I think what you might be running into is one of the default triggers on the property editor.  It might be kicking on an IsReadOnly flag on the ComboBox if the data accessor returns IsLimitedToStandardValues as true.  You could either change that return value on the data accessor or clone our value template and take out the portion that does that trigger.


Actipro Software Support

Posted 10 years ago by Evan Phillips
Avatar

Thanks for your response.

I was reluctant to make GetStandardValuesExclusive return false because in my case, the user must be restricted to what is in the list. I had (wrongly) assumed that like other properties in the grid that what the user was typing would not be pushed to the actual property until focus left the combobox. This would lead the user to believe that their entry was valid until they went to edit another property and the error popped up which obviously would be a bad user experience for this sort of thing. Luckily, this was not the case and the property grid shows an error immediately when the user types something that is not in the list.

I did try ticking IsReadOnly to false, but that did not work which still leaves me a little puzzled. Other Comboboxes in my application do not need to be editable in order for IsTextSearchEnabled to work. Regardless, I think I have found a workable solution. Thanks again for your help.

Posted 10 years ago by Evan Phillips
Avatar

Just dicovered a major issue related to changing GetStandardValuesExclusive to return false. If I have a type converted property who's source datatype is not nullable (int, long, etc.) and that property is not currently set to a value that can be successfully converted by the TypeConverter (such as when it is initialized to 0), the Property Grid displays "0" in the ComboBox and does not report an error (no red outline). If I erase that 0 and type 0 again, it shows it as an error correctly because an exception is being thrown in the TypeConverter. This seems to only happen when I have just opened the PropertyGrid.

If GetStandardValuesExclusive returns true in this case the ComboBox is simply empty, no value is shown until the user selects one. This is the most ideal solution but if I can't get it to do that, at the very least the field must show an error when the PropertyGrid is first opened.

Any ideas on how to solve this new issue or to otherwise implement my other request?

Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Evan,

Did you try my other idea of keeping the data accessor as is (no changes from what you originally had) but cloning the value templat and taking out the portion with the trigger that caused the problem?


Actipro Software Support

Posted 10 years ago by Evan Phillips
Avatar

I was a little confused by that other idea. l am not sure what you meant by cloning the value template, I do not have access to your source code so I am not sure how to accomplish what you're saying? I did, as an experiment, try using a style to set IsReadOnly to false the same way I set TextSearching in my example, but that produced the same error I just described where the property shows as "0" without reporting an error. Setting IsEditable to true produces the same as well.

[Modified 10 years ago]

Posted 10 years ago by Evan Phillips
Avatar

I have replicated my issue in a test project if you think that would be helpful. Just tell me how to attach it/send it to you as I don't see a straightforward way to do so.

Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Evan,

Since you don't have WPF Studio, I'll post the template:

	<!-- DynamicStringValueTemplateKey -->
	<DataTemplate x:Key="{x:Static propgridEditors:BuiltinEditors.DynamicStringValueTemplateKey}">
		<Grid x:Name="grid">
			<ComboBox x:Name="comboBox" Margin="0" Padding="0" 
					HorizontalContentAlignment="Left" VerticalContentAlignment="Center" BorderThickness="0"
					Background="Transparent" IsEditable="true"
					ItemsSource="{Binding StandardValuesAsStrings, RelativeSource={RelativeSource AncestorType={x:Type propgridPrimitives:IPropertyDataAccessor}}}" />
			<TextBox x:Name="textBox" Margin="0" Padding="0" 
					BorderThickness="0" Background="Transparent" MaxLines="1" Visibility="Collapsed" />
		</Grid>

		<DataTemplate.Triggers>
			<MultiDataTrigger>
				<MultiDataTrigger.Conditions>
					<Condition Binding="{Binding Visibility, ElementName=comboBox}" Value="Visible" />
					<Condition Binding="{Binding IsLimitedToStandardValues, RelativeSource={RelativeSource AncestorType={x:Type propgridPrimitives:IPropertyDataAccessor}}}"
							Value="false" />
				</MultiDataTrigger.Conditions>
				<Setter TargetName="comboBox" Property="Text"
						Value="{Binding ValueAsString, RelativeSource={RelativeSource AncestorType={x:Type propgridPrimitives:IPropertyDataAccessor}}, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, NotifyOnValidationError=True, Converter={StaticResource NoOpConverter}}" />
			</MultiDataTrigger>
			<MultiDataTrigger>
				<MultiDataTrigger.Conditions>
					<Condition Binding="{Binding Visibility, ElementName=comboBox}" Value="Visible" />
					<Condition Binding="{Binding IsLimitedToStandardValues, RelativeSource={RelativeSource AncestorType={x:Type propgridPrimitives:IPropertyDataAccessor}}}"
							Value="true" />
				</MultiDataTrigger.Conditions>
				<Setter TargetName="comboBox" Property="SelectedItem"
						Value="{Binding ValueAsString, RelativeSource={RelativeSource AncestorType={x:Type propgridPrimitives:IPropertyDataAccessor}}, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, NotifyOnValidationError=True, Converter={StaticResource NoOpConverter}}" />
				<!--<Setter TargetName="comboBox" Property="IsReadOnly" Value="true" />-->
			</MultiDataTrigger>
			<DataTrigger Binding="{Binding Visibility, ElementName=textBox}" Value="Visible">
				<Setter TargetName="textBox" Property="Text"
						Value="{Binding ValueAsString, RelativeSource={RelativeSource AncestorType={x:Type propgridPrimitives:IPropertyDataAccessor}}, Mode=TwoWay, ValidatesOnExceptions=True, ValidatesOnDataErrors=True, NotifyOnValidationError=True, Converter={StaticResource NoOpConverter}}" />
			</DataTrigger>
			<DataTrigger Binding="{Binding HasItems, ElementName=comboBox}" Value="false">
				<Setter TargetName="comboBox" Property="Visibility" Value="Collapsed" />
				<Setter TargetName="textBox" Property="Visibility" Value="Visible" />
			</DataTrigger>
			<DataTrigger Binding="{Binding IsReadOnly, RelativeSource={RelativeSource AncestorType={x:Type propgridPrimitives:IPropertyDataAccessor}}}"
					Value="true">
				<Setter TargetName="comboBox" Property="Visibility" Value="Collapsed" />
				<Setter TargetName="textBox" Property="Visibility" Value="Visible" />
				<Setter TargetName="textBox" Property="IsReadOnly" Value="true" />
				<Setter TargetName="textBox" Property="Foreground" Value="{DynamicResource {x:Static themes:AssetResourceKeys.ControlForegroundDisabledBrushKey}}" />
			</DataTrigger>
			<DataTrigger Binding="{Binding Path=(propgrid:PropertyGrid.IsReadOnly), RelativeSource={RelativeSource Self}}"
					Value="true">
				<Setter TargetName="comboBox" Property="Visibility" Value="Collapsed" />
				<Setter TargetName="textBox" Property="Visibility" Value="Visible" />
				<Setter TargetName="textBox" Property="IsReadOnly" Value="true" />
				<Setter TargetName="textBox" Property="Foreground" Value="{DynamicResource {x:Static themes:AssetResourceKeys.ControlForegroundDisabledBrushKey}}" />
			</DataTrigger>
		</DataTemplate.Triggers>
	</DataTemplate>

I've commented out the Setter that applied IsReadOnly.  If that doesn't work, perhaps try commenting out the entire MultiDataTrigger that contains that commented-out Setter.

If you can't get it working with either of those, please email our support address with your test project.  Be sure to reference this post and rename the .zip file extension so it doesn't get spam blocked.  Thanks!


Actipro Software Support

Posted 10 years ago by Evan Phillips
Avatar

Thank you for providing the template for me. I am happy to say that I was able to get it to compile and it works just how I want it to. The only reference that I could not resolve was the converter "NoOpConverter", which is a StaticResource and is not included in what you posted. I removed it and there were no noticable issues, but I am assuming that I will need that so if you could paste it or tell me how to find it, that would be great. I did notice that doing things this way causes some funky issues with the TypeConverter, null values being passed when the users input is incorrect, but I think I can handle that in my code.

Is there any chance that the ability to disable this trigger will be included in your product? While I am happy to have a workable solution I am less thrilled about having to manage the source code of this editor, I would miss any updates you made because I do not otherwise have access to your source.

 

Thanks,

Evan

[Modified 10 years ago]

Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar

Hi Evan,

That's great to hear.  Out of curiosity, did it work as-posted or did you need to remove the entire MultiDataTrigger?

You can add the NoOpConverter like this:

<shared:NoOpConverter x:Key="NoOpConverter" />

I believe it is used to work around a scenario where two way bindings sometimes wouldn't update in a certain scenario unless a converter was present due to a core WPF optimization.


Actipro Software Support

Posted 10 years ago by Evan Phillips
Avatar

It worked as posted. Thanks again for your help.

Posted 10 years ago by Evan Phillips
Avatar

I apologize for repeatedly marking this as resolved and then unresolving it, but I keep running into subtle issues. I am actually having issues with the new TypeConverter behavior as noted in my previous post, except not about handling nulls. Everything works great if the grid actually calls my TypeConverter, but if the first thing you type is bogus data, the TypeConverter is never called and no error is raised on the grid for the bad data. In fact, it basically does not do anything as the bad data isn't even set on the property. Now the trick is that once you do type something valid, from then on everything seems to work.

As this is getting quite complicated to explain, I will email in a test project that highlights the issue.

Thanks,

Evan

Posted 10 years ago by Evan Phillips
Avatar

Since the last issue turned out to be WPF ComboBox design/limitations I adopted a slightly different solution. First, I went back to returning false from GetStandardValuesExclusive though my convert methods threw exceptions if what the user typed as not in the list so this effectively made it exclusive. Second, I default any non-nullable property that uses a TypeConverter to a value that will properly TypeConvert (in my case just the first value in the list). This sidesteps the issue with ComboBoxes where the binding did not seem to work until a valid value was input first. Together, this gets me text searchable exclusive drop down lists with no apparent binding side effects or TypeConverter weirdness and it behaves exactly the way users would expect it to.

The latest build of this product (v24.1.3) was released 26 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.