TreeViewItem and null value - c#

Not sure it is me after a long day of work ... or something is different in WPF type trees. I'm adding data to a tree and then when it is a parent node need to add its children but SelectedItem is always null!
any comment or direction would be helpful.
Thanks.
XAML:
<TreeView x:Name="TreeView1" Grid.Row="0">
</TreeView>
Code:
TreeView myTree = FindChild<TreeView>(Application.Current.MainWindow, "TreeView1");
myTree.Items.Add(ObjEmployee.Tag);
TreeViewItem tvi = (TreeViewItem) myTree.SelectedItem;
//my assumption was that when an item is added to tree most recent used node will be selected but it seems something is avoiding it or maybe works in a different way.

You should post some code snippet .. I do not understand what you need.
If you are using Hierarchical Template ( http://msdn.microsoft.com/en-us/library/ms742521.aspx )
The object of the SelectIedtem will be one of the data you put in ItemsSource
Otherwise, the "SelectedItem" can be any type of visual component that you put inside the treeview.
var treeItem = myTree.SelectedItem;
if (treeItem != null)
treeitem.GetType().Name;
Normally using TreeViewItem inside the treeview, all objects are in treeViewItem.Items
(Edit) You can try this:
var result = FindVisualChildren<TreeViewItem>(treeView);
foreach (var item in result)
{
if (item.IsSelected)
{
....
}
}
and
public static IEnumerable<T> FindVisualChildren<T>(FrameworkElement depObj) where T : FrameworkElement
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
FrameworkElement child = (FrameworkElement)VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}

Related

How to take Children of a ListView in Code-Behind

I have a ListView with some buttons(created in code-behind). I want to take all theese buttons and place them in a variable:
Button tg = (Button)sender;
ListView st = (ListView) tg.Parent;
var a = st.Children(this function doesn't work for ListView, but it's similar to what should resolve my problem) ;
foreach(Button btn in a)
Since ListView uses data virtualization to have a better performance. See https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/listview-and-gridview-data-optimization. So the buttons may not rendered when they are not in your viewport. This causes you can't get all buttons of your ListView.
But through some trick, we can disable ListView's data virtualization. Note that doing this may causes your app react slow when you have a mount of items in your ListView.
Suppose you want this way.
First, you need to modify ListView's ItemsPanel to StackPanel, according to doc, https://learn.microsoft.com/en-us/windows/uwp/debug-test-perf/optimize-gridview-and-listview#ui-virtualization.
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
Second, you can get all buttons using a helper function, it returns a List of your controls finded.
public static List<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
List<T> list = new List<T>();
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
list.Add((T)child);
}
List<T> childItems = FindVisualChildren<T>(child);
if (childItems != null && childItems.Count() > 0)
{
foreach (var item in childItems)
{
list.Add(item);
}
}
}
}
return list;
}
Third, you can use it this way.
var listButtons = FindVisualChildren<Button>(listView);
foreach (var btn in listButtons )
{
//to do
}
Done!!!

How to get item from item container in WPF?

Im currently trying to get drag/drop within a treeview working(using databinding and HierarchicalDataTemplate), and have the dragging working, but im running into a problem when trying to get the drop working, since i need to get the data item being dropped on and add it to its item collection of children nodes.
//treeitem is the name of my item data class
private void TreeViewItem_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent("DragableTreeViewItem"))
{
//the data of the treeitem i need to duplicate, provided by the drag operation
TreeItem data = e.Data.GetData("DragableTreeViewItem") as TreeItem;
TreeViewItem tvi = sender as TreeViewItem;
//here im getting the treeviewitem, but i need the treeitem
}
}
My best idea on how to get around this was to assign the treeitem as the tag of the treeviewitem on initialization/loading of the tree, and to do so i need to get the itemcontainer of the item, which i already have a working function to get that which i use when initiating the DoDrag()
//returns the item container of the parent of the TreeItem given
private TreeViewItem GetParentContainerFromItem(TreeItem ti)
{
List<TreeItem> GetOrderedParents(TreeItem item)
{
TreeItem currentParent = item;
List<TreeItem> items = new List<TreeItem>();
int i = 0;
do
{
if (currentParent.parentItem != null)
{
items.Insert(0, currentParent);
currentParent = currentParent.parentItem;
}
else
{
items.Insert(0, currentParent);
i++;
return items;
}
} while (i == 0);
return null;
}
//the local tree in a list, ordered from the original item (in this case "ti") at 0, down to the root at the end of the list
List<TreeItem> LocalHierarchy = GetOrderedParents(ti);
if (LocalHierarchy != null)
{
//print out the names of each treeitem in it, in order from the root down
string hierarchyString = "";
foreach (TreeItem t in LocalHierarchy)
{
if (hierarchyString == "")
{
hierarchyString = t.Title;
}
else
{
hierarchyString = (hierarchyString + ", " + t.Title);
}
}
System.Console.WriteLine(hierarchyString);
TreeViewItem localCurrentParent = null;
TreeViewItem finalContainer = null;
//walk down the tree in order to get the container of the parent
foreach (TreeItem t in LocalHierarchy)
{
//if the parent of the item given is a root node, meaning we can return its container
if (LocalHierarchy.IndexOf(t) == 0 && (LocalHierarchy.IndexOf(t) == (LocalHierarchy.Count - 2)))
{
finalContainer = treeView.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
break;
}
else
//if we're at a root node
if (LocalHierarchy.IndexOf(t) == 0)
{
localCurrentParent = treeView.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
}
else
//if we're at the 2nd to last, AKA the parent of the item given
if (LocalHierarchy.IndexOf(t) == (LocalHierarchy.Count - 2))
{
finalContainer = localCurrentParent.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
break;
}
else
{
localCurrentParent = localCurrentParent.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
}
}
if (finalContainer == null)
{
System.Console.WriteLine("Final container is null");
}
return finalContainer;
}
else
{
System.Console.WriteLine("ERROR: LocalHierarchy is null");
return null;
}
}
This seems to work perfectly when using to start the DoDrag()
private void DoDrag()
{
if(selectedItem != null)
{
TreeItem t = selectedItem;
TreeViewItem tvi = null;
if (t.parentItem == null)
{
//it has no parent, and is a root node
tvi = treeView.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
}
else
{
//it has a parent, and i can get the container for the parent
tvi = GetParentContainerFromItem(t).ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
}
DragDrop.DoDragDrop(tvi, new DataObject("DragableTreeViewItem", t, true), DragDropEffects.Copy);
dragNeeded = false;
}
else if(selectedItem == null)
{
Console.WriteLine("Selected item was null; cant drag");
}
}
But when i try to use it in my function to assign container tags it says that Container was null and that GetParentContainerFromItem returned null, yet my function does not log that it returned null
private void AssignContainerTag(TreeItem t)
{
TreeViewItem Container = null;
if (t.parentItem == null)
{
//its a root node
Container = treeView.ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
Container.Tag = t;
}
else
{
//it is not a root node
Container = GetParentContainerFromItem(t).ItemContainerGenerator.ContainerFromItem(t) as TreeViewItem;
Container.Tag = t;
}
}
Ive spent days stumped on why this does not seem to work, so if someone could give me some pointers on what im doing wrong or another way i could get the item from its container it would be a lifesaver. Also please excuse if this is poorly written, i am exhausted and about to go to sleep.
Your question is a little bit confusing. But it looks like you are trying to get the data item of the TreeViewItem which is the drop target of the drag&drop operation.
This is pretty simple. All you need to know is that if the item container is auto-generated via data binding (ItemsControl.ItemsSource) the DataContext of the container is the data item itself.
This applies to all item containers of an ItemsControl (e.g., ComboBoxItem, ListBoxItem, ListViewItem).
So TreeViewItem.DataContext references the underlying TreeItem instance that is wrapped by the TreeViewItem:
private void TreeViewItem_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent("DragableTreeViewItem"))
{
var sourceItem = e.Data.GetData("DragableTreeViewItem") as TreeItem;
var dropTargetItemContainer = sender as TreeViewItem;
var dropTargetItem = targetItemContainer.DataContext as TreeItem;
}
}
Remark
It looks like you are using the ItemContainerGenerator wrong. TreeView.ItemContainerGenerator will only handle top level items (i.e. child items). But as a tree node can have child nodes, each TreeViewItem is itself an ItemsControl as it contains an ItemsPresenter to display child items.
Therefore you have to use the appropriate ItemContainerGenerator to retrieve the child container or ItemContainerGenerator will return null.
For top-level items use TreeView.ItemContainerGenerator.
For child items use the parent's TreeViewItem.ItemContainerGenerator.
Also, in case of UI virtualization is enabled not all containers are generated when the TreeView is loaded. They are generated when need e.g., for display. Those containers (the TreeViewItem) are also shared to save resources. So once you set the TreeViewItem.Tag property its value might get lost as a new TreeViewItem instance is generated later to wrap the data item.
So you start at the root node and get its generated container. Now perform a tree search by traversing the TreeViewItems using a specific algorithm until you found the node where the DataContext equals the data item you are looking for and e.g., modify the Tag property.
You access the children of e.g. treeViewItemA by referencing the treeViewItemA.Items property and get their containers by calling treeVieItemA.ItemContainerGenerator.ContainerFromItem method for each child:
Example
public static class MyExtensions
{
// Get item container of item from TreeView, TreeViewItem, ListView or any ItemsControl
public static bool TryGetContainerOfChildItem<TItemContainer>(this ItemsControl itemsControl, object item, out TItemContainer itemContainer) where TItemContainer : DependencyObject
{
itemContainer = null;
foreach (object childItem in itemsControl.Items)
{
if (childItem == item)
{
itemContainer = (TItemContainer) itemsControl.ItemContainerGenerator.ContainerFromItem(item);
return true;
}
DependencyObject childItemContainer = itemsControl.ItemContainerGenerator.ContainerFromItem(childItem);
if (childItemContainer is ItemsControl childItemsControl && childItemsControl.TryGetContainerOfChildItem(item, out itemContainer))
{
return true;
}
}
return false;
}
}
Usage
// Search whole TreeView
if (treeView.TryGetContainerOfChildItem(item, out TreeViewItem itemContainer)
{
...
}
// Search from a specific parent TreeViewItem node
if (treeViewItem.TryGetContainerOfChildItem(item, out TreeViewItem itemContainer)
{
...
}

Capture RichTextBlock Hyperlink events

I'm trying to capture the click event of Hyperlinks inside a dynamically generated RichTextBlock.
I'm dynamically generating the contents of a richtextblock and then applying them with XamlReader. The content can vary quite a bit, so I can't manually parse the xaml and hook up events at that point.
My basic idea is to, once the richtextblock is loaded, find all Hyperlinks in it and hook up their click event there. This is my current code:
public class HookUpEvents()
{
foreach (var child in FindVisualChildren<Hyperlink>(richtxtblock))
{
child.Click += MyFunction;
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
Obviously, it isn't working. It looks like the FindVisualChildren function isn't returning any Hyperlinks. Any ideas on how I can achieve this?
Well, I'm sure late to the party, but RichTextBlock won't place his Blocks and their Inlines in VisualTree most of the time. To find all Inline-based elements (Run,Span,Bold etc.) you will need to loop through all content, by visiting each Block and subsequent Inline's. I would suggest something like this:
public static IEnumerable<T> GetAllTextElements<T>(this RichTextBlock rtb) where T : TextElement
{
var result = new List<T>();
var blocks = rtb.Blocks;
foreach (var block in blocks)
{
if (block is T)
{
result.Add(block as T);
continue;
}
var inlines = ((Paragraph)block).Inlines;
var res = TraverseInline<T>(inlines);
if (res != null && res.Any())
result.AddRange(res);
}
return result;
}
private static IEnumerable<T> TraverseInline<T>(IEnumerable<Inline> inlines) where T : TextElement
{
var result = new List<T>();
foreach (var item in inlines)
{
if (item is T)
{
result.Add(item as T);
continue;
}
else if (item is Span) // first Inline derived class to have own `Inlines`
{
var spanItem = item as Span;
var spanInlines = spanItem.Inlines;
var results = TraverseInline<T>(spanInlines);
if (results != null && results.Any())
result.AddRange(results);
}
}
return result;
}
So you can look for any TextElement-derived item with it.
Usage would be something like:
var textHyperlinks = myRichTextBlock.GetAllTextElements<Hyperlink>();
This will do as far as you don't use InlineUIContainer. That type of Inline behaves differently, as you can put anything UIElement-based as it's Child property. In that case your initial approach should work.
There's a couple of things here:
If you're trying to find the hyperlink inside of the RichTextBlock, its type is: Windows.UI.Xaml.Documents.Hyperlink. Not the type of the HyperLinkButton.
You can put the Click event handler in your text and then provide the handler method in your code behind file. If you dynamically generate text that looks like:
<Paragraph>
Text with a
<Hyperlink x:Name="link" Click="link_Click">link.</Hyperlink>
</Paragraph>
Feed that to the XamlReader, and put the following code in your code behind file:
private void link_Click(Windows.UI.Xaml.Documents.Hyperlink sender, Windows.UI.Xaml.Documents.HyperlinkClickEventArgs args)
{
Debug.WriteLine("Handle link click, by: " + sender.Name);
}
Then it should connect up correctly at runtime. And you can do whatever you want on the Click event handler. Even if there are multiple links, you can name them differently and just use one click handler to process.

How to access controls inside an ItemTemplate

Using Windows Phone 8, C#.
What I've done is basically done is edited the pivot item. I've named it MainPivot and inside that I've edited the Pivot Item Title and added a TextBlock inside it called PivotTitletxt. XAML for that is:
<DataTemplate x:Key="DataTemplate3">
<TextBlock x:Name="PivotTitletxt" Height="34" TextWrapping="Wrap" Text="{Binding}" Width="447"/>
</DataTemplate>
How can I access this e.g. when setting opacity or changing foreground? so that I can use it on my MainPage like e.g. PivotTitletxt.Opacity = 30; ...
Thanks!
The link #Sankarann gave you is a pretty good example.
I'll try to put it on your scenario:
Your MainPivot has PivotItems right? So What you have to do on the Loaded event is:
var _mainPivot = MainPivot as Pivot
foreach (var _pivotItem in _mainPivot.Items)
{
var _container = _mainPivot.ItemContainerGenerator.ContainerFromItem(_pivotItem);
var _children = AllChildren(_container)
var _name = "PivotTitletxt";
var _control = (TextBlock)_Children.first(x=>x.Name == _name);
_control.Opacity = 30;
}
Then copy the AllChildren method exactly as the it is in the site.
The code above, might have a few adjustments because I've done it without VS...
Hope it helps.
Regards,
============ new answer ==============
Find all controls in WPF Window by type
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
Then try :
TextBlock xx = FindVisualChildren<TextBlock>(mainPivot).FirsOrDefault(x=>x.name=="PivotTitletxt");
if(xx!=null)
xx.opacity = 30
Once again, this come might need some correction...i'm doing it by heart, without VS.
Try it out

WPF Get Item of Itemscontrol in Visualtree

I am implementing an DragAndDrop-manager for wpf using attached properties. It works quite nice. But there is only one problem. To grab the dragged item i am using the visualtree. As example I want to have the listboxitem but the originalsource is the border of the listboxitem. So I just use one of my helper methods to search for the parent with the type of ListBoxItem. If I found that I get the data of it and drag that.
But I dont want to have my DragAndDrop-manager aviable only while using a listbox. No I want to use it on every Itemscontrol.
But a DataGrid uses DataGridRows, a listview uses ListViewItem... So is there any chance to get the item without writing the code again, again and again?
well, you can have this function
(i prefer to have it as static):
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
and use it some kind of this:
i.e. you want to find all TextBox elements in yourDependencyObjectToSearchIn container
foreach (TextBox txtChild in FindVisualChildren<TextBox>(yourDependencyObjectToSearchIn))
{
// do whatever you want with child of type you were looking for
// for example:
txtChild.IsReadOnly = true;
}
if you want me to provide you some explanation, i'll do this as soon as i get up)
You can use FrameworkElement or UIElement to identify the control.
Control inheritance hierarchy..
System.Object
System.Windows.Threading.DispatcherObject
System.Windows.DependencyObject
System.Windows.Media.Visual
System.Windows.UIElement
System.Windows.**FrameworkElement**
System.Windows.Controls.Control
System.Windows.Controls.ContentControl
System.Windows.Controls.ListBoxItem
System.Windows.Controls.**ListViewItem**
System.Object
System.Windows.Threading.DispatcherObject
System.Windows.DependencyObject
System.Windows.Media.Visual
System.Windows.UIElement
System.Windows.**FrameworkElement**
System.Windows.Controls.Control
System.Windows.Controls.**DataGridRow**

Categories

Resources