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);
}
Related
So I have a WinForms application running on .NET 7.0 and I need to get a selection from a focused control (and this should work in any application that supports text selection) and replace it with specific text. The problem is that I can replace all the text in the field, but I can't replace just the part of the text that I need.
private void GetSelectedText()
{
try
{
var element = AutomationElement.FocusedElement;
if (element != null)
{
object pattern;
object valuePattern;
// Here I can get the selected text from almost any application.
if (element.TryGetCurrentPattern(TextPattern.Pattern, out pattern))
{
var tp = (TextPattern)pattern;
var sb = new StringBuilder();
foreach (var r in tp.GetSelection())
{
sb.AppendLine(r.GetText(-1));
}
// Debug info:
MessageBox.Show(sb.ToString());
}
// And this code sets value of the focused control to "aaaaaaa".
if (element.TryGetCurrentPattern(ValuePattern.Pattern, out valuePattern))
{
((ValuePattern)valuePattern).SetValue("aaaaaaa");
}
}
}
catch (Exception ex)
{
// Debug info:
Console.WriteLine(ex.Message, ex.StackTrace);
}
}
I've also tried WinAPI calls for copying and pasting the value but I couldn't figure out how to replace the text without using the clipboard or writing a huge amount of code.
I would be grateful if you could help me with this.
Text pattern doesn't provide any method to modify the text, and the value pattern also allows just setting the whole value. What you can do is modifying the selection through direct keyboard input. This is what I tried and works as expected:
var element = AutomationElement.FocusedElement;
if (element != null)
{
if (element.TryGetCurrentPattern(TextPattern.Pattern, out object pattern))
{
var tp = (TextPattern)pattern;
var selection = tp.GetSelection().FirstOrDefault();
if(selection != null)
{
SendKeys.SendWait("XXXXXX");
}
}
}
Fore more information, take a look at TextPattern overview
The TextPattern classes do not provide a means to insert or modify
text. However, depending on the control, this may be accomplished by
the UI Automation ValuePattern or through direct keyboard input. See
the TextPattern Insert Text Sample for an example.
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
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"]);
I need to traverse all controls and components inside a UserControl I'm developing.
I tried:
public void Traverse(Control cnt)
{
foreach (Control c in cnt.Controls)
{
if (c.HasChildren) Traverse(c);
Debug.Print(c.Name); // For debugging purpose only
// My code goes here
}
}
Problem raises when functions meets a ToolStrip: it has no children, but Items (ToolStripItemCollection: IList, ICollection, IEnumerable).
I don't care of type: using Reflection I need to set some property, so I feel good having objects as result.
How can I get the name of every component that is inside my UserControl?
Thanks
I've written a version that goes through the control's properties and looks for IComponents that have ICollections on them:
Method:
private void GetControls(ICollection controls, IList<string> names)
{
foreach (var ctl in controls)
{
if (ctl is IComponent)
{
var name = ctl.GetType().GetProperty("Name");
if (name != null)
names.Add((string) name.GetValue(ctl, null));
foreach (var property in ctl.GetType().GetProperties())
{
var prop = property.GetValue(ctl, null);
if (prop is ICollection)
GetControls((ICollection)prop, names);
}
}
}
}
Called:
var ctlNames = new List<string>();
GetControls(Controls, ctlNames);
I've tested this and it seems to find every control on the form. I haven't tested it for every kind of control and I can't vouch for how efficient it is.
I'm busy with a simple application. It reads xml and puts the information in a treeview.
I do this by creating TreeNodes and nest them, and finaly, return the root treenode. Because I want to show some extra information when a treenode is selected, I put the information in the tag property of the TreeNode. In this way, I should be able to retrieve the information when the node is selected.
But when I try to retrieve the information in the Tag property, it says the value = null.
Here is the code where I fill the tag. This is in a function which is recursively used to read the XML dom. treeNode is a paramater given to this function.
if (treeNode.Tag == null)
{
treeNode.Tag = new List<AttributePair>();
}
(treeNode.Tag as List<AttributePair>).Add(new AttributePair(currentNode.Name, currentNode.Value));
This is the event where a treenode is selected
private void tvXML_AfterSelect(object sender, TreeViewEventArgs e)
{
if (tvXML.SelectedNode.Tag != null)
{
}
if (e.Node.Tag != null)
{
}
}
Both values evaluate to null. How can I solve this problem?
The code you posted should work as-is. Something else in your code, code that you didn't post here, is causing this to break. It could be clearing the Tag, it could be a data binding set on the tag, etc.
Without seeing all your code, the best I can do is guess and help you isolate the problem.
Here's what I'd do: setup Visual Studio to allow stepping into the .NET framework source code with the debugger. Then, set a breakpoint on the setter for the TreeNode.Tag property. After you set the tag in your code to your AttributePair List, see when it gets set again. The breakpoint will hit, you'll look at the stack trace and see what exactly is clearing your Tag property.
If using Tag property isn't in principle, I'm recommend inherit TreeItem:
public class MyTreeNode : TreeNode
{
public List<AttributePair> list;
public MyTreeNode (string text,List<AttributePair> list) : base(text)
{
this.list = list;
}
//or
public MyTreeNode (string text) : base(text)
{
this.list = new List<AttributePair>();
}
}
And use it:
private void tvXML_AfterSelect(object sender, TreeViewEventArgs e)
{
if (tvXML.SelectedNode is MyTreeNode)
{
MyTreeNode selectedNode = tvXML.SelectedNode as MyTreeNode;
selectedNode.list.Add(.., ..);
}
if (e.Node is MyTreeNode)
{
MyTreeNode node = e.Node as MyTreeNode;
node.list.Add(.., ..);
}
}
Maybe you are assigning the values after Select event. Otherwise you can maintain a dictionary of TreeNode and tag values as workaround.
Try declaring/initialising your List object somewhere above (outside of the inner scope you are in) and when you assign to the .tag property - don't create a new list but rather assign previously created List object.
private TreeViewItem _subsender;
private object _senderTag;
public TreeViewItem _sender
{
get {
return _subsender;
}
set
{
_senderTag = value.Tag;
_subsender = value;
}
}
Got the same problem this the solution that i found
Just don't use the .tag but _senderTag
(don't change the lines in the set for some reason :D )
(You cant just reset the tag (maybe new TreeViewItem ))