How to get item container (e.g. stackpanel) from listbox - c#

I have code like this
<ListBox x:Name="filterListBox" Height="60">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
VirtualizingStackPanel.VirtualizationMode="Standard"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="TargetPanel">
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Background>
<SolidColorBrush />
</ListBox.Background>
</ListBox>
and i get first listbox item with this
object item = filterListBox.ItemContainerGenerator.ContainerFromIndex(0);
ListBoxItem lbi = item as ListBoxItem;
Now I need to get this stackpanel called "TargetPanel" but I don't know how. Can you please help.

There's not a built in way to do this with a simple method call unfortunately. However, it's easy enough. With a few modifications to this example, you can get a particular child of the DataTemplate by name:
// the call
var item = filterListBox.ItemContainerGenerator
.ContainerFromIndex(0) as ListBoxItem;
var sp = FindVisualChild<StackPanel>(item, "TargetPanel");
// the variable sp should be set to the TargetPanel for the item now
The code you'll need to add somewhere (it could be in a "helper" class):
using System.Windows.Media; // for VisualTreeHelper
private static TChildItem FindVisualChild<TChildItem>(DependencyObject obj,
string matchName = "") where TChildItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is TChildItem)
{
// match by name
var childName = child.GetValue(FrameworkElement.NameProperty) as string;
if (!string.IsNullOrWhiteSpace(matchName))
{
if (matchName == childName)
{
return (TChildItem)child;
}
}
else
{
return (TChildItem)child;
}
}
else
{
var childOfChild = FindVisualChild<TChildItem>(child, matchName);
if (childOfChild != null)
{
return childOfChild;
}
}
}
return null;
}

Related

How to access nested StackPanel

I am using interactive data display lib, and I want to generate my charts dynamically. Unfortunatelly I can't access one of the StackPanels by its name:
<Grid>
<d3:Chart Name="plotter">
<d3:Chart.Title>
<TextBlock HorizontalAlignment="Center" FontSize="18" Margin="0,5,0,5">Line graph legend sample</TextBlock>
</d3:Chart.Title>
<d3:Chart.LegendContent>
<d3:LegendItemsPanel>
<d3:LegendItemsPanel.Resources>
<DataTemplate x:Key="InteractiveDataDisplay.WPF.LineGraph">
<StackPanel Name="chartPanel" Orientation="Horizontal">
</StackPanel>
</DataTemplate>
</d3:LegendItemsPanel.Resources>
</d3:LegendItemsPanel>
</d3:Chart.LegendContent>
<Grid Name="lines"/>
</d3:Chart>
</Grid>
And when I am trying to access it by name (chartPanel.method()) it seems that it doesnt exist.
Implement this in your Code
private DependencyObject FindChildControl<T>(DependencyObject control, string ctrlName)
{
int childNumber = VisualTreeHelper.GetChildrenCount(control);
for (int i = 0; i < childNumber; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(control, i);
FrameworkElement fe = child as FrameworkElement;
// Not a framework element or is null
if (fe == null) return null;
if (child is T && fe.Name == ctrlName)
{
// Found the control so return
return child;
}
else
{
// Not found it - search children
DependencyObject nextLevel = FindChildControl<T>(child, ctrlName);
if (nextLevel != null)
return nextLevel;
}
}
return null;
After that u can search the stackpanel with this Method:
StackPanel chartPanel= FindChildControl<StackPanel>(plotter, "chartPanel") as StackPanel;
now you can edit your Stackpanel.

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);

Error: "This operation is valid only on elements that have this template applied"

I have a C# WPF application that contains a user control:
<UserControl
x:Name="payrollEntryControl"
x:Class="MyNamespace.PayrollEntryControl"
[...]
>
[...]
</UserControl>
Within the user control, I have a Telerik RadDataForm:
<telerik:RadDataForm
x:Name="payrollAddForm"
CurrentItem="[...]"
EditTemplate="{StaticResource myEditTemplate}"
/>
The template contains a Telerik RadGridView and a Button:
<telerik:RadGridView Grid.Row="0" Grid.Column="0"
x:Name="workGridView"
[...]
ItemsSource="{Binding [...]}"
>
<telerik:RadGridView.Columns>
[...]
</telerik:RadGridView.Columns>
</telerik:RadGridView>
<Button Grid.Row="1" Grid.Column="0"
Command="{Binding addWorkCommand, ElementName=payrollEntryControl}"
>
Add
</Button>
I want the command to do is call BeginInsert() on workGridView. But I can't seem to get access to workGridView.
My command, so far:
private DelegateCommand addWorkCommand_ = null;
public DelegateCommand addWorkCommand
{
get
{
if (this.addWorkCommand_ == null)
{
this.addWorkCommand_ = new DelegateCommand(
o => addWork(o)
);
}
return this.addWorkCommand_;
}
}
private void addWork(object o)
{
var addForm = this.payrollAddForm;
var editTemplate = addForm.EditTemplate;
var workGrid = editTemplate.FindName("workGridView", addForm);
}
My problem? When I make the call to editTemplate.FindName(), I get an exception:
This operation is valid only on elements that have this template applied.
I don't understand. I'm getting the template from the form. How can it not be applied?
Peter Duniho's comment pointed me to this answer, which addressed my problem.
This method will help you:
public T FindElementByName<T>(FrameworkElement element, string sChildName) where T : FrameworkElement
{
T childElement = null;
var nChildCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < nChildCount; i++)
{
FrameworkElement child = VisualTreeHelper.GetChild(element, i) as FrameworkElement;
if (child == null)
continue;
if (child is T && child.Name.Equals(sChildName))
{
childElement = (T)child;
break;
}
childElement = FindElementByName<T>(child, sChildName);
if (childElement != null)
break;
}
return childElement;
}
And, how I use it, just add button, and on button Click:
private void Button_OnClick(object sender, RoutedEventArgs e)
{
var element = FindElementByName<ComboBox>(ccBloodGroup, "cbBloodGroup");
}
[1]: https://stackoverflow.com/a/19907800/243563
An alternative is to pass the workGridView as a CommandParameter:
<Button Grid.Row="1" Grid.Column="0"
CommandParameter="{Binding ElementName=workGridView}"
Command="{Binding addWorkCommand}" >
....
private void addWork(object o)
{
RadGridView grid = o as RadGridView;
grid.BeginInsert();
}

Find child element from ListBox wp8

I am trying to find child element from Listbox. Listbox contains Itemtemplate. Here is my design.
I have created UserControl. In that I have added ListBox.
I am showing this control as pop up. Here is code for pop up
GlobalSettings.popup = new Popup();
//GlobalSettings.popup.VerticalOffset = 50;
FilesListControl popupcontrol = new FilesListControl();
popupcontrol.Height = 480;
popupcontrol.Width = 480;
GlobalSettings.popup.Child = popupcontrol;
popupcontrol.fileListbox.ItemsSource = filesList;
LayoutRoot.IsHitTestVisible = false;
GlobalSettings.popup.IsOpen = true;
//Here I need to create checkbox. so thats why I need to find the child elemnt of listbox
popupcontrol.btnDone.Click += (s, args) =>
{
};
Here code from FilesListControl
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Auto">
<ListBox Name="fileListbox">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox Name="chkFile" CommandParameter="{Binding value}" Content="{Binding Key}" Click="chkFile_Click" FontFamily="Segoe WP SemiLight"></CheckBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
I want to find the CheckBox i.e. chkFile. Here is my code
ListBoxItem item = popupcontrol.fileListbox.ItemContainerGenerator.ContainerFromIndex(1) as ListBoxItem;
CheckBox chk = FindFirstElementInVisualTree<CheckBox>(item);
private T FindFirstElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
{
var count = VisualTreeHelper.GetChildrenCount(parentElement);
if (count == 0)
return null;
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parentElement, i);
if (child != null && child is T)
{
return (T)child;
}
else
{
var result = FindFirstElementInVisualTree<T>(child);
if (result != null)
return result;
}
}
return null;
}
But nothing is getting. What I did wrong? How can I access CheckBox Click event?

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