How do I get Count of parent collection from child? - c#

I've got an ObservableCollection assigned to ItemsSource of a listbox. Listbox is using a DataTemplate which has a usercontrol in it which has items bound to each listboxitem's properties.
I have an up and down button on the usercontrol which moves an item up or down the list. The list is sorted by the property that I'm changing. Click up or down, the DisplayOrder property is changed, I'm using INotifyProperty to tell the ObservableCollection it needs to re-sort.
What is the best way for the usercontrol to get the item count so that I can disable the down button when an item reaches the bottom of the list. (The top is easy, I compare to 0)

I see two ways of handling this.
The first is to pass a handle of your collection to each of your items (when they get added to the collection) so that they can calculate if they are the first or last item themselves.
The other is to expose writable properties on your items, such as CanGoUp and CanGoDown, and your parent control becomes responsible for setting these properties properly. I prefer this solution because it decouples the behavior of your parent list, from the child items. Even though the up/down buttons are placed on your child items, it's really a functionality of the parent list.

listBox1.Items.Count ?
this.Parent.Controls.Count?

Related

Listbox with checkboxes - code seeing every item IsChecked = true

I have a WPF Listbox which contains a list of checkboxes which are all named as the names of other controls in another window.
When the listbox is looped by grabbing each item in lst_control.Items:
_details.controlIDs.Clear();
foreach(Control item in lst_controls.Items)
{
if (item.IsChecked)
//Add item to list
_details.controlIDs.Add(item.controlID);
}
The code is fired on a check/uncheck of any of the checkboxes within the listbox. It sees each item.IsChecked as true - even if it is unchecked.
EG: Check the top box in the list, it sees it as IsChecked = true, but it also does for every other control in the list.
Weird behavior - has anyone seen this before?
Even if you're not going to go the full MVVM route, this issue is best resolved by separating the UI and the data layers in your application.
Make a data item class that includes an IsSelected boolean property, and set the ListBox's ItemsSource to a collection (eg ObservableCollection) of these items. For two way data binding, the data item class should implement INotifyPropertyChanged.
In your UI, make a DataTemplate for the ListBox's ItemTemplate property, that includes a CheckBox that is bound to IsSelected.
That way you can scan the collection of data items (using Linq or otherwise) to find those that are selected.

Track changes inside the collection of complex objects

I have a collection of objects "SourceItemCollection" used to populate the ListBox with checkboxes. Each item of the collection consists of two fields, Item and IsChecked (I've created a small class for this combined items). I would like to track all changes when user selects or deselects something from the collection (not when the button is pressed or something else, but "on-sight"). For this I would like to use another collection "SelectedItems" which will consist only of Items, without IsChecked property (I create and would like to use this collection outside the abovementioned small class of the source collection's items).
The tricky thing is that "SourceItemCollection" doesn't change itself, it always stays the same, changes only the IsChecked property of each item. I do get a notification each time I tick or untick something, but I get it inside the small class of my combined items and I can't access my SelectedItems collection from there.

How to get the updated selected item in WPF?

I had to refresh the items of a TreeView as some of the changes made were not reflected in the code behind. There are about 15 parent items and each parent has around 50 - 100 child items.
treeView.Items.Refresh();
treeview.UpdateLayout();
This is called every time a search is made on the tree (textbox search).
Will it affect the performance?
EDIT :
I set the selected item of the tree in the ViewModel. Somehow for some cases(random), even after setting a new selected item, the change is not reflected in the view.
treeView.SelectedItem - remembers the previously selected item and returns the previous selection. It returns the new selection only if I call UpdateLayout() on the tree before I get the SelectedItem. This in turn affects the performance. What can I do to get the updated SelectedItem.

Is binding an ItemsControl to an ObservableCollection<T> more efficient than a List<T>?

I have a render-heavy item template in an ItemsControl and want to minimize the recreation of child item templates when ItemsSource signals a change. I am wondering if, because ObservableCollection can tell WPF precisely what has changed (as opposed to just the whole list), if it is more efficient in rendering changes to the collection, or if WPF is smart enough to reuse previous item views if it detects the same item is still in the changed list.
Your suspicion is correct. WPF will not reuse previous views. If you replace the ItemsSource of an ItemsControl with a new List, it will create completely new views for each item in the list, even if the same items were in the old list.
You can test this yourself by putting a custom control in the ItemTemplate and adding a breakpoint or debug logging to its constructor. If you replace the ItemsSource with an identical list, you will see your control constructed once for each item in the list. On the other hand, when an item is added to an ObservableCollection you will only see it called once.
Note that the ItemsControl can reuse the container (such as ListBoxItem) if you are using a virtualizing panel and have container recycling enabled. See Link. It still can't reuse the contents of the container, however.
ObservableCollection only informs of addition and removal of objects - so perhaps not as precise as what you were expecting (if an object within the list changes, ObservableCollection will not fire off any notifications).

How do I cache TreeViewItems to re-expand after a re-binding?

I have a TreeView that is bound to a collection class that I have no control over. Inside this class is a collection of objects, which each have their own collection of items. This hierarchy is fixed at 3 deep. The children of the TreeView are contained in an ObservableCollection and are updated in the TreeView accordingly. The collections inside each of these objects are not observable, and thus I have to manually re-bind the data to the TreeView each time I add an object to one of the children. This causes all of the expanded children to be reset to an unexpanded state. I am trying to cache the objects that were expanded so they can be re-expanded after re-binding. The children work as I would expect, however when I try to expand a grandchild of the TreeView I get a null object.
To get a TreeViewItem I use the ItemContainerGenerator property of the ItemsControl:
TreeViewItem cfItem = treeView.ItemContainerGenerator
.ContainerFromItem(obj) as TreeViewItem;
cfItem.IsExpanded = true;
The second level collections all have a reference to their parent object. So since I have many of these object, they are looped over and each uses it's parent object to find the TreeViewItem of it's parent. The order in which they are added to the collection guarantees (I think) that the children will always be processed after the parent. Thus I get this ugly line of code:
qualItem = (
(TreeViewItem)treeView.ItemContainerGenerator
.ContainerFromItem(
((Child)obj).ParentObject
)
)
.ItemContainerGenerator.ContainerFromItem(obj) as TreeViewItem;
This line always fails when it attempts to get the container from item obj. It successfully gets the parent TreeViewItem, but when attempting to get the Child's TreeViewItem container, I always receive null. The documentation states that ContainerFromItem() returns
"A System.Windows.UIElement that
corresponds to the given item. Returns
null if the item does not belong to
the item collection, or if a
System.Windows.UIElement has not been
generated for it."
I know that the second level child exists in the parent's item collection. I checked in the debugger in VS 2010, and all of the items are there. I spent a good bit of time on Google searching for an answer and came across someone who said that the container items are created on a background worker thread and may not be generated by the time an attempt is made to get the child item container. I tried waiting for the Status property of the ItemContainerGenerator to be equal to GeneratorStatus.ContainersGenerated, but I still got the same result. I need to somehow obtain the TreeViewItem container for my second level children so they can be re-expanded just like the first level children.
"A System.Windows.UIElement that corresponds to the given item. Returns null if the item does not belong to the item collection, or if a System.Windows.UIElement has not been generated for it."
Looks like because of Virtualization, the UIElement you look for doesn't exist when you are looking for it. If the collection is not too large, try turning the Virtualization off.
The solution is to add another layer of abstraction between the data and the TreeView. The top level collection is an ObservableCollection, and that contains several ViewModels, which implement INotifyPropertyChanged, and also have ObservableCollections of elements that are in the tree beneath it. This system allows WPF to more easily bind and keep track of the elements that are being added and deleted.
My main source of information was this article:
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

Categories

Resources