Expand Root in TreeView when ItemSource changed - silverlight

I have a ChildWindow in a Silverlight 4 App with a TreeView. The ItemSource is binded to an ObservableCollection of Items in a ViewModel. When the window opens the item are loaded from a webservice.
I have only one root node and I need it to be initially expanded. The TreeView even has the extension ExpandToDepth() which seems perfect but I don't know where I can call it. I didn't find an event that occurs after the items are updated from the ItemSource.
I tried using ItemContainerGenerator.ItemChanged and OnItemsChanged in TreeView but they are both executed before the tree view items are generated so the expand commands won't work.
How can I get this to work?

Try to register for your ObservableCollection's CollectionChange Event and do your actions there.

Derive from TreeView and create IsRootItemExpanded property and create same name property in ViewModel. In set accesser check if value true then call your method like this ExpandToDepth(1) When itemssource is need to update then set IsRootItemExpanded property true. You must to bind IsRootItemExpanded of TreeView to IsRootItemExpanded of ViewModel.

I solved my problem by overriding the PrepareContainerForItemOverride method in TreeView. I wondered why this is just called for the root node but it works.
public class ExpandedRootTreeView : TreeView
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
TreeViewItem treeViewItem = element as TreeViewItem;
if (treeViewItem != null) treeViewItem.IsExpanded = true;
base.PrepareContainerForItemOverride(element, item);
}
}
Thanks to everybody who responded.

Related

How do I get the UIElement when Collection changes?

I have a wpf Treeview which has a dynamic itemssource. The User can add and remove items at runtime.
I'm missing an event which gives me the currently added UIElement that was added to the treeviews itemsSource. So I guess I need to switch to OnCollectionChanged.
This is what I have:
// MyItemViewModel is a viewmodel for a TreeViewItem
// MyCollection is bound to hte Treeview's ItemsSource
public class MyCollection : ObservableCollection<MyItemViewModel>
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
// i like to have the UIelement which was added to the collection
// (e.NewItems contains the FrameworkElement's DataContext)
break;
}
}
}
Im following MVVM, as good as I can, and don't want to hold any view elements in the viewmodel.
I like to have an event that is fired when an item is added, which provides the new added UIElement in its sender or EventArgs.
I already tried ItemContainerGenerator class, but it's not useful inside a viewmodel since it requires already a UIElement Control.
You seem to be looking at this problem from the wrong direction... in MVVM, you can pretty much forget about the UI for the most part. So, instead of thinking how to get hold of the item that the user added into the collection control in the UI, think about accessing the data object that you added to the data collection in the view model that is data bound to the UI collection control in response to an ICommand that was initiated by the user.
So to me, it sounds like you need to implement an ICommand that is connected to a Button in the UI, where you add the new item into the data bound collection rather than any event. In this way, you'll always know the state of all of your data items.

Execute custom code after command was executed

I have a TreeView control, and after a new node is added, I need to execute some custom code (ex expand the node, make it visible, and fire begind edit method). Since I want this to be available in every instance of TreeView control, I thought of subclassing the TreeView control.
Now, in order for treeview to know when the new node is added, it would either have an event that is fired when item is added (which it doesn't), or to have a reference to command that was executed to add a new item.
So two questions:
1) Is there a way to add an event in TreeView that would be fired whenever a treenode is added (I am always adding nodes through source collection from ViewModel) - I could not find any way to do this
2) I could add an AddCommand property to TreeList, that would be bound to ViewModel's AddCommand, and then have some button, or ContextMenu item that would bind to TreeList.AddCommand, instead of view model. This way TreeView would hold reference to AddCommand, but the drawback would be that actual usage would be kind of wierd. Question: How can I know when an TreeView's AddCommand (or any command, to that matter) is executed, so I can fire some custom code after it? It seems that CommandManager.AddExecutedHandler is a solution, but I am unable to execute it.
Is this any help?
public class CustomTreeControl : TreeView
{
...
// WPF only
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
base.OnItemsSourceChanged(oldValue, newValue);
Debug.WriteLine("OnItemsSourceChanged");
}
// WPF + Silverlight
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
Debug.WriteLine("OnItemsChanged: {0}", e.Action);
}
}

WPF Accessing Items inside Listbox which has a UserControl as ItemTemplate

I have Window that has a ListBox
ListBox(MyListBox) has a DataTable for its DataContext
ListBox's ItemSource is : {Binding}
Listbox has a UserControl(MyUserControl) as DataTemplate
UserControl has RadioButtons and TextBoxes (At first They're filled with values from DataTable and then user can change them)
Window has one Submit Button
What I want to do is, when user clicks the submit button
For each ListBox Item, get the values form UserControl's TextBoxes and RadioButtons.
I was using that method for this job :
foreach(var element in MyListBox.Items)
{
var border = MyListBox.ItemContainerGenerator.ContainerFromItem(element)as FrameworkElement;
MyUserControl currentControl = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(myBorder,0) as Border,0)as ContentPresenter,0)as MyUserControl;
//And use currentControl
}
I realised nothing when using 3-5 items in Listbox. But when I used much more items, I saw that "var border" gets "null" after some elements looped in foreach function.
I found the reason here :
ListView.ItemContainerGenerator.ContainerFromItem(item) return null after 20 items
So what can I do now? I want to access all items and get their values sitting on user controls.
Thanks
You should use objects who implement INotifyPropertyChanged and bind an ObservableCollection of it to the ItemSource
And then you can get all the list of items.
Here some quick links from MSDN to get more informations
How to: Implement Property Change Notification
Binding Sources Overview
You should google for some tutorials about this.
Zied's post is a solution for this problem. But I did the following for my project:
I realised that there's no need to use UserControl as DataTemplate in my project. So I removed ListBox's DataTemplate.
I removed MyListBox.DataContext = myDataTable and used this:
foreach(DataRow dr in myDataTable.Rows)
{
MyUserControl muc = new MyUserControl(dr);
myListBox.Items.Add(muc);
}
I took DataRow in my UserControl's constructor and did what I want.
And at last I could access my UserControls in ListBox by using :
foreach(MyUserControl muc in
myListBox)
{
//do what you want
}
Easy huh? :)

C# WPF - Adding a child node to a selected node in treeview

In WPF treeview control, I need to add a child node to a parent node i select using mousedoubleclick event.
http://msdn.microsoft.com/en-us/library/system.windows.controls.treeview.selecteditem.aspx
I followed the step in the MSDN, but i get invalidCastException when i do this.
TreeViewItem newChild =
(TreeViewItem)treeView1.SelectedItem;
How can i solve this?
Thanks
SelectedItem returns the selected data item, not the visual representing it.
If you need to access the selected TreeViewItem, use the ItemContainerGenerator :
TreeViewItem item = treeView1.ItemContainerGenerator.ContainerFromItem(treeView1.SelectedItem) as TreeViewItem;
Not sure it works for nested items though... you might have to use the ItemContainerGenerator of the parent TreeViewItem, which wouldn't be very convenient
EDIT: just tested, indeed it only works for root nodes...
Anyway, the best way to add a node is to use bindings and HierarchicalDataTemplates. You just need to add the object to the data source, and the corresponding TreeViewItem will be added automatically (provided the containing collection implements INotifyCollectionChanged...)
What type of Items do you Add() to the Tree? The same type will be returned.
If it is mixed, use
TreeViewItem newChild = treeView1.SelectedItem as TreeViewItem;
if (newChild != null) { ... }

WPF ListBox - Getting UIElement instead of of SelectedItem

I created a ListBox that has a DataTemplate as Itemtemplate. However, is there an easy way to access the generated UIElement instead of the SelectedItem in codebehind?
When I access SelectedItem, I just get the selected object from my
ItemsSource collection. Is there a way to access the UIElement (ie. the
element generated from the DataTemplate together with the bound object)?
You are looking for the ItemContainerGenerator property. Each ItemsSource has an ItemContainerGenerator instance. This class has the following method that might interest you: ContainerFromItem(object instance).
Once you have a handle to the ListBoxItem, you can go ahead and browse the logical and visual tree. Check out Logical Tree Helper and Visual Tree Helper.
Like Andy said in the comments, just because the item exists in your collection doesn't mean a container has been generated for it. Any kind of virtualizing panel scenario will raise this issue; UIElements will be reused across the different items. Be careful with that as well.
siz, Andy and Bodeaker are absolutely right.
Here is how I was able to retrieve the textbox of the listbox's selected item using its handle.
var container = listboxSaveList.ItemContainerGenerator.ContainerFromItem(listboxSaveList.SelectedItem) as FrameworkElement;
if (container != null)
{
ContentPresenter queueListBoxItemCP = VisualTreeWalker.FindVisualChild<ContentPresenter>(container);
if (queueListBoxItemCP == null)
return;
DataTemplate dataTemplate = queueListBoxItemCP.ContentTemplate;
TextBox tbxTitle = (TextBox)dataTemplate.FindName("tbxTitle", queueListBoxItemCP);
tbxTitle.Focus();
}
(Note: Here, VisualTreeWalker is my own wrapper over VisualTreeHelper with various useful functions exposed)

Resources