I'm very new to WPF and don't know how to do this. I have a text box in a tab item on a tab control. How can I programmatically (C#) determine what tab item is the parent of this text box? I would also like to determine what tab control is the parent of the tab item.
Thanks very much.
TabItem.Parent will provide the logical parent element of the TabItem; which will be the associated TabControl. You can use the same approach for any control with the TabItem.
((FrameworkElement)myTextBox.Parent).Parent;
If the item is deeper in the tree and becomes unknown in its depth you will need to begin to approach it in a recursive manner.
You can use FrameworkElement.Parent to walk up the hierarchy of a control in WPF. This should let you (recursively) walk up until you find the TabItem, then walk up to the TabControl from there.
I am newbie in WPF too, but what about cycle searching?
For example:
TextBox TB = new TextBox();
TabControl MyTabControl = new TabControl();
// ...
foreach (TabItem ti in MyTabControl.Items)
if (TB.Parent == ti)
{
// textbox is here!
MessageBox.Show(ti.ToString());
break;
}
Here is a generic method for finding parent controls: How can I find WPF controls by name or type?
You can call it like this:
TabItem owner = UIHelper.FindVisualParent<TabItem>(myTextBox);
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 have a tabcontrol using MVVM Wpf c#. Now, I want to default focus control when active tabitem but it doesnot lost focus when move tab.
I used live visual tree but not active because paramater is viewmodel. I have used FocusManger get focus for control therefore I need get controls/UI from a tabItem itself.
Can you help me?Thanks
You can use this method to get all the logical children recursively:
private void GetAllChildren(DependencyObject parent, List<DependencyObject> allChildren)
{
var children = LogicalTreeHelper.GetChildren(parent);
foreach (var child in children.OfType<DependencyObject>())
{
allChildren.Add(child);
GetAllChildren(child, allChildren);
}
}
How can I get multiple instances of a usercontrol without the viewmodel being shared? Each usercontrol (and thus viewmodel) should be an instance of its own.
I have read a solution in this question: MVVMLight UserControl View Model-Create new Instance of User control for each view but I cannot get it to work!
I have a listview and a tabcontrol. When I click an item of the listview a new tab must be created with as content the usercontrol which holds information from the selected listitem. The problem is that when selecting multiple items all the items contain the information from the last selected item.
This is my viewmodellocator:
public DossierDetailViewModel DossierDetail
{
get
{
return new DossierDetailViewModel();
}
}
And I call the new usercontrol like this:
DossierDetailViewModel newDossier = new DossierDetailViewModel();
newDossier.TabName = SelectedDossier.Omschrijving;
this.OpenDossiers.Add(newDossier);
Messenger.Default.Send<DTO.Dossier.Dossier>(SelectedDossier, "SetDossier");
EDIT:
Is there no one who can help me or put me in the right direction? :(
The answer of this problem can be found here: https://mvvmlight.codeplex.com/discussions/577555
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 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)