Get XAML controls by their bound properties? - c#

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

Related

Accessing the children of an ItemsControl [duplicate]

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 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.

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

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 XAML element by name stored in a string variable?

For example, I have an UIElement:
<TextBlock Name="sometextblock" Text="sample text"/>
In the code I have a string variable with that name:
string elementName = "sometextblock";
How to get this element, using this variable? I need to get access to element's properties, for example, i need to be able to change a Text property.
How to do this?
Thank you!
If you have named elements in your XAML as follows:
<TextBlock x:Name="sometextblock" />
You can find them via the FindName method:
TextBlock txt = this.FindName("sometextblock") as TextBlock;
string elementName = txt.xyzproperty //do what you want with using txt.xyz property
Assuming you've this on your XAML:
<Button Name="MyButton1" .../>
<Button Name="MyButton2" .../>
<Image Name="MyImage1" .../>
<TextBox Name="MyTextBox1" .../>
<TextBox Name="MyTextBox2" .../>
<Button Name="MyButton3" .../>
You must put the name of your controls inside an array of string, so:
string[] NameControls = { "MyButton1", "MyButton2", "MyImage1", "MyTextBox1", "MyTextBox2", "MyButton3" };
Then you can iterate the controls and access the properties:
for (int i = 0; i < NameControls.Length; i++)
{
dynamic MyControl = this.FindName(NameControls[i]);
// do something
}
For example in my case I need to change the Opacity of determinated controls so, inside the for block I've put this:
dynamic MyControl = this.FindName(NameControls[i]);
MyControl.Opacity = 0.7;
you can use this method:
var textBlock = FindChild<TextBlock>(Application.Current.RootVisual, "sometextblock");
and the FindChild method is :
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);
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
var 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;
}
// Need this in case the element we want is nested
// in another element of the same type
foundChild = FindChild<T>(child, childName);
}
else
{
// child element found.
foundChild = (T) child;
break;
}
}
return foundChild;
}
}
You can refer to this method:
public static bool FindVisualChildByName<T>(this DependencyObject parent, string name, out T control) where T : DependencyObject
{
if (parent == null)
throw new ArgumentNullException(nameof(parent), "Control cấp cha không được null.");
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentNullException(nameof(name), "Tên của control cần tìm không được null hoặc empty.");
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
var controlName = child.GetValue(FrameworkElement.NameProperty) as string;
if (controlName == name)
{
control = child as T;
return true;
}
if (FindVisualChildByName(child, name, out control)) return true;
}
control = null;
return false;
}

Categories

Resources