WPF replace UIElement with another - c#

If I have
<StackPanel>
<Button name="btn"/>
</StackPanel>
how could I replace Button btn for a different control when I press that button? In this case I know the parent is a StackPanel so I could cast the parent to a stackPanel and then replace that child. but what if I have a Border?
<Border>
<Button name="btn"/>
</Border>
Thanks to McGarnagle I created this extension method
how can I replace a Button with my custom control without knowing in advance the parent of the button?
Edit
public static void ReplaceWith(this FrameworkElement elementToReplace, FrameworkElement newControl)
{
newControl.Width = elementToReplace.Width;
newControl.Height = elementToReplace.Height;
newControl.Margin = elementToReplace.Margin;
// get parent of control
var parent = elementToReplace.Parent;
if (parent is Panel)
{
var panel = (Panel)parent;
for (var i = 0; i < panel.Children.Count; i++)
{
if (panel.Children[i] == elementToReplace)
{
panel.Children.RemoveAt(i);
panel.Children.Insert(i, newControl);
break;
}
}
}
else if (parent is Decorator)
{
((Decorator)parent).Child = newControl;
}
else if (parent is ContentControl)
{
((ContentControl)parent).Content = newControl;
}
else
{
if(Debugger.IsAttached)
Debugger.Break();
throw new NotImplementedException("Missing other possibilities to implement");
}
}

I suppose you could cycle through the possible base classes. There aren't that many: StackPanel is a Panel, Border is a Decorator, …
var parent = btn.Parent;
var replacement = new TextBlock { Text = "replacement" };
if (parent is Panel)
{
var panel = (Panel)parent;
panel.Children.Remove(btn);
panel.Children.Add(replacement);
}
else if (parent is Decorator)
{
((Decorator)parent).Child = replacement;
}
else if (parent is ContentControl)
{
((ContentControl)parent).Content = replacement;
}

All containers are Panel or ContentControl or UserControl. So you can check if parent is inherited from one of these controls.

Yo can get the logical parent of Control and check it's type using the operator is like this:
var parent = MyButton.Parent;
if (parent is StackPanel)
{
// do something with StackPanel
}
if (parent is Border)
{
// do something with Border
}

Related

How to find view by tag?

I have a Form that contains a few TableLayoutPanel (List Panels) that I create dynamically.
Each TableLayoutPanel has a unique Tag. Actually each panel has Lable and this Lable has Name = "lable_name"
I need to update this exact Label inside a TableLayoutPanel.
public void UpdateLable(string tag, string newText)
{
foreach(var tlp in Views)
{
if (tlp.Tag.ToString().Equals(tag))
{
var lable = tlp.findViewByName("lable_name") as Label;
lable.Text = newText;
}
}
}
But I can't find method like findViewByName()
So, question is - how to find view by name?
Create a method as below
public static IEnumerable<Control> GetControlsOfType<T>(Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetControlsOfType<T>(ctrl)).Concat(controls).Where(c => c is T);
}
Use it like
Var control= GetControlsOfType<Label>(yourView).FirstOrDefault(x => x.Tag == tag);
if(control != null)
control.Text = newText
there is no such method directly in TableLayoutPanel class. But each Control has Controls property - collection of child controls.
that specialized collection has Find method which allows to get child by name:
var label = tlp.Controls.Find("lable_name", true)[0] as Label;

Find all TextBox controls in UWP Page

I need to find all TextBox(es) that are on a UWP Page but having no luck. I thought it would be a simple foreach on Page.Controls but this does not exist.
Using DEBUG I am able to see, for example, a Grid. But I have to first cast the Page.Content to Grid before I can see the Children collection. I do not want to do this as it may not be a Grid at the root of the page.
Thank you in advance.
UPDATE: This is not the same as 'Find all controls in WPF Window by type'. That is WPF. This is UWP. They are different.
You're almost there! Cast the Page.Content to UIElementCollection, that way you can get the Children collection and be generic.
You'll have to make your method recurse and look either for Content property if element is a UIElement or Children if element is UIElementCollection.
Here's an example:
void FindTextBoxex(object uiElement, IList<TextBox> foundOnes)
{
if (uiElement is TextBox)
{
foundOnes.Add((TextBox)uiElement);
}
else if (uiElement is Panel)
{
var uiElementAsCollection = (Panel)uiElement;
foreach (var element in uiElementAsCollection.Children)
{
FindTextBoxex(element, foundOnes);
}
}
else if (uiElement is UserControl)
{
var uiElementAsUserControl = (UserControl)uiElement;
FindTextBoxex(uiElementAsUserControl.Content, foundOnes);
}
else if (uiElement is ContentControl)
{
var uiElementAsContentControl = (ContentControl)uiElement;
FindTextBoxex(uiElementAsContentControl.Content, foundOnes);
}
else if (uiElement is Decorator)
{
var uiElementAsBorder = (Decorator)uiElement;
FindTextBoxex(uiElementAsBorder.Child, foundOnes);
}
}
Then you call that method with:
var tb = new List<TextBox>();
FindTextBoxex(this, tb);
// now you got your textboxes in tb!
You can also use the following generic method from the VisualTreeHelper documentation to get all your child controls of a given type:
internal static void FindChildren<T>(List<T> results, DependencyObject startNode)
where T : DependencyObject
{
int count = VisualTreeHelper.GetChildrenCount(startNode);
for (int i = 0; i < count; i++)
{
DependencyObject current = VisualTreeHelper.GetChild(startNode, i);
if ((current.GetType()).Equals(typeof(T)) || (current.GetType().GetTypeInfo().IsSubclassOf(typeof(T))))
{
T asType = (T)current;
results.Add(asType);
}
FindChildren<T>(results, current);
}
}
It basically recursively get the children for the current item and add any item matching the requested type to the provided list.
Then, you just have to do the following somewhere to get your elements:
var allTextBoxes = new List<TextBox>();
FindChildren(allTextBoxes, this);
To my mind, you could do it in the same way as in WPF. Because UWP uses mostly the same XAML that WPF.
So, please check out answer for the same question about WPF

how to access control located in DataTemplate from a control located outside DataTemplate tag

Situation : i have a textbox control located within DataTemplate of <phone:Panorama.TitleTemplate> tags
<phone:Panorama.TitleTemplate>
<DataTemplate>
<TextBlock Text="select your problem" Margin="7,40,0,0"
FontSize="60" Foreground="{StaticResource PhoneForegroundBrush}"/>
</DataTemplate>
</phone:Panorama.TitleTemplate>
now i have another button located outside of DataTemplate tag and within LayoutRoot Grid tag . this button has a click event whose definition is present in the code behind cs file .
Problem :
i want to access the textbox within the event handler of this button . How do i do it ?
You could use the VisualTreeHelper.
Try this snippet which is for a listbox, you could modify it:
public string option_selected = "";
public int check_count = 0;
public void SearchElement(DependencyObject targeted_control)
{
var count = VisualTreeHelper.GetChildrenCount(targeted_control); // targeted_control is the listbox
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(targeted_control, i);
if (child is TextBlock) // specific/child control
{
TextBlock targeted_element = (TextBlock)child;
if (targeted_element.IsChecked == true)
{
if (targeted_element.Tag!= null)
{
option_selected = targeted_element.Tag.ToString();
}
return;
}
}
else
{
SearchElement(child);
}
}
}
else
{
return;
}
}
This would be a great sample you could go through How to access a specific item in a Listbox with DataTemplate?
Hope it helps!

How to access controls inside an ItemTemplate

Using Windows Phone 8, C#.
What I've done is basically done is edited the pivot item. I've named it MainPivot and inside that I've edited the Pivot Item Title and added a TextBlock inside it called PivotTitletxt. XAML for that is:
<DataTemplate x:Key="DataTemplate3">
<TextBlock x:Name="PivotTitletxt" Height="34" TextWrapping="Wrap" Text="{Binding}" Width="447"/>
</DataTemplate>
How can I access this e.g. when setting opacity or changing foreground? so that I can use it on my MainPage like e.g. PivotTitletxt.Opacity = 30; ...
Thanks!
The link #Sankarann gave you is a pretty good example.
I'll try to put it on your scenario:
Your MainPivot has PivotItems right? So What you have to do on the Loaded event is:
var _mainPivot = MainPivot as Pivot
foreach (var _pivotItem in _mainPivot.Items)
{
var _container = _mainPivot.ItemContainerGenerator.ContainerFromItem(_pivotItem);
var _children = AllChildren(_container)
var _name = "PivotTitletxt";
var _control = (TextBlock)_Children.first(x=>x.Name == _name);
_control.Opacity = 30;
}
Then copy the AllChildren method exactly as the it is in the site.
The code above, might have a few adjustments because I've done it without VS...
Hope it helps.
Regards,
============ new answer ==============
Find all controls in WPF Window by type
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
Then try :
TextBlock xx = FindVisualChildren<TextBlock>(mainPivot).FirsOrDefault(x=>x.name=="PivotTitletxt");
if(xx!=null)
xx.opacity = 30
Once again, this come might need some correction...i'm doing it by heart, without VS.
Try it out

WPF TreeView Scrolled to Bottom automatically?

WPF default TreeView is scrolled to bottom of the node automatically where as we need to show the top view of the tree view. How to do that?
Also I could not get the scroll viewer by walking down the Visual Tree.
Preselect top node and call TreeViewItem.BringIntoView method on selection changed event. Call TreeView.ItemContainerGenerator.ContainerFromItem(e.NewValue) to get hold of the TreeViewItem.
This code is very rough.
The key to getting the TreeViewItem.BringIntoView() to get an item to the top, is to first scroll the TreeView to the bottom rather than the top.
To do this, we need to access the ScrollViewer inside the TreeView's control template first. Lots of messing around IMO, that should have been provided in the framework from the outset.
Your item control in this case, should be your TreeViewItem that you are trying to get to the top.
The uxTree control is the TreeView.
item.IsSelected = true;
ScrollViewer scroller = (ScrollViewer)this.FindVisualChildElement(this.uxTree, typeof(ScrollViewer));
scroller.ScrollToBottom();
item.BringIntoView();
private FrameworkElement FindVisualChildElement(DependencyObject element, Type childType)
{
int count = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < count; i++)
{
var dependencyObject = VisualTreeHelper.GetChild(element, i);
var fe = (FrameworkElement)dependencyObject;
if (fe.GetType() == childType)
{
return fe;
}
FrameworkElement ret = null;
if (fe.GetType().Equals(typeof(ScrollViewer)))
{
ret = FindVisualChildElement((fe as ScrollViewer).Content as FrameworkElement, childType);
}
else
{
ret = FindVisualChildElement(fe, childType);
}
if (ret != null)
{
return ret;
}
}
return null;
}

Categories

Resources