Bind a SortedList to ListBox WPF - wpf

I'm converting an older .NET WinForms app to WPF. It uses a SortedList:
Dim MyCol as New System.Collections.SortedList(New CaseInsensitiveComparer)
It fills a ListBox in the form by iterating the list, i.e. no data binding.
For Each p In MyCol.Values
lstBox.Items.Add(p.Name, False)
Next
It's nice because the items appear in the list sorted. Also, because it contains Key,Value pairs, you can instantly get a desired object from the list, or see if it is in the list at all:
If MyCol.Contains("the key") ...
myObject = MyCol("the key")
Debug.Print(myObject.Name)
I do this a LOT in the app, so the 'random access' nature of the SortedList is very important for performance.
So, fast forward to WPF, I need to bind my ListBox to MyCol, but would like to keep it as a SortedList, if possible. How can I bind it?
Edit: To clarify further, I am trying to do it right and want to use MVVM. And ideally I'd bind an ObservableCollection(of myClass) to the ListBox, and use Linq to sort and to query for a particular item. But then I've lost the 'random access' that I had with the SortedList, and I fear this would be a significant performance hit. But I haven't been able to figure out how to convert the SortedList to an ObservableCollection (I'm good, but not super-advanced), so I posted this question to explore if it's possible to stick with the SortedList first, before abandoning it for something else.
What I ended up doing, at least for starters:
In my ViewModel, I have a dependency property of type System.Collections.ICollection. Let's call it MyColProperty.
Once MyCol is populated, I assign it to the property like this. (SortedList.Values is of type System.Collections.ICollection).
MyColProperty = MyCol.Values
In the XAML, the ListBox binds to it like this:
<ListBox ItemsSource="{Binding MyColProperty}" DisplayMemberPath="Name"></ListBox>
And that works to display the Name property of my objects in the MyCol SortedList. So it's sort-of MVVM, though it's not an observable collection. I'll have to see how far I can take it with that short-coming, and then make a decision whether to switch to a true ObservableCollection, or if this will do.
Thanks very much, and hope it helps someone who comes across it.
Sandra

one way would be to simply set the SortedList as ItemsSource for the WPF ListBox and use the Name Property as DisplayMemberPath.
lstBox.ItemsSource = MyCol;
lstBox.DisplayMemberPath = "Name";
nervertheless you should also look into MVVM and Binding when doing WPF now :)

Make your collection MyCol as a collection of key and value, maybe something like,
new List<tuple<string,string>>()
Then bind your listBox to this collection with DisplayMemberPath as Item2 and use Item one as your key for searching etc.
Also you can use LINQ to sort your list,
myCol.OrderBy(p => p.Item2)

Related

Bind SelectedItem as ItemsSource's binding's not completed

I've run into an issue I was surprised I couldn't find any discussion about (except WPF MVVM ComboBox SelectedItem or SelectedValue not working maybe).
I have a MVVM form that has 2 ctors, one is for "new item creation", the other is for "item modification". I have a combobox that represents one of the item's properties.
In the modification ctor, the property bound to ItemsSource is initialized, and then the property bound to SelectedItem is set. But nothing is selected in the UI, unless I delay (even a tiny bit) the SelectedItem set.
How can I solve this ? I decently can't keep a timer with a totally random interval to fix the issue :D
Thank you for your help
It seems like the elegant way to ensure the ItemsSource is initialized before I set the SelectedItem from VM is to have the binding source of ItemsSource (whatever it is) declared in my view resources.
I'm sure someone can lead me to the light now that I've pointed that out.
I have tried with a CollectionViewSource but didn't find the way to use its Filter capability without breaking the MVVM pattern. Plus I don't know how to re-raise the Filter as I used to with ICollectionView.Filter (filtered-out items depend on another combobox selection, nothing really fanciful imo).
Perhaps the resource declared in the view and used as ItemsSource has not necessarily to be a CVS, I'm looking for suggestions here.
--Edit--
I found out that the IsSynchronizedWithCurrentItem="True" solution spread all over the web is actually working. I was mislead because it didn't fix my problem at first try due to a remaining SelectedValuePath that wasn't used anymore on my control.
public MyViewModel()
{
this.Items = ...;
//this.SelectedItem = ...;
// select in separate message so that the ItemsSource has definitely been set
this.Dispatcher.BeginInvoke(delegate
{
this.SelectedItem = ...;
});
}

Problem with filtering DataGrid

I have bound my DataGrid to a DataTable and only few of the details are displayed in the grid. When I wanted to filter the DataGrid I created a View with my DataGrid's ItemsSource.
Code:
Dim myView As ICollectionView = CollectionViewSource.GetDefaultView(MyDGrid.ItemsSource)
myView.Filter = New Predicate(Of Object)(AddressOf filterFunc1)
Now When I do the search, the non-displayed fields are also included in the search.
Public Function filterFunc1(ByVal obj As Object) As Boolean
Dim filStr As String = "*" & TextBox1.Text & "*"
For Each item As String In obj.Row.ItemArray
**If item.ToLower Like filStr.ToLower Then**
Return True
End If
Next
Return False
End Function
Also I Have ComboBox fields in the DataGrid which are loaded separately from other DataTable's. Now I cant Include them in the search.
A screenshot from my App:
So how do I make a search that includes only the Text from Displayed part.
EDIT: Also how do I skip searching the null valued fileds? 'cause thats causing an exception in my case.
Well then...
Your question is pretty disjointed and I can't understand all of it - maybe that's why you didn't get an answer so far. Skipping null fields is simply a matter of adding a new condition in filterFunc1 - if Convert.IsDBNull(item) then continue for (assuming item is a field in a DataRow, of course).
However, this programming style is pretty foggy and I'd recommend at the very least being more clear on which columns you filter, and the types of objects in the columns. A much better approach would be to map the data you're getting from the database to actual objects in your application - that allows for more type-safe programming. I think the main problem here is that nobody can really tell what's going on there from a few lines of code because nobody can make any assumptions about what kind of objects are there.
About the items in the ComboBox, no idea what kind of difficulties you're having, you might want to clear that up a bit.
you could maintain, instead of simply strings, structures containing both captions and IDs, like say
public class YourComboItem
public property Id as string [get/set]
public property Title as string [get/set]
end class
Then bind your ComboBox's ItemsSource to a collection of these items retrieved from the database, and set DisplayMemberPath to Title and ValueMemberPath to Id. Then you can use the ComboBox's SelectedValue to get the selected ID. As you can see, having objects instead of raw data structures can have quite some advantages.
Of course, I described getting the SelectedValue directly from the ComboBox, while a much better architecture would be MVVM, with the ViewModel containing an ObservableCollection(Of YourComboItem) and the ComboBox's ItemSource bound to it with an actual binding. Then you can also bind the SelectedItem to a property in your ViewModel, and have the item as a whole, including both Id and Title, to work with without knowing anything about your user interface. Alternatively you could have an ICollectionView generated from the collection of items and bind the ItemsSource to that, then you'd have the selected item in the ICollectionView's CurrentItem property.
I'd really recommend reading up on MVVM (Model-View-ViewModel) to make your work with WPF a whole lot easier.

Can MethodParameters in an ObjectDataProvider be databound to other controls?

I've been exploring WPF and XAML for a while now, but have hit a slight stumbling block revolving around binding to a method.
My situation is:
There is a ComboBox bound to a DataTable. There is a ListBox bound to the return value of a method (GetDates) via an ObjectDataProvider. One of the input parameters of the method GetDates is an Id stored in the ComboBox/DataTable.
How can I bind a MethodParameter in the ObjectDataProvider to a particular value of the SelectedItem of a ComboBox (in this case, the SelectedItem is of type DataRowView)? Alternatively, am I missing a better way of solving this problem?
I can see ways out of it by using the code-behind, but I'd like to know if there's a more XAML-y solution. It's always useful to pick up little tips and tricks, even if it turns out not to be the best fix to this problem.
http://msdn.microsoft.com/en-us/library/system.windows.data.objectdataprovider.methodparameters.aspx
This seems to describe what I need - although it's actually trying to answer a different problem.
(Aside: Is it just me or is that example on MSDN trying to do too much all at once?)
By binding the ItemsSource of the ComboBox to a DataTable, and the SelectedItem of the ComboBox to a MethodParameter (with a converter to extract the value I need from the DataRowView), the ObjectDataProvider will have the parameter it needs.
It would probably be easier to read/follow/maintain if I just hooked into the ComboBox.SelectionChanged event.

MVVM selecting a treeview item programmatically

It seems that others have had variations on this question, but from what I can tell it hasnt been addressed for folks using collections in a single view model.
I have a VM that contains an ObservableCollection of objects, not a VM for each object. Therefore I dont think I can use the SelectedItem bool that is often discussed because I dont think I can bind to the property on the collection's objects...just the properties on the VM.
So I've got the whole thing pretty well written with no code-behind and minimal coupling, but when a new item is added to the collection, which is bound to the treeView, I need to select that item.
Ideas?
Thanks!
When thinking about this. You should really build a wrapper for every element of the tree view that has the IsSelected bool on it as well as the IsExpanded bool they make life so much easier for displaying the data. You could even just add them to your class and use them from there.
Josh Smith has an article on CodeProject where he suggests creating a ViewModel object to represent each node of the TreeView, and then autowires them up as needed.
http://www.codeproject.com/KB/WPF/TreeViewWithViewModel.aspx

(WPF) Binding two classes to a control

I have two viewmodels. One which displays a collection of IPAddresses, and one which displays a collection of objects that has numerous parameters. One of these parameters is an IPAddress. So, I have another panel that binds to the properties of the second object. I would like a combobox to have the ItemSource set to the first object, but the selected item bound to the second object. However, I can only seem to set one datacontext on a control in code behind. Is there any way around this? I would prefer to do this all in code behind if possible (i find the xaml programming to be non-ideal at best), but I'll take anything.
For the ComboBox bind the collection of IPAddresses to the ItemsSource property, and bind the SelectedItem of the ComboBox to the IPAddress property of the SelectedItem of the collection of "numerous property objects".
This would be easier to answer if I had a better description of your objects including names. But it sounds to me like you should make a dictionary with the ipaddress as the key and the second object as the value.
If you can do that then you can bind to it in code like so:
comboBox.ItemsSource = dictionary;
comboBox.DisplayMemberPath = "Key";
comboBox.SelectedValuePath = "Value";
This is assuming that you have exactly one "second object" for every IPAddress in the collection. Which sounds about right given your description.
Take a look at the Source property of Bindings. It is basically a DataContext for a specific binding. It should make what you're trying to do very simple, especially in code behind.

Resources