Friends,
I have a WPF Combobox. When the Combobox is opened, I have the items dynamically generated based upon environment variables. So basically a combobox that is bound to a list that is dynamically changing.
Everything works as expected until I Exit the combobox with the dropdown open to enter another control(another combobox).
When I reopen the first combobox, the items appear to be frozen and no longer bound to the list when INDEED the list is changing and is still bound. Its almost like the binding broke.
When this event occurs, I have attempted to forcefully add items, and that doesn't work either. I can see in the code behind that the combobox now contains the additional items, yet it doesn't appear that contain them in the UI.
What is this black magic? Any way to prevent it? is this some type of Stuck focus issue? Maybe the dropdown isn't re-sizing?
I think i have narrowed it down to the physical dropdown is not re-sizing to the new items.
EDIT*
The controls are dynamically generating, so I have no real hard code to show you other than this.
private void CBControl_DropDownOpened(object sender, EventArgs e)
{
((ComboBox)sender).Items.Add("Option");
}
On this event, I will add an items to the combobox, although the items ARE being added to the list, they are not displayed in the UI.
EDIT 2*
I figured it out, so i have 2 comboboxes, it appears that the 2nd was steeling and holding the focus INSIDE the dropdown. (odd bug)
in order to fix it i needed to release it bu changing the index of the 2nd combo box WHILE it is open.
int sel = ComboBoxTwo.SelectedIndex;
ComboBoxTwo.IsDropDownOpen = true;
ComboBoxTwo.SelectedIndex = -1;
ComboBoxTwo.IsDropDownOpen = true;
ComboBoxTwo.SelectedIndex = sel;
and I had to manage the unintended recursive call.
So, here it is.
You got 2 ComboBoxes. If you Open one then directly click into another, the focus is moved to the second combobox dropdown.
If you make any changes to the first combobox, the changes will not take effect to the style(dropdown resize) UNTIL you release the focus from the 2nd combo box dropdown item. Although the items change will take effect, the resize wont.
to release the focus, you need to open the second combox and change the selected index like so:
int sel = ComboBoxTwo.SelectedIndex;
ComboBoxTwo.IsDropDownOpen = true;
ComboBoxTwo.SelectedIndex = -1;
ComboBoxTwo.IsDropDownOpen = true;
ComboBoxTwo.SelectedIndex = sel;
10 hours of debugging. 5 hours of research. First solution I have found. It may be dirty, but its all I can find.
Related
I have a Windows form with a series of list boxes
The contents of subsequent lists change based on choices in earlier ones
All the boxes are bound to a BindingList with OnListChanged enabled
I use this to retain user-specified 'checked' items when elements are added or removed
However, I note that if I add an item to the list, it appears to update the UI only after all the other events have been fired
I've looked at the events I would expect to fire on the CheckedListBox, and in the related ViewModel, in order to catch the one which adds an item to the list, but so far without success
Can someone please advise me which event would allow me to call my 'CheckBoxes' method after the UI has been updated, otherwise they all get set to blank again until the form is closed and opened
private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
// I've removed the debug statements, but this event when the bound list updates is fired before the UI updates
}
private void teams_checked_list_box_SelectedIndexChanged(object sender, EventArgs e)
{
// same with this event, and the other events like SizeChanged
}
EDITED: On reflection, I realise that the problem occurs because I am not setting the true / false checked flag in the binding, because I couldn't figure out how to do it. If someone could point me in the right direction? Code currently looks like this:
teams_checked_list_box.DataSource = Globals.ThisAddIn.TFSTeamsViewModel.ListOfTeamsFromVM.value;
teams_checked_list_box.DisplayMember = "name";
So basically I am only updating the item name, and the check flag is handled on a later pass
How are you adding items to your list? Are you re-binding the listbox? There are basically two ways you can do this depending on your method of adding items:
If you are re-binding, capture the event BEFORE the item is added. Loop through the CheckedItems property of your list box. Save the values. Add your item. Loop through the NEW values and recheck.
OR
When the user checks the item in the listbox, capture that using the ItemChecked event and then store the value of the item somewhere. (Variable, hidden textbox -- ugly, but it works -- etc.) If you are only adding items to the BOTTOM of your list, you can store the index of the item. If you are re-sorting your list, you'll need to store a unique id to reference the item. (Note: you will also need to REMOVE the checked items from your stored value if the user UN-checks an item in your listbox.) Then, after you add your items to the listbox, loop back through and set the checked value on the appropriate listbox items.
I have a VB6 application that has been running for quite sometime. Currently I'm trying to update one of the form that has a combobox 2.0. Because the combobox is populated with hundreds of items - I'm trying to update it so that users are able to click on a look up button next to it, where another window opens up with all the items from the combobox. User will be able to search by keyword and/or select an item and double click on it and have it appear in the combobox. The issue I'm having is with trying to pass no value or "" when CANCEL is clicked. I'm able to pass the value if I in the properties window my STYLE Is set to COMBO rather than list. However, the issue I come across is that with COMBO the value (text) in the combobox sometimes is not aligned properly. Is there a way to pass a "" value to a combobox 2.0 without changing the style to COMBO?
If they hit cancel set the ListIndex of your combo to -1 rather than setting the text property. This is the value for no item being selected.
I have a ComboBox implemented with an auto-completion system. My ComboBox contains more than 100 items. When users are typing text in, the auto-completion system opens the dropdown list and highlights the most relevant item. Moreover, when the dropdown list is expanded, all items are available (no filters). But the most relevant item is always at the bottom of the dropdown list.
I would like it to be in the middle, if possible. One item can have the same reference but another type than another one, that's why I need to see most of them in my dropdown by placing them in the middle.
Any idea ? It's not really important but kind of useful for them. Thanks !
Update :
Here's my ComboBox with the open dropdown. Sorry about that, I had to blur its elements. As you can see, the user starts writting the reference in the ComboBox. The autocompletion works fine, but the corresponding item is found at the end of the dropdown list (in the red frame), almost out of bounds.
I wish it would be highlighted in the middle of my dropdown list instead of so far below.
Your item search may work well, but your list isn't visually filtered, which means it's size always remains the same.
It's scrolled into view, by the wpf system, but still displaying all other items around the relevant one. The reason why it's at the bottom is because wpf Scrollviewer just finished scrolling the item into view and sees no need to scroll it further into the middle.
You could use the CollectionViewSource class. Why ?
It's simple to use, will keep your viewmodel data as it is, and you would have your relevant completion item at the top. It can be obtained by GetDefaultView(..)
Let's say you have a viewmodel flag "IsHidden", stating that it's content does not match the user input:
ICollectionView cv= CollectionViewSource.GetDefaultView(myComboBox.ItemsSource);
// switch filter on
cv.Filter = obj => (obj as myViewModel).IsHidden == false;
// switch off
cv.Filter = null
I am having some trouble with my comboBox logic in my viewModel. The viewModel populates the comboBox and the user can select items.
When an item is selected, some editable info appears in the view and they can make changes. Now, I want to prompt the user to save if changes were made and not saved and they are trying to select another item in the drop down (a "Save?" yes/no/cancel messageBox).
Right now I need to remember the previously selected item and handle this myself by resetting the selected item if the user selects cancel. Since the comboBox does not have a PreviewSelectionChanged event, this is the only way I can think of of handling this scenario. It works but it gets a little messy hwen there are multiple comboBoxes, etc.
Am I missing anything or is this just the way it needs to be done?
You essentially have to make a flag in your view model called isDirty or something along those lines.
public class EditorViewModel
{
private bool _isDirty = false;
private long _editableProperty;
public long EditableProperty
{
get { return _editableProperty; }
set
{
_editableProperty = value;
// We've detected a change so mark this view model as dirty.
_isDirty = true;
}
}
}
Note that you will have to have to jump through a few more hoops if you want ensure that the data is in fact different from your original. So say someone accidently adds a space in EditableProperty and removes it your view model will think it's dirty and prompt the user.
The Windows Forms ComboBox provided a SelectionChangeCommitted event, but for the WPF ComboBox control you correct in that there is no event that will notify before the selection change occurs that will provide you with a means of cancelling the event.
If you are going to take a change tracking/editable approach, I would recommend considering implementing IChangeTracking and IEditableObject on the items in your combobox items source.
You will probably have to handle the SelectionChanged event, inspect the removed items to determine if the item that was previously selected was modified and then display a dialog requesting confirmation. If no/cancel was indicated, you can then set the selected index back to that of the previously selected item.
What about making the Editable item a copy of an item instead of the actual item?
So your ViewModel would contain
ObservableCollection<MyModel> ComboBoxItems;
int SelectedComboBoxIndex;
MyModel EditingItem;
Whenever the PropertyChange event occurs on SelectedComboBoxIndex, you check and see if EditingItem is null or not. If it is null, it means you're safe to switch and you set
EditingItem = ComboBoxItem[SelectedComboBoxIndex]).Copy();
If the EditingItem is not null, then you throw up a prompt asking if the user wants to save changes or cancel.
When the user hits Save, it takes the EditingItem and applies the changes to the data store and updates the item in the ComboBoxItems list.
If they hit Cancel, the EditingItem is simply discarded.
I'm wondering if I can make fake sections in the popup menu:
The rule would be, if the 5th character of the displayed item is different from the 5th char of the previous item in the menu, it has to be highlighted
What do you think?
Thanks!
To achieve this would be a hack.
Normally the items that appear in the popup part of a combo box will be an instantiated data template, and each gets its own data item and has no clue or knowledge of the other items in the list, so you couldn't use a converter or anything else to achieve this behavior.
What you could do though is inject (attach) your own control into the popup part of the combo box, and take over the rendering of the data items. How you do this will depend upon which combo box you are using (i.e. MS or some other vendor's) and would be a whole new question.
Would that be easier if I were to create my own combobox as follow:
a TextBox associated with a Button that when pushed would popup a datagrid in which I could implement this conditional formatting?