Accessing the children of an ItemsControl [duplicate] - c#

I am creating a WP 8.1 Silverlight app.
I have an ObservableCollection of string names, that is set to the ItemsSource of a ListBox. Which are the names of buttons in the ListBox. I then want to extract the buttons from the ListBox, but the return value is of type string.
The xaml code is:
<ListBox x:Name="Game_ScrollViewer_online" Margin="41,104,128,6" SelectedValuePath="Current_game_button">
<ListBox.ItemTemplate>
<DataTemplate>
<Button x:Name="Current_game_button" Content="{Binding}"
HorizontalAlignment="Center" Height="80" Margin="-14,6,-15,0"
VerticalAlignment="Top" Width="210" Template="{StaticResource Button_CurrentLayout1}"
RenderTransformOrigin="0.5,0.5" Foreground="#FFCBECCB" FontFamily="Times New Roman"
Click="LoadGame_online" FontSize="16">
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I then want to extract the button element:
for (int i = 0; i < Game_ScrollViewer_online.Items.Count; i++)
{
var tempType = Game_ScrollViewer_online.Items[i].GetType();
Button tempBut = (Game_ScrollViewer_online.Items[i] as Button);
//Do stuff with button
}
But as said the return type is string.
Why is it not button ? And is there a way to access the button?

You need FrameworkTemplate.FindName Method (String, FrameworkElement) for this purpose:
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
Then:
for (int i = 0; i < Game_ScrollViewer_online.Items.Count; i++)
{
ListBoxItem GameListBoxItem = (ListBoxItem)(Game_ScrollViewer_online.ItemContainerGenerator.ContainerFromIndex(i));
ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(GameListBoxItem);
DataTemplate myDataTemplate = contentPresenter.ContentTemplate;
Button tempBut = (Button) myDataTemplate.FindName("Current_game_button", contentPresenter);
//Do stuff with button
}
To solve missing FindName use FindDescendant like this:
public T FindDescendant<T>(DependencyObject obj) where T : DependencyObject
{
if (obj is T)
return obj as T;
int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
if (childrenCount < 1)
return null;
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is T)
return child as T;
}
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = FindDescendant<T>(VisualTreeHelper.GetChild(obj, i));
if (child != null && child is T)
return child as T;
}
return null;
}

Related

How do you get all the Textbox from a DataGrid?

I simply want to achieve the same thing like this;
MyTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource(); //this will forcely update source of this TextBox.
I have a DataGrid with DataTemplate.
<DataGrid x:Name="MyDataGrid">
<DataGrid.Columns>
.....
<DataGridTemplateColumn MinWidth="50" DisplayIndex="4">
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
....
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="TheTextBox">
....
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</Datagrid.Columns>
</DataGrid>
I got NullReferenceException on this;
for (int x = 0; x < MyDataGrid.Items.Count; x++)
{
TextBox? ele = ((ContentPresenter)(MyDataGrid.Columns[2].GetCellContent(x))).Content as TextBox;
ele.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
Basically, what I want is to forcibly update the source of the TextBoxes in my DataGrid.
It looks this is not simple as I thought. It needs two methods in order to get the TextBox from the DataGrid.
First, using the GetVisualChild;
public static T? GetVisualChild<T>(Visual parent) where T : Visual{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++){
Visual visual = (Visual)VisualTreeHelper.GetChild(parent, i);
if (visual is not T child) child = GetVisualChild<T>(visual);
if (child != null) return child;
}
return default;
}
Second, using FindVisualChild;
public T? FindVisualChild<T>(DependencyObject obj, string name) where T : DependencyObject{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++){
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T t && child is FrameworkElement frameworkElement && frameworkElement.Name == name) return t;
T childOfChild = FindVisualChild<T>(child, name);
if (childOfChild != null) return childOfChild;
}
return null;
}
Combining these two in order to get the TextBox;
for (int i = 0; i < MyDataGrid.Items.Count; i++){
DataGridRow dataGridRow = (DataGridRow)MyDataGrid.ItemContainerGenerator.ContainerFromIndex(i);
DataGridCellsPresenter dataGridCellsPresenter = GetVisualChild<DataGridCellsPresenter>(dataGridRow);
for (int j = 0; j < MyDataGrid.Columns.Count; j++){
TextBox textBox = FindVisualChild<TextBox>((DataGridCell)dataGridCellsPresenter.ItemContainerGenerator.ContainerFromIndex(j), "TheTextBox"); // I also need to specify the name of the TextBox from my DataTemplate.
if (textBox != null) textBox.GetBindingExpression(TextBox.TextProperty).UpdateSource(); //updatesource of the TextBox
}
}
Somehow it solves my problem.

Get XAML controls by their bound properties?

Is there a way to get the controls in XAML using the properties bound to them,
something like this:
Account acc = GetFromDB(id);
foreach (var prop in acc.GetType().GetProperties())
{
Control control = GetControl(prop.Name);
//Then I want to set properties like Foreground for the control
}
private Control GetControl(string propertyName)
{
//getting the control bound to the propertyName
}
and say that this is XAML code:
<Label>Name</Label>
<TextBox Name="txtName"
Grid.Column="1"
Text="{Binding Name}"/>
<Label Grid.Row="1">Debt</Label>
<TextBox Name="txtDebt"
Grid.Column="1"
Grid.Row="1"
Text="{Binding Debt}"/>
<Label Grid.Row="2">Join Date</Label>
<DatePicker Name="dpJoin"
Grid.Column="1"
Grid.Row="2"
Text="{Binding JDate}"/>
Just set x:Name and find control you need by VisualTreeHelper.
If you need a specific control name, you can bind it like x:Name="{Binding Name}"
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;
}
And here is a sample ho do it by property name:
public static T FindVisualChildByName<T>(FrameworkElement depObj, string name) where T : FrameworkElement
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i) as FrameworkElement;
if (child != null && child is T && child.Name == name)
return (T)child;
T childItem = FindVisualChildByName<T>(child, name);
if (childItem != null)
return childItem;
}
}
return null;
}
EDIT:
There is example how to use this methods.
FindVisualChildByName<T> method have two parameters, first is the page or control instance where you will seeking for, and second is control name to find on the page.
Not sure how to get Frame or page instance in WPF, but in UWP it looks like this:
var frame = Window.Current.Content as Frame;
var textBox = VisualHelper.GetChildOfType<TextBox>(frame);
var anotherTextBox = VisualHelper.FindVisualChildByName<TextBox>(frame, "MyTextBox");
Or cast like this:
var obj = VisualHelper.FindVisualChildByName<object>(frame, "MyTextBox");
var type = obj.GetType();
var textBox = System.Convert.ChangeType(obj, type);

Get datagrid's scrollviewer

I'm trying to get the datagrid's scrollviewer to be able to set the offset (which has been stored earlier).
I use this function :
public static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
And I call it like this :
this.dataGrid.ItemsSource = _myData;
ScrollViewer sc = ressource_design.GetVisualChild<ScrollViewer>(this.dataGrid);
if (sc != null) sc.ScrollToVerticalOffset(stateDatagrid.ScrollbarOffset);
And it works in many cases, but in some cases the function returns null and I'm not able to get the scrollviewer.
This call is made just after setting the ItemsSource (ObservableCollection of items) and it works well in 90% cases. The datagrid has not been rendered yet.
I've also tried with the function :
public static ScrollViewer GetScrollViewerFromDataGrid(DataGrid dataGrid)
{
ScrollViewer retour = null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(dataGrid) && retour == null; i++)
{
if (VisualTreeHelper.GetChild(dataGrid, i) is ScrollViewer)
{
retour = (ScrollViewer)(VisualTreeHelper.GetChild(dataGrid, i));
}
}
return retour;
}
still null.
Why I'm unable to get the datagrid's scrollviewer ?
I've not pasted my datagrid's style since I have datagrids working with it and it is complicated with many dependencies.
I thought it could be related to virtualization but i'm not able to retrieve the scrollviewer of this datagrid :
<DataGrid Style="{StaticResource StyleDataGrid}" HeadersVisibility="None" ItemsSource="{Binding _Data}" Name="dataGrid1" RowDetailsVisibilityMode="Visible" SelectionChanged="dataGrid1_SelectionChanged">
You need to go recursive through the VisualTree elements. Your function only looks at DataGrid layer. If the ScrollViewer isn't there (see picture) you will not find it.
Try the following function:
public static ScrollViewer GetScrollViewer(UIElement element)
{
if (element == null) return null;
ScrollViewer retour = null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element) && retour == null; i++) {
if (VisualTreeHelper.GetChild(element, i) is ScrollViewer) {
retour = (ScrollViewer) (VisualTreeHelper.GetChild(element, i));
}
else {
retour = GetScrollViewer(VisualTreeHelper.GetChild(element, i) as UIElement);
}
}
return retour;
}

ListBox items return string, when DataTemplate is Button

I am creating a WP 8.1 Silverlight app.
I have an ObservableCollection of string names, that is set to the ItemsSource of a ListBox. Which are the names of buttons in the ListBox. I then want to extract the buttons from the ListBox, but the return value is of type string.
The xaml code is:
<ListBox x:Name="Game_ScrollViewer_online" Margin="41,104,128,6" SelectedValuePath="Current_game_button">
<ListBox.ItemTemplate>
<DataTemplate>
<Button x:Name="Current_game_button" Content="{Binding}"
HorizontalAlignment="Center" Height="80" Margin="-14,6,-15,0"
VerticalAlignment="Top" Width="210" Template="{StaticResource Button_CurrentLayout1}"
RenderTransformOrigin="0.5,0.5" Foreground="#FFCBECCB" FontFamily="Times New Roman"
Click="LoadGame_online" FontSize="16">
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I then want to extract the button element:
for (int i = 0; i < Game_ScrollViewer_online.Items.Count; i++)
{
var tempType = Game_ScrollViewer_online.Items[i].GetType();
Button tempBut = (Game_ScrollViewer_online.Items[i] as Button);
//Do stuff with button
}
But as said the return type is string.
Why is it not button ? And is there a way to access the button?
You need FrameworkTemplate.FindName Method (String, FrameworkElement) for this purpose:
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
Then:
for (int i = 0; i < Game_ScrollViewer_online.Items.Count; i++)
{
ListBoxItem GameListBoxItem = (ListBoxItem)(Game_ScrollViewer_online.ItemContainerGenerator.ContainerFromIndex(i));
ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(GameListBoxItem);
DataTemplate myDataTemplate = contentPresenter.ContentTemplate;
Button tempBut = (Button) myDataTemplate.FindName("Current_game_button", contentPresenter);
//Do stuff with button
}
To solve missing FindName use FindDescendant like this:
public T FindDescendant<T>(DependencyObject obj) where T : DependencyObject
{
if (obj is T)
return obj as T;
int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
if (childrenCount < 1)
return null;
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is T)
return child as T;
}
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = FindDescendant<T>(VisualTreeHelper.GetChild(obj, i));
if (child != null && child is T)
return child as T;
}
return null;
}

How to get ListBox ItemsPanel in code behind

I have a ListBox with an ItemsPanel
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel x:Name="ThumbListStack" Orientation="Horizontal" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
I am wanting to move the Stack Panel along the X-axis using a TranslateTransform in code behind.
Problem is, I can't find the Stack Panel.
ThumbListBox.FindName("ThumbListStack")
Returns nothing.
I want to use it in:
Storyboard.SetTarget(x, ThumbListBox.FindName("ThumbListStack"))
How do I get the Stack Panel so I can then use it with the TranslateTransform
Thanks
You can use the Loaded event for the StackPanel that is in the ItemsPanelTemplate
<Grid>
<Grid.Resources>
<Style TargetType="ListBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel x:Name="ThumbListStack" Orientation="Horizontal"
Loaded="StackPanel_Loaded" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ListBox />
</Grid>
And then in code behind
private StackPanel m_itemsPanelStackPanel;
private void StackPanel_Loaded(object sender, RoutedEventArgs e)
{
m_itemsPanelStackPanel = sender as StackPanel;
}
Another way is to traverse the Visual Tree and find the StackPanel which will be the first child of the ItemsPresenter.
public void SomeMethod()
{
ItemsPresenter itemsPresenter = GetVisualChild<ItemsPresenter>(listBox);
StackPanel itemsPanelStackPanel = GetVisualChild<StackPanel>(itemsPresenter);
}
private static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
sorry I just noticed I forgot to save the edit...I realize you've already accepted an answer, but it seems more of a hack to me. Here's my implementation of FindChild, you might want to use it for the future or if you're going to be doing this often.
public static T FindChild<T>(this FrameworkElement obj, string name)
{
DependencyObject dep = obj as DependencyObject;
T ret = default(T);
if (dep != null)
{
int childcount = VisualTreeHelper.GetChildrenCount(dep);
for (int i = 0; i < childcount; i++)
{
DependencyObject childDep = VisualTreeHelper.GetChild(dep, i);
FrameworkElement child = childDep as FrameworkElement;
if (child.GetType() == typeof(T) && child.Name == name)
{
ret = (T)Convert.ChangeType(child, typeof(T));
break;
}
ret = child.FindChild<T>(name);
if (ret != null)
break;
}
}
return ret;
}
It checks all the children and the children's children comparing the type and Name set on the control. Use it like this:
StackPanel ipanel = ThumbListBox.FindChild<StackPanel>("ThumbListStack");
if(ipanel == null)
MessageBox.Show("couldn't find anything");
else
MessageBox.Show("Aha! Found: " ipanel.Name);
Try out following extension method:
var childStackPanels = FindVisualChildren<StackPanel>(ThumbListBox);
Method itself:
public static IEnumerable<T> FindVisualChildren<T>(this DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
var typedChild = child as T;
if (typedChild != null)
{
yield return typedChild;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
PS: You can yourself extend it to check for specific control name so method would return single control instead of list.

Categories

Resources