Finding the dialog a silverlight FrameworkElement lives in? - c#

I have a silverlight app consisting of several dialogs each with a collection of FrameworkElements in it.
Is it possible to find the dialog in which a Framework element is in?

You can use the VisualTreeHelper. The code below is what I use to find the Page in a WPF application. You can replace Page with whatever container you need in Silverlight, maybe Popup.
var parent = VisualTreeHelper.GetParent(this);
while (!(parent is Page))
{
parent = VisualTreeHelper.GetParent(parent);
}

http://forums.silverlight.net/p/55369/142519.aspx has a method to simplify the above example code and make it generic-friendly:
public static class ControlFinder
{
public static T FindParent<T>(this UIElement control) where T: UIElement
{
UIElement p = VisualTreeHelper.GetParent(control) as UIElement;
if (p != null)
{
if (p is T)
return p as T;
else
return ControlFinder.FindParent<T>(p);
}
return null;
}
}
Use it like:
var page = myElement.FindParent<Page>();

Yes it is possible. If you know the structure of your control, then you can user FrameworkElement.GetParent() or else you can use Tree-traversal algorithms like BFS or DFS to find the your framework element.

Related

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

Make Copy of xaml child elements

I am working on a windows phone app. I want to copy children of one canvas to other canvas. I can do it with the following code but the problem is I have to remove it from one canvas first. Code is:
private void add_template_Click(object sender, RoutedEventArgs e)
{
var childrenList = Template_canvas1.Children.Cast<UIElement>().ToArray();
root.Children.Clear();
foreach (var c in childrenList)
{
Template_canvas1.Children.Remove(c);
root.Children.Add(c);
}
}
I want to keep these elements on both the canvas. Is there another way?
Instead of trying to add the same Template_canvas1.Children to the root canvas, first make a copy of those Children and then add the copy to the root canvas.
public static T CloneXaml<T>(T source)
{
string xaml = XamlWriter.Save(source);
StringReader sr = new StringReader(xaml);
XmlReader xr = XmlReader.Create(sr);
return (T)XamlReader.Load(xr);
}
Then change your loop to:
foreach (var c in childrenList)
{
var copy = CloneXaml(c);
root.Children.Add(copy);
}
I haven't tested this code, so you may have to modify it a bit, but it should put you in the right direction.
Alternatively, you can probably use the code below which is copied from Dr Herbie's answer:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml;
using System.Reflection;
using Windows.UI.Xaml.Controls;
namespace UIElementClone {
public static class UIElementExtensions {
public static T DeepClone<T>(this T source) where T : UIElement {
T result; // Get the type
Type type = source.GetType(); // Create an instance
result = Activator.CreateInstance(type) as T;
CopyProperties<T>(source, result, type);
DeepCopyChildren<T>(source, result);
return result;
}
private static void DeepCopyChildren<T>(T source, T result) where T : UIElement {
// Deep copy children.
Panel sourcePanel = source as Panel;
if (sourcePanel != null) {
Panel resultPanel = result as Panel;
if (resultPanel != null) {
foreach (UIElement child in sourcePanel.Children) {
// RECURSION!
UIElement childClone = DeepClone(child);
resultPanel.Children.Add(childClone);
}
}
}
}
private static void CopyProperties<T>(T source, T result, Type type) where T : UIElement {
// Copy all properties.
IEnumerable<PropertyInfo> properties = type.GetRuntimeProperties();
foreach (var property in properties) {
if (property.Name != "Name") { // do not copy names or we cannot add the clone to the same parent as the original.
if ((property.CanWrite) && (property.CanRead)) {
object sourceProperty = property.GetValue(source);
UIElement element = sourceProperty as UIElement;
if (element != null) {
UIElement propertyClone = element.DeepClone();
property.SetValue(result, propertyClone);
}
else {
try {
property.SetValue(result, sourceProperty);
}
catch (Exception ex) {
System.Diagnostics.Debug.WriteLine(ex);
}
}
}
}
}
}
}
}
If none of these worked for you, I'm afraid you'd have to implement your own serializer. It looks like David Poll implemented a decent serlizer, so have a look. Using his serlizer is as simple as using the XamlWriter, then you can use the XamlReader:
public static T CloneXaml<T>(T source)
{
UiXamlSerializer uxs = new UiXamlSerializer();
string xaml = uxs.Serialize(source);
StringReader sr = new StringReader(xaml);
XmlReader xr = XmlReader.Create(sr);
return (T)XamlReader.Load(xr);
}
To get this functionality, download his Slab library, go to the "Binaries" folder and copy all the dlls that start with "SLaB.Utilities.Xaml.Serializer" to your project. There might be some other dlls required as dependency. He has example solution in the library if you like to look at the code an learn.
Without a good Minimal, Complete, and Verifiable example that shows clearly what you've tried, with a precise description of what exactly you're trying to achieve, it's impossible to know for sure what the best answer would be.
That said, given that WPF already knows how to "clone" elements in a sense, through the use of data templates, your question really sounds a lot like an XY Problem to me. That is, you only think you need to literally clone the elements already in your visual tree, when in fact what you should be doing is defining a view model that represents the data to be displayed for the element(s) to be "cloned", define a single data template that uses XAML to describe the visual elements that will display the data in the view model, and then simply apply the template as necessary wherever you want the visual elements to be "cloned".
I.e. they won't really be cloned. Instead, WPF will automatically populate a whole new sub-tree of visual elements exactly as you want them to be. Since the template allows you to completely define all aspects, there is no issue related to e.g. trying to get the event subscriptions hooked up, setting up bindings correctly, etc.
In your specific example (vague though it is), it sounds like you most likely want to use an ItemsControl element, in which the ItemsPanel is a Canvas object. You would then define a DataTemplate that represents a single item in the ItemsPanel; this template would be referenced either implicitly by setting its DataType property, or explicitly by setting the ItemsControl.ItemTemplate property. Then, instead of cloning anything, you just create an ItemsControl when you want a copy of your visual for the data.
New answer after user's feedback that it is not working on Windows Phone
Complete final Windows Phone App can be downloaded here.
There are some API differences, for example instead of pinfo.SetMethod property we have to use pinfo.GetSetMethod() etc.
Secondly, I was unknowingly didn't check for Name property which must not be copied as otherwise we would be making another instance with same name.
Third, I posted for simple case of simple controls like Button, TextBox, Rectangle etc which do not contain children. If that is the case you have to go for recursive deep cloning to clone children too. As children could have more children and so on.
foreach (UIElement oldElem in Canvas1.Children)
{
try
{
Type t = oldElem.GetType();
UIElement newElem = (UIElement)Activator.CreateInstance(t);
PropertyInfo[] info = t.GetProperties();
int i = 0;
foreach (PropertyInfo pinfo in info)
{
if (pinfo.Name == "Name") continue;
try
{
if (pinfo.GetSetMethod() != null) // avoid read-only properties
pinfo.SetValue(newElem, pinfo.GetValue(oldElem, null),null);
}
catch (Exception ex)
{
Debug.WriteLine((++i).ToString() + " : " + pinfo.ToString());
}
}
Canvas.SetLeft(newElem, Canvas.GetLeft((oldElem)));
Canvas.SetTop(newElem, Canvas.GetTop((oldElem)));
Canvas2.Children.Add(newElem);
}
catch (Exception ex)
{
}
}
And if you are going for truly deep cloning then replace code in outer try block above with a simpler:
foreach (UIElement oldElem in Canvas1.Children)
{
try
{
UIElement newElem = oldElem.DeepClone();
Canvas2.Children.Add(newElem);
Canvas.SetLeft(newElem, Canvas.GetLeft(oldElem));
Canvas.SetTop(newElem, Canvas.GetTop(oldElem));
}
catch (Exception ex){ }
}
Old answer based on WPF only
Don't know about windows phone but in WPF this creates a fresh element and puts it in exactly same place in another canvas. Check if it fits your needs, else I will update it again.
foreach (UIElement oldElem in Canvas1.Children)
{
Type t = oldElem.GetType();
UIElement newElem = (UIElement)Activator.CreateInstance(t);
PropertyInfo[] info = t.GetProperties();
int i = 0;
foreach (PropertyInfo pinfo in info)
{
try
{
if (pinfo.SetMethod != null) // avoid read-only properties
pinfo.SetValue(newElem, pinfo.GetValue(oldElem));
}
catch (Exception ex)
{
Debug.WriteLine((++i).ToString() + " : " + pinfo.ToString());
}
}
Canvas.SetLeft(newElem, Canvas.GetLeft((oldElem)));
Canvas.SetTop(newElem, Canvas.GetTop((oldElem)));
Canvas.SetRight(newElem, Canvas.GetRight((oldElem)));
Canvas.SetBottom(newElem, Canvas.GetBottom((oldElem)));
Canvas2.Children.Add(newElem);
}

How to set ItemsSource property programmatically?

This is XAML code;
<toolkit:AutoCompleteBox x:Name="newTaskNameTextBox"
ItemsSource="{StaticResource BankNamesList}" />
How to assign this ItemSource attribute to the newTaskNameTextBox by C# programmatically ?
(Solution for WPF)
You should use the TryFindResource method.
newTaskNameTextBox.ItemsSource =
(IEnumerable)newTaskNameTextBox.TryFindResource("BankNamesList");
This searches up the logical tree, in the same way {StaticResource BankNamesList} does.
UPDATE: (solution for WP8)
Sounds lile you're using WP8 (which doesn't include FindResource / TryFindResource) so try this instead:
newTaskNameTextBox.ItemsSource = (IEnumerable)Resources["BankNamesList"];
UPDATE: (how to implement the missing TryFindResource)
Note that the code above requires the resource to exist in the owner of this code behind (e.g. the window). However, there may be cases where the resource exists in another parent element up the logical tree. For example, you may be writing the code behind for a custom user control but the resource you're looking for exists in the MainWindow. For such cases, it wouldn't be too hard to write a basic implementation of WPF's TryFindResouces, which has the advantage of searching up the logical tree (source link):
public static class FrameworkElementExtensions
{
public static object TryFindResource(this FrameworkElement element, object resourceKey)
{
var currentElement = element;
while (currentElement != null)
{
var resource = currentElement.Resources[resourceKey];
if (resource != null)
{
return resource;
}
currentElement = currentElement.Parent as FrameworkElement;
}
return Application.Current.Resources[resourceKey];
}
}
/**********************************************************************/
// Or, the recursive version of TryFindResource method as suggested by #Default:
public static object TryFindResource(this FrameworkElement element, object resourceKey)
{
if (element == null)
return Application.Current.Resources[resourceKey];
var resource = element.Resources[resourceKey];
if (resource != null)
{
return resource;
}
return TryFindResource(element.Parent, resourceKey);
}
So if you include this FrameworkElementExtensions class in your namespace, then you should be able to do this (same code I've given for WPF originally):
newTaskNameTextBox.ItemsSource =
(IEnumerable)newTaskNameTextBox.TryFindResource("BankNamesList");
If BankNamesList is resource in the resources of your window, then in code behind you can do:
newTaskNameTextBox.ItemsSource = Resources["BankNamesList"]
Try this:
newTaskNameTextBox.ItemsSource = (IEnumerable)(Application.Current.Resources["BankNamesList"]);

GetChildAtPoint method is returning the wrong control

My form hierarchy is something like this:
Form -> TableLayoutOne -> TableLayoutTwo -> Panel -> ListBox
In the MouseMove event of the ListBox, I have code like this:
Point cursosPosition2 = PointToClient(new Point(Cursor.Position.X, Cursor.Position.Y));
Control crp = this.GetChildAtPoint(cursosPosition2);
if (crp != null)
MessageBox.Show(crp.Name);
The MessageBox is showing me "TableLayoutOne", but I expect it to show me "ListBox". Where in my code am I going wrong? Thanks.
The GetChildFromPoint() method uses the native ChildWindowFromPointEx() method, whose documentation states:
Determines which, if any, of the child windows belonging to the
specified parent window contains the specified point. The function can
ignore invisible, disabled, and transparent child windows. The search
is restricted to immediate child windows. Grandchildren and deeper
descendants are not searched.
Note the bolded text: the method can't get what you want.
In theory you could call GetChildFromPoint() on the returned control until you got null:
Control crp = this.GetChildAtPoint(cursosPosition2);
Control lastCrp = crp;
while (crp != null)
{
lastCrp = crp;
crp = crp.GetChildAtPoint(cursorPosition2);
}
And then you'd know that lastCrp was the lowest descendant at that position.
a better code can be written as following:
Public Control FindControlAtScreenPosition(Form form, Point p)
{
if (!form.Bounds.Contains(p)) return null; //not inside the form
Control c = form, c1 = null;
while (c != null)
{
c1 = c;
c = c.GetChildAtPoint(c.PointToClient(p), GetChildAtPointSkip.Invisible | GetChildAtPointSkip.Transparent); //,GetChildAtPointSkip.Invisible
}
return c1;
}
The usage is as here:
Control c = FindControlAtScreenPosition(this, Cursor.Position);

Know who got the focus in a Lost Focus event

Is it possible to know who got the focus in a lost focus event?
Compact Framework does not have an ActiveControl, so I don't know how to tell who got the focus.
This is the solution that ended up working:
public System.Windows.Forms.Control FindFocusedControl()
{
return FindFocusedControl(this);
}
public static System.Windows.Forms.Control FindFocusedControl(System.Windows.Forms.Control container)
{
foreach (System.Windows.Forms.Control childControl in container.Controls)
{
if (childControl.Focused)
{
return childControl;
}
}
foreach (System.Windows.Forms.Control childControl in container.Controls)
{
System.Windows.Forms.Control maybeFocusedControl = FindFocusedControl(childControl);
if (maybeFocusedControl != null)
{
return maybeFocusedControl;
}
}
return null; // Couldn't find any, darn!
}
One option would be to interop the GetFocus API
[DllImport("coredll.dll, EntryPoint="GetFocus")]
public extern static IntPtr GetFocus();
This will give you the handle to the window that currently has input focus, you can then recursively iterate the control tree to find the control with that handle.
Using the corell.dll looks like a good idea.
Another possible way is to create GotFocus event handlers for all the controls on your form Then create a class level variable that updates with the name of the control that has the current focus.
No. first comes the LostFocus-event of one control then comes the GotFocus-event of the next control. as long as you can not figure out which control the user uses in the next moment, it is not possible.
whereas if the compact framework control does have a TabIndex-property it could be predicted only if the user uses the tab-key.
Edit:
OK You posted the solution and it works fine I must admit: the simple "No" is wrong
+1
This is a shorter code for the Vaccano's answer, using Linq
private static Control FindFocusedControl(Control container)
{
foreach (Control childControl in container.Controls.Cast<Control>().Where(childControl => childControl.Focused)) return childControl;
return (from Control childControl in container.Controls select FindFocusedControl(childControl)).FirstOrDefault(maybeFocusedControl => maybeFocusedControl != null);
}
Exactly the same (in high-level, abstraction).

Categories

Resources