I have an interesting data binding question related to combobox. Hope someone has some good suggestion.
I have a simple form, it contains a file picker and a comboxbox. Every time a file is picked, I read a list of strings from the file to a List object and I set comboBox.DataSource = listOfStrings.
In the form load event, I set comboBox.DataBindings.Add("SelectedItem", myObject, "PickedValue");
The purpose is clear: every time a string is selected from the combobox, I want to write the string to myObject.PickedValue.
That is the whole story.
Now I launch the form, rather than go pick a file, I check the combobox first. Of course, at this point, comboBox.DataSource is null, comboBox.SelectedItem is null, too. But the data binding on the comboBox is already setup (since the setting is in form load event). Now my focus cannot be moved from the combobox to anywhere else.
I think the reason is, when I try to check the combobox, it has null as SelectedItem. When I try to move the focus to somewhere else, the data binding of the combobox is triggered. Underlying, it tries to convert the selected item to string and update myObject.PickedValue with that converted string. Since you cannot convert a null to a string, the data binding validation fails, and the validation mechanism doesn't allow my focus to be moved elsewhere and I am sucked at this moment, cannot even move to pick a file.
My question is, what is the normal binding setup work-flow for my application scenario to prevent this trap? What is the correct order of setting up such a data binding so I can check my combobox before its data source is filled by something?
FYI, I tried to bind myObject.PickedValue to SelectedText property of the combobox (I noticed that SelectedText is a string and never be null, even when SelectedItem is null). But interestingly, even if I select something from the combobox, SelectedText is still empty string when data binding is triggered. What's wrong here?
Thanks for any help.
The failure is a little simpler than you describe: Your ComboBox will fail just because there is no selected item, because there's nothing to select from.
I would just disable the ComboBox if there's nothing to select from. It's pretty easy to do. Remember to hook up a PropertyChanged event in your data object; the binding source will find it automatically with reflection.
class MyData
{
public event PropertyChangedEventHandler PropertyChanged;
// ...
public HasListOfStrings { get { return ListOfStrings != null && 0 < ListOfStrings.Count; } }
private void LoadListOfStrings
{
// ... load the list of strings ...
if ( PropertyChanged) {
PropertyChanged(this, "ListOfStrings");
PropertyChanged(this, "HasListOfStrings");
}
}
}
In the designer, bind the 'Enabled' property of the 'ComboBox' to the HasListOfStrings property. You can do it in code with:
listOfStringsComboBox.Bindings.Add ("Enabled", bindingSource, "HasListOfStrings");
I also recommend you change the AutoValidate property of the container (or container's container) to EnableAllowFocusChange.
This doesn't seem right; it should be possible to set a string property to null. Possibly the focus problem lies elsewhere. Have you tried setting a breakpoint on your property setter to confirm your theory?
The SelectedText property of a combo box refers to text that has been selected in the text portion of the combobox. This only works if the dropdown style is set to combo. Basically it's the selected text of the text box portion of the combo control (the reason a combobox is called "combo" is because it is a combination of a textbox and a selection list). You would ordinarily expect this property to be empty unless the user was editing the text portion of the combo.
If you want a workaround for this problem that is consistent with a good user experience, try disabling the combo box on form load, then enabling it when a file is picked.
Related
i have a lot of trouble with my CUIT. I want to test my window with data from database. The window is so constructed that some fields are deactivated so that the user can not change the values. The ComboBox gets a value, but in CUIT I can not read this value, because the control is not enabled. That's why I can not read properties of control right, SelectedIndex is f.e. always -1. Is there any way to find the text of ComboBox?
if you are working with WpfComboBox, you can obtain all items of the combobox by getting the property Items. It returns you basically UITestControlCollection and you can enumerate through its elements:
foreach (UITestControl uiTestControl in yourUiTestControlCollection)
{
string name = uiTestControl.FriendlyName;
}
or similarly by using lambda-expressions.
In this case it does not matter which item is selected and in which state (Enabled = true/false;) the combobox is.
However, it might not help if your combobox loads all the items somehow dynamically when changing its state to enabled. Then you will have to find another test scenario to make the combobox enabled before you analyze it with your test method.
Hope it will help.
listBox_groupmakingrepairs is a listbox control on my form. When first loading the form, nothing is selected in the listbox and the code in the following 'if' statement will run because the condition is true.
if (listBox_groupmakingrepairs.Text == "")
{
Error = "You must indicate what group will be making the repairs.";
Con = listBox_groupmakingrepairs;
}
But, if I run the following code...
listBox_groupmakingrepairs.Text = "Cell";
Followed by this code...
listBox_groupmakingrepairs.ClearSelected();
The listbox will not have anything selected, yet it will cause the first code snippet above to be false and not run the code in the 'if' block. When I step through and check the value of 'listBox_groupmakingrepairs.Text' it is "Cell". Yet on the form, the listbox clearly has nothing selected.
Am I using the Text property of the Listbox control incorrectly? Or is this a bug? Or am I missing something altogether obvious?
The way I see it I have a property (Text) that seems to work well most of the time. But under certain conditions it returns a value that isn't the correct value anymore. Why is the Text property returning an old value that has since changed? Does this make logical sense to anyone who can explain it to me?
That is because the ListBox control of C# winform control handles both the Text property and the SelectedItem separately, but in a way together.
It works in the following way:
IF [SelectedItem is True i.e. Item is selected] THEN
Text property = SelectedItem.ToString()
ELSE IF [SelectedItem is False i.e. No Item is selected] THEN
Text Property will still assume its current value (or in a way previous value) unless its manually reset
So always in such cases donot forget resetting the Text property. Hope this helps...
Maybe I am a bit slow, but eventually I realized that Microsoft is the creator, so to them I will go for the answer.
Here is what I found:
According to Microsoft, the following code I was using is a correct way of selecting text in a listbox control.
listBox_groupmakingrepairs.Text = "Cell";
Then, also according to Microsoft, the following code I was using is a correct way of deselecting all items in a listbox.
listBox_groupmakingrepairs.ClearSelected();
Finally, also according to Bill Gates (see two links above) the Text property is also a correct way to retrieve the text of the first selected item.
string SelectedText = listBox_groupmakingrepairs.Text;
But, as I described in my original question above, when I evaluated the Text property WHEN NOTHING WAS SELECTED IN THE LISTBOX, it contained text. But Microsoft says "this property returns the text of the first selected item." WHICH IS FALSE. It should have returned null because there was nothing selected...yet it returned text which was NOT SELECTED.
So to answer my two questions in my original post:
"Am I using the Text property of the Listbox control incorrectly?" -- The answer is YES.
"Or is this a bug?" -- Yes, it is.
I have the code below
FooCB.DisplayMember = "FooNome";
FooCB.ValueMember = "Foo";
FooCB.DataSource = FooRepository.Instance.All();
FooCB.DataBindings.Add("SelectedItem", Bar, "Foo");
but when I display the form the SelectedItem is always the first.
What am I doing wrong?
I have been struggling a little with the behaviour of Winforms comboboxes and databinding recently and these are my observations (.Net4) when binding the ComboBox.DataSource to a list of items and also binding an object property to ComboBox.SelectedItem.
When binding a list of objects (in your case List<Foo>) to ComboBox.DataSource, the first object in the list is always shown in the combobox.
If you bind an object property to ComboBox.SelectedItem (in your case Bar.Foo) and that object property matches one of the combobox list objects then that object is displayed in the combobox. If the object property is null (Bar.Foo == null) or the object property is not in the combobox list then the first object is shown in the combobox.
Setting ComboBox.SelectedItem = null or ComboBox.SelectedIndex = -1 clears the displayed item on the combobox even though this seems to warn against it. And will set your bound object property to null.
If a user clears the combobox selection when using ComboBox.DropDownStyle == DropDown (with backspace) then the bound object property is set to null.
If you have implemented INotifyPropertyChanged on the object whose property is bound to Combobox.SelectedItem (Bar.Foo) and you programatically set the bound property to a value and that value appears in the combobox list then the changed value will be displayed. If you set the property to null or a value not in the list then the combobox displayed value will not change.
So what can you do about it? The only real issue I have is having no value displayed when my bound property is null so I have just been explicitly setting Combobox.SelectedItem = null as in point #3. You may be able to extend ComboBox and override the default behaviour but so far I have been content with an extra line of code here and there combined with using default values on non-nullable properties.
Probably you are missing some decleration. If you created the Combobox from the Tool Box, -I had the similar problem- you might want to add name of the Combobox's Name as a tag on XAML.
Other than that, if you created it dynamically by code, check if you are missing any decleration for class.
I can't tell from the OP's code whether I'm answering their question, but maybe this will help somebody reading this question. The ComboBox has four ways to set the current value:
SelectedIndex
SelectedItem
SelectedText
SelectedValue
You need to be consistent about what you're setting (and about which event handler you're using). You'll get an error if you set SelectedIndex to something dumb (less than -1 or longer than the list). However, you don't get errors setting the other three to something that doesn't exist for that type of selection.
Suppose you use a Dictionary (that's pseudo code) as a binding source and set DisplayMember = "Value" and ValueMember = "Key", the mapping would look like:
SelectedIndex - -1 to index of last item
SelectedItem - KeyValuePair<Key, Value>
SelectedText - Dictionary value
SelectedValue - Dictionary key
Supplying either value or key to SelectedItem will not generate an error, it will simply act like the OP has described. And that's why I thought this answer might help somebody.
I might also note that, if you're swapping out the contents of the ComboBox, it's not always safe to use SelectedIndex. Suppose the same basic kind of data is in the ComboBox, but selections are limited in some cases compared to others. Using SelectedIndex to persist a previous selection that was still valid in the new list of options is only going work if that previous selection held the exact same place in the list. You'd almost think this was the voice of very, very recent experience speaking...
I need to track current selected item in ListBox to turn off some other controls on the form when selected item become null. I try to use SelectedIndexChanged event, but it not raise when selected item is null.
Can you please advise something?
UPDATE: Selected item becomes null because i set new DataSource value with empty collection. May be it's a reason of my problem?
I need to explain. ListBox represent collection of items from database. When user add/edit/delete some item, I refresh listbox by calling this method:
private void RefreshList()
{
lbParts.DataSource = this.database.Fetch<part>(string.Empty);
}
It fetched all items from database, convert it to List<part> collection and set as ListBox DataSource.
That is incorrect. SelectedIndexChanged is raised when SelectedItem becomes null. In this case, SelectedIndex will be -1.
EDIT: you are indeed correct that when you change DataSource, you don't get SelectedIndexChanged. I would recommend explicitly setting SelectedIndex=-1 immediately before anytime you change DataSource
Perhaps handle the DataSourceChanged event as well? If the choices available in the listbox change, then I assume this is reason to perform a refresh on the forms avialable controls?
I'm working on a custom control that internally uses a ComboBox.
My problem is when the ComboBox is focused and has the drop-down open, it appears to focus the entire control. I would like to automatically highlight the first item in the drop drown, but right now you have to push the Down key to do so.
Is there a way to programmatically Highlight the first item in a ComboBox (set the readonly IsHighlighted property to true)? I believe the concept of IsHighlight within a ComboBox is different than Focus. Also, I am binding via ItemsSource, so I have no reference to ComboBoxItems.
Here's a way of doing it, although it might not cover all the cases - but you didn't provide too many details (for example, what happens when there is already an element selected? Do you still want to select the first element in the list? The code below will highlight the first element only when there is no selection in the combobox. To make it always select the first element, the DropDownOpened event should be handled too).
public MainWindow()
{
InitializeComponent();
combobox.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);
}
void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (combobox.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
(combobox.ItemContainerGenerator.ContainerFromIndex(0) as ComboBoxItem).Focus();
}
}
(Hope I understood correctly and this is what you want to do).
It might not be what you are looking for but if you set mycombo.SelectedIndex = 0 then mycombo.IsDropDownOpen = True it should open it up and have the first item selected. It will be highlighted but will also be the value in the combobox as well. I'm not sure if this is not the desired effect though..