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) { ... }
Related
I have a Button on a UserControl that adds an item to a ListBox on that UserControl. Let's call that control Parent. The ListBoxItems contain another UserControl. Let's call that Child. The button adds an item to the ItemSource of the listbox (MVVM style).
I can scroll that into view without a problem. I can set the focus to the ListBoxItem, but what I want is the focus to be set on the first TextBox of the child UserControlof the content of the ListBoxItem. I can't seem to figure that out. The code below sets the focus to the ListBoxItem, not the UserControl child of it or any control on it.
Private Sub bnAdd(sender As Object, e As RoutedEventArgs)
VM.AddDetail()
MyList.ScrollIntoView(MyList.Items(MyList.Items.Count - 1))
Dim ListBoxItem As ListBoxItem = MyList.ItemContainerGenerator.ContainerFromItem(MyList.SelectedItem)
ListBoxItem.Focus()
End Sub
On my child UserControl I used this in XAML:
FocusManager.FocusedElement="{Binding ElementName=txtMyBox}"
There is a related question here and most of the approaches use hooking into focus events to achieve the focus change. I want to propose another solution that is based on traversing the visual tree. Unfortunately, I can only provide you C# code, but you can use the concept to apply it in your Visual Basic code.
As far as I can see, you are using code-behind to scroll your items into view. I will build on that. Your list box has list box items and I guess you use a data template to display UserControls as child items. Within these user controls there is a TextBox that you have assigned a name in XAML via x:Name or in code-behind via the Name property. Now, you need a helper method to traverse the visual tree and search for text boxes.
private IEnumerable<TextBox> FindTextBox(DependencyObject dependencyObject)
{
// No other child controls, break
if (dependencyObject == null)
yield break;
// Search children of the current control
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++)
{
var child = VisualTreeHelper.GetChild(dependencyObject, i);
// Check if the current item is a text box
if (child is TextBox textBox)
yield return textBox;
// If we did not find a text box, search the children of this child recursively
foreach (var childOfChild in FindTextBox(child))
yield return childOfChild;
}
}
Then we add a method that filters the enumerable of text boxes for a given name using Linq.
private TextBox FindTextBox(DependencyObject dependencyObject, string name)
{
// Filter the list of text boxes for the right one with the specified name
return FindTextBox(dependencyObject).SingleOrDefault(child => child.Name.Equals(name));
}
In your bnAdd handler you can take your ListBoxItem, search for the text box child and focus it.
var textBox = FindTextBox(listBoxItem, "MyTextBox");
textBox.Focus();
I got an ItemsControl (a Treeview or a TreevieItem) wich Item is filled with my own Model.
I wan't to programatically unfold the TreeView. So I try this:
var model = itemsControl_.Items.Select(i => i as MyModel).Where(ftn => ftn != null && ftn.key = searchedKey));
now that i have founded the model a want to unfold. I search for the container to unfold it:
var tvi = itemsControl_.ItemContainerGenerator.ContainerFromItem(model) as TreeViewItem;
if(tvi!=null)
{
if (!tvi.IsExpanded)
{
tvi.IsExpanded = true;
}
}
And sometimes tvi is null !?!
Can someone explain me how that can be possible ?
Based on your description, it seems you want to expand TreeViewItem.
I think this is most likely caused by the Virtualizating. The Virtualizating Attached Property is on by default for TreeView, which means if an item is not in the viewport, it probably didn't get an container.
If you call GetContainerFromItem on a TreeView, the ItemContainerGenerator searches only the direct child objects of the TreeView. So you need to recursively traverse the TreeView and child TreeViewItem objects. A further complication is that if the TreeView virtualizes its items (you enable virtualization by setting the VirtualizingStackPanel.IsVirtualizing property to true), the child items need to be created before you can check its data object.
Hope this will help.
Pls refer to http://blogs.msdn.com/b/wpfsdk/archive/2010/02/23/finding-an-object-treeviewitem.aspx
Regards
Dipak
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.
I have the same problem as a previous question on this forum:
previous thread
I have a TreeView which uses a HierarchicalDataTemplate to bind its data. I need to get the TreeViewItem from the selected item, which is my own class.
I have tried the solution given in the previous thread as well as modified versions of it. Nothing works. myTreeView.Items.CurrentPosition returns -1. If I check inside Items I can only see my 2 root items. I have several levels of items.
myTreeView.ItemContainerGenerator.ContainerFromObject(myTreeView.SelectedItem) doesn't work either, it returns null.
myTreeView.ItemContainerGenerator.ContainerFromObject(myTreeView.Items.CurrentObject(myTreeView.Items.CurrentItem) returns null.
this is what you need:
private void TreeView_OnSelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
object entity = null;
entity = e.NewValue;
}
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)