WPF Xaml Access Custom control in DataTemplate - c#

I have a custom control `AutoCompleteTextBox in a DataTemplate as shown below:
<DataGridTextColumn.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock
Text="{Binding Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"
Margin="5" />
<Border DockPanel.Dock="Top"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Padding="2">
<uc:AutoCompleteTextBox x:Name="AutoTextBox" />
</Border>
</StackPanel>
</DataTemplate>
</DataGridTextColumn.HeaderTemplate>
How can i Access AutoTextBox in code Behind?

It is not possible to access controls in a DataTemplate directly by name! But you could try getting down the VisualTree...
DependencyObject dgColumnHeader = GetYourColumnHeader();
var yourAutoCompleteTextBox = FindVisualChild<AutoCompleteTextBox>(dgColumnHeader);
public static T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}

Related

Couldn't locate expander in listview

I am having an expander than bind in the listview.
And I have another button to collapse/ expand all expander.
In code behind, I couldn’t locate the expander by using the code: Expander exp = (Expander)listViewResult.FindResource("MyExpander");
Any idea to do so?
<ListView Name="listViewResult" Margin="0,172,-10,-491" BorderBrush="Gray" BorderThickness="1"
TextElement.FontFamily="Segoe UI" TextElement.FontSize="12"
Background="White"
GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler" >
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource GridViewColumnHeaderStyle}" >
……
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander Name="MyExpander" IsExpanded="False">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" FontWeight="Bold" VerticalAlignment="Bottom" />
</StackPanel>
</Expander.Header>
<ItemsPresenter Margin="20,0,0,0" />
<!--<ItemsPresenter />-->
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
In order to expand/ collapse all expander, you could locate all the expanders by finding all child in the collection
Calling the method:
Collection<Expander> collection = FindVisualChild<Expander>(listViewResult);
foreach (Expander expander in collection)
{
expander.IsExpanded = true;
}
The Method:
private static Collection<T> FindVisualChild<T>(DependencyObject current) where T : DependencyObject
{
if (current == null)
{
return null;
}
var children = new Collection<T>();
FindVisualChild (current, children);
return children;
}
private static void FindVisualChild<T>(DependencyObject current, Collection<T> children) where T : DependencyObject
{
if (current != null)
{
if (current.GetType() == typeof(T))
{ children.Add((T)current); }
for (int i = 0; i < System.Windows.Media.VisualTreeHelper.GetChildrenCount(current); i++)
{
FindVisualChild (System.Windows.Media.VisualTreeHelper.GetChild(current, i), children);
}
}
}
Use following method to find the controls within your Visual tree.
public static Visual GetDescendantByName (Visual element, string name)
{
if (element == null) return null;
if (element is FrameworkElement
&& (element as FrameworkElement).Name == name) return element;
Visual result = null;
if (element is FrameworkElement)
(element as FrameworkElement).ApplyTemplate();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
result = GetDescendantByName(visual, name);
if (result != null)
break;
}
return result;
}
where element will be your ListView and name will be name of the FrameworkElement you are looking for.
Try using x:Name property to define name for your expander and below code can help:
Expander exp = (Expander)listViewResult.FindName("MyExpander");

how to access elements in a StackPanel in listbox

I have a ListBox that contains a StackPanel which contains an Image and a TextBlock. I want to access the TextBlock via c# (code-behind) to change it's font manually.
Relevent XAML:
<ListBox x:Name="CategoriesListBox" Margin="0,0,-12,0" ItemsSource="{Binding Categories}" SelectionChanged="CategoriesListBox_SelectionChanged" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432" Height="62">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image x:Name="catImage" Source="{Binding icon}"/>
<TextBlock x:Name="catName" Grid.Column="1" Text="{Binding name_shown}" TextWrapping="Wrap" Style="{StaticResource PhoneTextLargeStyle}"/>
</Grid>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I need to access the "catName" TextBlock for all cells.
try this--->
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;
}
u can use above method in this way--->
ListBoxItem item = this.list.ItemContainerGenerator.ContainerFromIndex(2) as ListBoxItem;
TextBlock txt = FindFirstElementInVisualTree<TextBlock>(item);
txt.Text = "some text";

WP7/ XAML change image source on app start if theme is light

my image is inside the datatemplate, what I want to do it is change the image if the wp theme is light
<DataTemplate x:Key="citiesItemTemplate">
<StackPanel Grid.Column="1" VerticalAlignment="Top">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Height="50" Tap="ProgLngGropus_Tap" Text="{Binding Name}" FontSize="26" Margin="12,-5,12,6"/>
<ToggleButton Grid.Column="2" x:Name="MyToggleButton" Style="{StaticResource FlipButton}">
<ToggleButton.Content>
<Image Grid.Column="2" Margin="0,-10,-33,0" Height="40" Width="40" x:Name="ArrowDownImg" Source="/Images/appbar.dark.arrow.down.circle.rest.png" />
</ToggleButton.Content>
</ToggleButton>
<TextBlock TextWrapping="Wrap" Text="{Binding Lang}" Grid.Column="0" Grid.Row="1" x:Name="Desc"
Foreground="Orange" Visibility="{Binding ElementName=MyToggleButton,
Path=IsChecked, Converter={StaticResource ValueConverterBoolToVis}}">
</TextBlock>
</Grid>
</StackPanel>
</DataTemplate>
But Im unable to access ArrowDownImg
Visibility darkBackgroundVisibility = (Visibility)Application.Current.Resources["PhoneDarkThemeVisibility"];
if (darkBackgroundVisibility != Visibility.Visible)
{
//Error in finding ArrowDownImg
***//ArrowDownImg.Source = "/Images/appbar.light.arrow.down.circle.rest.png"***
}
You could also do
Image arrowDownImg = MyObjectUsingTheDataTemplate.FindVisualChild("ArrowDownImg") as Image;
arrowDownImg.Source = ...
by using the following extension methods
public static FrameworkElement FindVisualChild(this FrameworkElement root, string name)
{
FrameworkElement temp = root.FindName(name) as FrameworkElement;
if (temp != null)
return temp;
foreach (FrameworkElement element in root.GetVisualDescendents())
{
temp = element.FindName(name) as FrameworkElement;
if (temp != null)
return temp;
}
return null;
}
public static IEnumerable<FrameworkElement> GetVisualDescendents(this FrameworkElement root)
{
Queue<IEnumerable<FrameworkElement>> toDo = new Queue<IEnumerable<FrameworkElement>>();
toDo.Enqueue(root.GetVisualChildren());
while (toDo.Count > 0)
{
IEnumerable<FrameworkElement> children = toDo.Dequeue();
foreach (FrameworkElement child in children)
{
yield return child;
toDo.Enqueue(child.GetVisualChildren());
}
}
}
public static IEnumerable<FrameworkElement> GetVisualChildren(this FrameworkElement root)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
{
yield return VisualTreeHelper.GetChild(root, i) as FrameworkElement;
}
}
Answer: Here is what I did
I have added the loaded event
<ToggleButton Grid.Row="0" Grid.Column="2" Margin="0,-10,-33,0" x:Name="MyToggleButton" Style="{StaticResource FlipButton}">
<ToggleButton.Content>
<Image Loaded="ArrowDownImg_Loaded" Height="50" Width="50" x:Name="ArrowDownImg" />
</ToggleButton.Content>
</ToggleButton>
and in the event:
private void ArrowDownImg_Loaded(object sender, RoutedEventArgs e)
{
Visibility darkBackgroundVisibility = (Visibility)Application.Current.Resources["PhoneDarkThemeVisibility"];
var ArrowDownImg = sender as Image;
if (darkBackgroundVisibility != Visibility.Visible)
{
ArrowDownImg.Source = new BitmapImage(new Uri("/Images/appbar.arrow.down.circle.dark.rest.png", UriKind.Relative));
}
else
{
ArrowDownImg.Source = new BitmapImage(new Uri("/Images/appbar.arrow.down.circle.light.rest.png", UriKind.Relative));
}
}

How to access a specific item in a Listbox with DataTemplate?

I have a ListBox including an ItemTemplate with 2 StackPanels.
There is a TextBox in the second StackPanel i want to access.
(Change it's visibility to true and accept user input)
The trigger should be the SelectionChangedEvent. So, if a user clicks on an ListBoxItem, the TextBlock gets invisible and the TextBox gets visible.
XAML CODE:
<ListBox Grid.Row="1" Name="ContactListBox" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ItemsSource="{Binding Contacts}" Margin="0,36,0,0" SelectionChanged="ContactListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Edit Contact" Click="ContactMenuItem_Click"/>
<toolkit:MenuItem Header="Delete Contact" Click="ContactMenuItem_Click"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<Grid>
<Rectangle Fill="{StaticResource PhoneAccentBrush}"
Width="72" Height="72">
<Rectangle.OpacityMask>
<ImageBrush ImageSource="/Images/defaultContactImage.png" Stretch="UniformToFill"/>
</Rectangle.OpacityMask>
</Rectangle>
</Grid>
<StackPanel>
<TextBox Text="{Binding Name}" TextWrapping="Wrap" Visibility="Collapsed"/>
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}" />
<TextBlock Text="{Binding Number}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextAccentStyle}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I guess there are several ways to solve this, but nothing I tried worked.
My current approach looks like this
private void ContactListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListBoxItem listBoxItem = ContactListBox.SelectedItem as ListBoxItem;
DataTemplate listBoxTemplate = listBoxItem.ContentTemplate;
// How to access the DataTemplate content?
StackPanel outerStackPanel = listBoxTemplate.XXX as StackPanel;
StackPanel innerStackPanel = outerStackPanel.Children[1] as StackPanel;
TextBox nameBox = innerStackPanel.Children[0] as TextBox;
TextBlock nameBlock = innerStackPanel.Children[1] as TextBlock;
nameBox.Visibility = System.Windows.Visibility.Visible;
nameBlock.Visibility = System.Windows.Visibility.Collapsed;
}
Thank you for your help guys!! Finally i got it. Solved the problem with the VisualTreeHelper. What a great function ^^
private void ContactListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ContactListBox.SelectedIndex == -1)
return;
currentSelectedListBoxItem = this.ContactListBox.ItemContainerGenerator.ContainerFromIndex(ContactListBox.SelectedIndex) as ListBoxItem;
if (currentSelectedListBoxItem == null)
return;
// Iterate whole listbox tree and search for this items
TextBox nameBox = helperClass.FindDescendant<TextBox>(currentSelectedListBoxItem);
TextBlock nameBlock = helperClass.FindDescendant<TextBlock>(currentSelectedListBoxItem);
helperFunction
public T FindDescendant<T>(DependencyObject obj) where T : DependencyObject
{
// Check if this object is the specified type
if (obj is T)
return obj as T;
// Check for children
int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
if (childrenCount < 1)
return null;
// First check all the children
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is T)
return child as T;
}
// Then check the childrens children
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;
}
With this edited function you can also search for control by name (its converted from VB.NET):
public T FindDescendantByName<T>(DependencyObject obj, string objname) where T : DependencyObject
{
string controlneve = "";
Type tyype = obj.GetType();
if (tyype.GetProperty("Name") != null) {
PropertyInfo prop = tyype.GetProperty("Name");
controlneve = prop.GetValue((object)obj, null);
} else {
return null;
}
if (obj is T && objname.ToString().ToLower() == controlneve.ToString().ToLower()) {
return obj as T;
}
// Check for children
int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
if (childrenCount < 1)
return null;
// First check all the children
for (int i = 0; i <= childrenCount - 1; i++) {
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child is T && objname.ToString().ToLower() == controlneve.ToString().ToLower()) {
return child as T;
}
}
// Then check the childrens children
for (int i = 0; i <= childrenCount - 1; i++) {
string checkobjname = objname;
DependencyObject child = FindDescendantByName<T>(VisualTreeHelper.GetChild(obj, i), objname);
if (child != null && child is T && objname.ToString().ToLower() == checkobjname.ToString().ToLower()) {
return child as T;
}
}
return null;
}
I can't give you a complete answer...
But I think you can use the VisualTreeHelper to iterate through the children of any control
http://blogs.msdn.com/b/kmahone/archive/2009/03/29/visualtreehelper.aspx
However, for the effect you are looking for, then I think using the SelectedItem Style might be a better solution - e.g. see this article - http://joshsmithonwpf.wordpress.com/2007/07/30/customizing-the-selected-item-in-a-listbox/
Use ItemContainerGenerator.
private void ContactListBox_SelectionChanged
(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count == 1)
{
var container = (FrameworkElement)ContactListBox.ItemContainerGenerator.
ContainerFromItem(e.AddedItems[0]);
StackPanel sp = container.FindVisualChild<StackPanel>();
TextBox tbName = (TextBox) sp.FindName("tbName");
TextBlock lblName = (TextBlock)sp.FindName("lblName");
TextBlock lblNumber = (TextBlock)sp.FindName("lblNumber");
}
}
Since DataTemplate is a generic template that could be used many times in the code, there is no way to access it by name (x:Name="numberTextBox").
I solved similar problem to this by making a collection of Controls - while Listbox was populating I add Textbox control to the collection.
string text = myCollectionOfTextBoxes[listbox.SelectedIndex].Text;
Till I found a better soultion - Tag property. In your ListboxItem you bind Tag property to the name
Tag="{Binding Name}"
and the to access it
ListBoxItem listBoxItem = ContactListBox.SelectedItem as ListBoxItem;
string name = listBoxItem.Tag.ToString();

Find control inside Listbox.ItemTemplate (WPF C#)

I have some problems finding the right TextBlock control inside a StackPanel.
My markup:
<ListBox Name="lstTimeline" ItemContainerStyle="{StaticResource TwItemStyle}"
MouseDoubleClick="lstTimeline_MouseDoubleClick">
<ListBox.ItemTemplate>
<DataTemplate>
<DockPanel MaxWidth="{Binding ElementName=lstTimeline, Path=ActualWidth}">
<Border Margin="10" DockPanel.Dock="Left" BorderBrush="White"
BorderThickness="1" Height="48" Width="48" HorizontalAlignment="Center">
<Image Source="{Binding ThumbNail, IsAsync=True}" Height="48" Width="48" />
</Border>
<StackPanel Name="stkPanel" Margin="10" DockPanel.Dock="Right">
<TextBlock Text="{Binding UserName}" FontWeight="Bold" FontSize="18" />
<TextBlock Text="{Binding Text}" Margin="0,4,0,0" FontSize="14"
Foreground="#c6de96" TextWrapping="WrapWithOverflow" />
<TextBlock Text="{Binding ApproximateTime}" FontSize="14"
FontFamily="Georgia" FontStyle="Italic" Foreground="#BBB" />
<TextBlock Text="{Binding ScreenName}" Name="lblScreenName" FontSize="14"
FontFamily="Georgia" FontStyle="Italic" Foreground="#BBB"
Loaded="lblScreenName_Loaded" />
</StackPanel>
</DockPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My double click code:
private void lstTimeline_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ListBoxItem lbi = (lstTimeline.SelectedItem as ListBoxItem);
StackPanel item = lbi.FindName("stkPanel") as StackPanel;
if (item != null)
MessageBox.Show("StackPanel null");
TextBlock textBox = item.FindName("lblScreenName") as TextBlock;
if (textBox != null)
MessageBox.Show("TextBlock null");
MessageBox.Show(textBox.Text);
}
But the StackPanel is null. How do find the right TextBlock in SelectedItem?
Thanks for your help.
ListBoxItem myListBoxItem = (ListBoxItem)(lstUniqueIds.ItemContainerGenerator.ContainerFromIndex(lstUniqueIds.SelectedIndex));
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(myListBoxItem);
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
CheckBox target = (CheckBox)myDataTemplate.FindName("chkUniqueId", myContentPresenter);
if (target.IsChecked)
{
target.IsChecked = false;
}
else
{
target.IsChecked = true;
}
Function FindVisualChild can be found on the MSDN page FrameworkTemplate.FindName Method:
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;
}
There's a specific function to use when you're looking for something whose name is defined in a template. Try it like this:
private void lstTimeline_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
ListBoxItem lbi = (lstTimeline.SelectedItem as ListBoxItem);
StackPanel item = Template.FindName("stkPanel",lbi) as StackPanel;
if (item != null)
MessageBox.Show("StackPanel null");
TextBlock textBox = Template.FindName("lblScreenName",item) as TextBlock;
if (textBox != null)
MessageBox.Show("TextBlock null");
MessageBox.Show(textBox.Text);
}
Linq to xml with a get and set model.
var item = ...
lstTimeline.SelectedIndex = -1;
lstTimeline.ItemsSource = item;

Categories