VisualTreeHelper.GetChildrenCount return 0? - c#

I'm using VisualTreeHelper.GetChildrenCount() to find child controls, but it always return 0.
Here is my code
<ScrollViewer x:Name="scrollViewerChannelsRecordTimeData">
<StackPanel x:Name="channelsRecordTimeData">
<ItemsControl x:Name="channelRecordTimeItems" ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid x:Name="hoursLines">
//Some Controls here
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
C# code:
channelRecordTimeItems.ItemContainerGenerator.StatusChanged += ChannelRecordTimeItemsStatusChangedEventHandler;
private void ChannelRecordTimeItemsStatusChangedEventHandler(Object sender, EventArgs e)
{
if (channelRecordTimeItems.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
if (channelRecordTimeItems.HasItems)
{
DependencyObject dependencyObject = null;
Grid gridHighlightRecordData = null;
for (int i = 0; i < channelRecordTimeItems.Items.Count; i++)
{
dependencyObject = channelRecordTimeItems.ItemContainerGenerator.ContainerFromIndex(i); //dependencyObject != null
if (dependencyObject != null)
{
Grid hoursLines = FindElement.FindChild<Grid>(dependencyObject, "hoursLines"); //hoursLines = null
}
}
}
}
}
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent); //Return 0 here
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
VisualTreeHelper.GetChildrenCount() always return 0,
The code for constructing for items here
List<ChannelRecordTimeItemData> listChannelRecordTimeItemData = new List<ChannelRecordTimeItemData>();
for(int i = 0; i < 5; i++)
{
ChannelRecordTimeItemData item = new ChannelRecordTimeItemData();
listChannelRecordTimeItemData.Add(ChannelRecordTimeItemData);
}
channelRecordTimeItems.ItemsSource = listChannelRecordTimeItemData;
channelRecordTimeItems.Items.Refresh();
I have searched on forum and internet, but i can not solve it, someone can help me?
Many thanks!
T&T

The problem is that when the ItemContainerGenerator signals the ContainersGenerated status, the container (a ContentPresenter) has been created, but not yet loaded. Especially the data template has not yet been applied to the ContentPresenter, hence there is nothing in the visual tree.
You may get around this by adding a Loaded event handler when looping over the generated containers.
private void ItemContainerGeneratorStatusChanged(object sender, EventArgs e)
{
if (itemsControl.ItemContainerGenerator.Status
== GeneratorStatus.ContainersGenerated)
{
var containers = itemsControl.Items.Cast<object>().Select(
item => (FrameworkElement)itemsControl
.ItemContainerGenerator.ContainerFromItem(item));
foreach (var container in containers)
{
container.Loaded += ItemContainerLoaded;
}
}
}
private void ItemContainerLoaded(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
element.Loaded -= ItemContainerLoaded;
var grid = VisualTreeHelper.GetChild(element, 0) as Grid;
...
}

If your using Caliburn.Micro this will help you.
For your Viewmodel the base Class should be Screen then only VisualTreeHelper.GetChildrenCount() give no.of childs.(because Screen will Activate all childs
or
otherwise (FrameworkElement)YourParent).ApplyTemplate() method

Related

Adding button click event in datagridtemplatecolumn in user control from mainwindow programmatically

I have a MainWindow. It has a stackpanel myStack and some other things.
In stackPanel, there is a usercontrol (TaskGrid(_TG)) added programmatically.
In this UserControl, there is a DataGrid(dgEmployee), which have 4 template columns. the last column contains a button.
I am trying to assign the button click event from the mainwindow constructor and handle the event here.
Here are the codes:
in MainWindow.xaml
<Grid Grid.Row="2">
<StackPanel Name="myStack"/>
</Grid>
in MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
_TG = new TaskGrid();
_TD = new _1.TaskDetails();
_TM = new _1.TaskMaster();
myStack.Children.Add(_TG);
_AUC = ActiveUserControl.Grid;
foreach (object child in myStack.Children)
{
string childname = "";
if (child is FrameworkElement)
{
childname = (child as FrameworkElement).Name;
if (childname == "TaskGrid")
{
Grid dg = ((Grid)((UserControl)child).Content);
foreach (var item in dg.Children)
{
DataGridColumn b = ((DataGrid)item).Columns[3] as DataGridColumn;
}
}
}
}
}
And in TaskGrid.xaml, the only template column is given here
<DataGridTemplateColumn Width="30">
<DataGridTemplateColumn.CellTemplate>
<ItemContainerTemplate>
<Button Name="btnMaster" Background="Transparent">
<Button.Template>
<ControlTemplate>
<Image Source="ArrowRight.png"/>
</ControlTemplate>
</Button.Template>
</Button>
</ItemContainerTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I have to assign the click event like
button.click += new RoutedEvent(button_click);
And later use button_click event in the MainWindow.xaml.cs
Wait until the UserControl has been loaded. You could then get a reference to the DataGrid using the following helper method that searches for an element of a specific type recursively in the visual tree.
private static T GetChildOfType<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj == null)
return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
The same way you can get a reference to a specific cell:
public static DataGridCell GetCell(DataGrid dataGrid, DataGridRow rowContainer, int column)
{
if (rowContainer != null)
{
System.Windows.Controls.Primitives.DataGridCellsPresenter presenter =
GetChildOfType<System.Windows.Controls.Primitives.DataGridCellsPresenter>(rowContainer);
if (presenter != null)
return presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
}
return null;
}
Please refer to the following blog post for more information about this: https://blog.magnusmontin.net/2013/11/08/how-to-programmatically-select-and-focus-a-row-or-cell-in-a-datagrid-in-wpf/
Below is a full example for you. Note that the DataGrid may contain several rows and some of the rows may have been virtualized away. You will find more information about this on the link above.
public MainWindow()
{
InitializeComponent();
_TG = new TaskGrid();
_TD = new _1.TaskDetails();
_TM = new _1.TaskMaster();
myStack.Children.Add(_TG);
_AUC = ActiveUserControl.Grid;
_TG.Loaded += (s, e) =>
{
DataGrid dataGrid = GetChildOfType<DataGrid>(_TG);
if (dataGrid != null)
{
foreach (var item in dataGrid.Items)
{
DataGridRow dgr = dataGrid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
if (dgr != null)
{
DataGridCell cell = GetCell(dataGrid, dgr, 3); //<-- column index
if (cell != null)
{
Button button = GetChildOfType<Button>(cell);
if (button != null)
{
button.Click += new RoutedEvent(button_click);
}
}
}
}
}
};
}

How to Convert XAML File (View) into DependencyObject?

I am working on a project where i have to deal with navigation based on Frame in MVVM pattern thus to get to the element Name x:Name of type Frame we have to convert MainWindow into DependencyObject like this..
private static FrameworkElement GetDescendantFromName(DependencyObject parent, string name)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count < 1)
{
return null;
}
for (var i = 0; i < count; i++)
{
var frameworkElement = VisualTreeHelper.GetChild(parent, i) as FrameworkElement;
if (frameworkElement != null)
{
if (frameworkElement.Name == name)
{
return frameworkElement;
}
frameworkElement = GetDescendantFromName(frameworkElement, name);
if (frameworkElement != null)
{
return frameworkElement;
}
}
}
return null;
}
In Navigation Service Class i use...
var frame = GetDescendantFromName(Application.Current.MainWindow, "FrameName") as Frame;
frame.source = new Uri("Views/StudentView.Xaml");
This technique is limited to only MainWindow. When i pass new instence of EmployeeDetailView.Xaml as a depenecy Object, The Xaml File is not loaded and GetChildrenCount() returns 0.
var frame = GetDescendantFromName(EmployeeDetaiView.Xaml, "FrameName") as Frame;
here frame has null value.
how could i make it work with currently rendered EmployeeDetailView to get the Frame element?
Use Application.LoadComponent Method (Uri)
Page p = (Page) Application.LoadComponent(new Uri("Views/EmployeeDetaiView.Xaml.xaml", UriKind.Relative));
var ctrl = GetDescendantFromName(p, "SomeControl");
Here, EmployeeDetaiView.Xaml lies in Views folder.
EDIT #1 after user comments
However, as pointed by OP in his comment that VisualTreeHelper.GetChildrenCount() is returning 0. This happens because p is not part of VisualTree, once it is part of VisualTree it will work correctly.
Page p;
private void Button_Click_1(object sender, RoutedEventArgs e)
{
p = (Page) Application.LoadComponent(new Uri("Views/EmployeeDetaiView.Xaml.xaml", UriKind.Relative));
Frm.Content = p;
// This will print 0
int i = VisualTreeHelper.GetChildrenCount(p);
System.Diagnostics.Debug.WriteLine("Children count = " + i);
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
// Now it will correctly print 1, as 'p' is now part of VisualTree
int i = VisualTreeHelper.GetChildrenCount(p);
System.Diagnostics.Debug.WriteLine("Children count = " + i);
}

How to remove/delete ContentControl in Canvas WPF

i'm starting with WPF and i have a problem. Please help me. Thanks, sorry for my bad English!
I have added ContentControl to a Canvas, and i want to remove/delete it.
Draw ContentControl code:
ContentControl cc = new ContentControl();
cc.Content = shape;
cc.Height = h;
cc.Width = w;
Style s = myCanvas.FindResource("DesignerItemStyle") as Style;
cc.Style = s;
Canvas.SetLeft(cc, x);
Canvas.SetTop(cc, y);
myCanvas.Children.Add(cc);
I use HitTest to remove it but i can remove only shape
private void myCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
Point pt = e.GetPosition((Canvas)sender);
HitTestResult result = VisualTreeHelper.HitTest(myCanvas, pt);
if (result != null)
{
myCanvas.Children.Remove(result.VisualHit as Shape); //it works with shape
// i have changed it into myCanvas.Children.Remove(result.VisualHit as ContentControl);
//but it didn't work with ContentControl
}
}
It is so because the ContentControl is the parent of the Shape, and the Canvas's children contains the ContentControl that hosts the shape.
You could do this to fix your issue :)
private void myCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
DependencyObject ob = FindAncestor<ContentControl>((DependencyObject)e.Source);
if (ob.GetType() == typeof(ContentControl))
myCanvas.Children.Remove(ob as ContentControl);
}
public T FindAncestor<T>(DependencyObject dependencyObject)
where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null) return null;
var parentT = parent as T;
return parentT ?? FindAncestor<T>(parent);
}
I suggest you the next solution:
private void myCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var dObj = sender as DependencyObject;
if(dObj == null) return;
var ob = dObj.GetChildOfType<ContentControl>();
if (ob != null)
{
myCanvas.Children.Remove(ob);
}
}
Helper code:
public static T GetChildOfType<T>(this DependencyObject depObj)
where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
just put the helper inside the public static class as it is an extension method
regards
You can add the control, for example:
Rectangle r = new Rectangle() { Name = "MyName" };
and to remove from the canvas:
UIElement element = StationLayout.FindName ("MyName") as UIElement;
StationLayout.Children.Remove(element);

Add ListBox to DataTemplate from C# code

I am trying to add HubSections dynamically (C# code) - that works.
Then from the same code I want to add ListBoxes to each of 'em - and apparently I have no idea how to do that.
I found several examples like adding:
ContentTemplate = new DataTemplate() { VisualTree = ... }
... to HubSection constructor but there is no VisualTree in DataTemplate.
Please, ask for any details if my problem description is too vague - I am a WP8.1 newbie so I could skip some important info.
The best way I found is to create DataTemplate like this:
public class ViewSection : HubSection {
public ViewSection(View view) {
string xaml = "<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><StackPanel /></DataTemplate>";
ContentTemplate = XamlReader.Load(xaml) as DataTemplate;
this.Loaded += ViewSection_Loaded;
}
private void ViewSection_Loaded(object sender, RoutedEventArgs e) {
base.OnApplyTemplate();
StackPanel stackPanel = findStackPanelInSubtree(this);
...
< adding stuff to stack panel >
...
this.Loaded -= ViewSection_Loaded;
}
private StackPanel findStackPanelInSubtree(FrameworkElement element) {
if (element != null) {
if (element.GetType() == typeof(StackPanel)) {
return element as StackPanel;
}
foreach (FrameworkElement child in getChildren(element)*) {
StackPanel result = findStackPanelInSubtree(child);
if (result != null) return result;
}
}
return null;
}
private List<FrameworkElement> getChildren(FrameworkElement element)* {
if (element != null) {
List<FrameworkElement> result = new List<FrameworkElement>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) {
result.Add(VisualTreeHelper.GetChild(element, i) as FrameworkElement);
}
return result;
}
return null;
}
}
Is this some kind of cruel joke of Microsoft devs or there is a better way to do this?
* of course this is totally redundant but foreach makes it sooo much nicer to read
Unavailability of the whole children collection looks not really convenient. You may however rewrite your handy getChildren method to exhibit yield keyword usage:
private List<FrameworkElement> getChildren(FrameworkElement element) {
if (element != null) {
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++) {
yield return (VisualTreeHelper.GetChild(element, i) as FrameworkElement);
}
}
yield break;
}

Wp8:Not able to get checkBox in listbox

I am not able to find checkbox in listbox xaml:
<ListBox x:Name="my_list" Grid.Row="0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<CheckBox x:Name="cbx_state" Tag="{Binding}"/>
<TextBlock x:Name="txt_string" Text="{Binding}" VerticalAlignment="Center" FontSize="34" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
I am trying to get cbk_state so that i can set its checked property.The function i used to get the checkbox is
private void GetItemsRecursive(DependencyObject lb)
{
var childrenCount = VisualTreeHelper.GetChildrenCount(lb);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(lb, i);
if (child is ListBoxItem)
{
MessageBox.Show(child.GetType().ToString());
return;
}
GetItemsRecursive(child);
}
}
The problem is that i am getting ChildrenCount as zero everytime.
I have gone through several methods but no as such of use.Also tried
this
but here i am not getting ItemContainerGenerator for listBox.
I am new to wp8 programming plz help.Thanks
Are you asking about getting the Checked property of the Checkbox?
Is this the one you were looking for?. Sample code to find the Children control within a Parent using VisualTreeHelper:
private ChildControl FindVisualChild<ChildControl>(DependencyObject DependencyObj)
where ChildControl : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(DependencyObj); i++)
{
DependencyObject Child = VisualTreeHelper.GetChild(DependencyObj, i);
if (Child != null && Child is ChildControl)
{
return (ChildControl)Child;
}
else
{
ChildControl ChildOfChild = FindVisualChild<ChildControl>(Child);
if (ChildOfChild != null)
{
return ChildOfChild;
}
}
}
return null;
}
Hi got the solution here. there is no need to set virtualization property its simple.
private void GetItemsRecursive(DependencyObject lb)
{
var childrenCount = VisualTreeHelper.GetChildrenCount(lb);
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(lb, i);
if (child is CheckBox) // specific/child control
{
CheckBox targeted_element = (CheckBox)child;
targeted_element.IsChecked = true;
if (targeted_element.IsChecked == true)
{
return;
}
}
GetItemsRecursive(child);
}
}
just a bit change at DependencyObject child = VisualTreeHelper.GetChild(lb, i); instead of var child

Categories