Adding a control to the main container of unknown object - c#

void Visualize(object CoreObj, object ParentControl)
{
if(CoreObj is typeA)
{
object control1 = new MyControl1(CoreObj);
ParentControl.FirstChild.Children.Add(control1);
foreach (object obj in CoreObj.Children)
{
Visualize(obj, control1);
}
}
else if (CoreObj is typeB)
{
object control2 = new MyControl2(CoreObj);
ParentControl.FirstChild.Children.Add(control2);
foreach (object obj in CoreObj.Children)
{
Visualize(obj, control2);
}
}
}
Where FirstChild is always container, no matter StackPanel, Grid or Canvas, or whatever.
How I get the first child, and the harder part, how to do Children.Add() on object?
I can require something else in case "Children" is inherited from somewhere in all wpf containers, but I can't find out which ancestor/interface contains "Children". Or I can use Reflection probably..
How to do this?
Here's what I came with, finally
interface IContain
{
Panel GetMain(); //return main container
}
// ...
void Visualize(object CoreObj, Panel ParentControl)
{
UIElement control = new UIElement();
if (CoreObj is File) { control = new NameSpacer(); } //new NameSpacer(obj);
else if (CoreObj is Namespace) { control = new NameSpacer(); }
else if(CoreObj is Using) { control = new NameSpacer(); }
if (control.GetType() == typeof(UIElement)) return;
ParentControl.Children.Add(control);
FieldInfo finf = CoreObj.GetType().GetField("Children"); if (finf == null) return;
var val = finf.GetValue(CoreObj); if (val.GetType() != typeof(IEnumerable<object>)) return;
if (control is IContain == false) return;
Panel container = ((IContain)control).GetMain();
foreach (object o in val as IEnumerable<object>)
{
Visualize(o, container);
}
}

You can use VisualTreeHelper class to get the first child.
Children property is defined in abstract class Panel.
var firstChild = parentControl.Descendants().OfType<Panel>().First();
firstChild.Children.Add(control1);
The descendants method leverages VisualTreeHelper in order to get all descendants and you have to define it as extension method:
public static IEnumerable<DependencyObject> Descendants(this DependencyObject element)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childrenCount; i++)
{
var visualChild = VisualTreeHelper.GetChild(element, i);
yield return visualChild;
foreach (var visualChildren in Descendants(visualChild))
{
yield return visualChildren;
}
}
}

StackPanel, Grid and Canvas, all derive from the Panel class. This is also the class that contains the Children property.
if you know your argument is a Panel, you also have access to Children.First() and Children.Add(..)

Related

How to programatically expand all expanders in window

I've got a window with some expanders in it.
When you open a expander there is some information inside it.
What i need to do is to open all expanders with one button so everything inside them becomes visible.
When everything is visible i want to print the full page.
This is my code for expanding all expanders now:
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;
}
}
}
}
The lines i use to iterate through my controls:
foreach (Expander exp in FindVisualChildren<Expander>(printpage))
{
exp.IsExpanded = true;
}
Now to the point:
The code above works in most cases.
The only problem i have is that sometimes there are some expanders WITHIN expanders.
The parent expanders do expand when the above code executes, The child expanders however remain unexpanded.
I hope someone can teach me how to expand those child expanders too.
EDIT
I forgot to mention that the child-expanders are not direct childs of the main expanders..
They are children of children of children of the main expanders.
My controll-tree goes something like this:
-Stackpanel
---List item
-----Grid
-------Expander (Main expanders)
---------Grid
-----------Textblock
-------------Expander
So i need to expand all expanders in this tree.
Your code is quite complicated already for what it does. Yields are absolutely not necessary if you call and you really should to execute your method in a recursive fashion.
When, inside your method you encounter a control with children, you call the same method but with a new visual root, which will be a control with children you've just found.
This should work for you (may be a few syntax errors but i'm sure if there are you can fix them)
foreach (Expander exp in FindVisualChildren<Expander>(printpage))
{
exp.IsExpanded = true;
for(int i =0;i<exp.Children.Count;i++)
{
if(exp.Children[i] is Expander)
{
expandChildren(exp.Children[i]);
}
}
}
private expandChildren(Expander exp)
{
exp.IsExpanded = true;
for(int i =0;i<exp.Children.Count;i++)
{
if(exp.Children[i] is Expander)
{
expandChildren(exp.Children[i]);
}
}
}
Okay, I found my anwser in this post
The anwser on this question is what I used to solve my problem.
Here's the function I used:
public static List<T> GetLogicalChildCollection<T>(object parent) where T : DependencyObject
{
List<T> logicalCollection = new List<T>();
GetLogicalChildCollection(parent as DependencyObject, logicalCollection);
return logicalCollection;
}
private static void GetLogicalChildCollection<T>(DependencyObject parent, List<T> logicalCollection) where T : DependencyObject
{
IEnumerable children = LogicalTreeHelper.GetChildren(parent);
foreach (object child in children)
{
if (child is DependencyObject)
{
DependencyObject depChild = child as DependencyObject;
if (child is T)
{
logicalCollection.Add(child as T);
}
GetLogicalChildCollection(depChild, logicalCollection);
}
}
}
In my code I used these lines to append what I needed to my expanders:
List<Expander> exp = GetLogicalChildCollection<Expander>(printpage.StackPanelPrinting);
foreach (Expander exp in expander)
{
exp.IsExpanded = true;
exp.FontWeight = FontWeights.Bold;
exp.Background = Brushes.LightBlue;
}

Finding ALL child controls WPF

I would like to find all of the controls within a WPF control. I have had a look at a lot of samples and it seems that they all either require a Name to be passed as parameter or simply do not work.
I have existing code but it isn't working properly:
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;
}
}
}
}
For instance it will not get a DataGrid within a TabItem.
Any suggestions?
You can use these.
public static List<T> GetLogicalChildCollection<T>(this DependencyObject parent) where T : DependencyObject
{
List<T> logicalCollection = new List<T>();
GetLogicalChildCollection(parent, logicalCollection);
return logicalCollection;
}
private static void GetLogicalChildCollection<T>(DependencyObject parent, List<T> logicalCollection) where T : DependencyObject
{
IEnumerable children = LogicalTreeHelper.GetChildren(parent);
foreach (object child in children)
{
if (child is DependencyObject)
{
DependencyObject depChild = child as DependencyObject;
if (child is T)
{
logicalCollection.Add(child as T);
}
GetLogicalChildCollection(depChild, logicalCollection);
}
}
}
You can get child button controls in RootGrid f.e like that:
List<Button> button = RootGrid.GetLogicalChildCollection<Button>();
You can use this Example:
public Void HideAllControl()
{
/// casting the content into panel
Panel mainContainer = (Panel)this.Content;
/// GetAll UIElement
UIElementCollection element = mainContainer.Children;
/// casting the UIElementCollection into List
List < FrameworkElement> lstElement = element.Cast<FrameworkElement().ToList();
/// Geting all Control from list
var lstControl = lstElement.OfType<Control>();
foreach (Control contol in lstControl)
{
///Hide all Controls
contol.Visibility = System.Windows.Visibility.Hidden;
}
}

Get control by name, including children

I'm trying to get a control by name. I wrote the following code:
public Control GetControlByName(string name)
{
Control currentControl;
for(int i = 0,count = Controls.Count; i < count; i++)
{
currentControl = Controls[i];
if (currentControl.HasChildren)
{
while (currentControl.HasChildren)
{
for(int x = 0,size = currentControl.Controls.Count; x < size; x++)
{
currentControl = currentControl.Controls[x];
if (currentControl.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
{
return currentControl;
}
}
}
}
else
{
if (currentControl.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
{
return currentControl;
}
}
}
return null;
}
It only ever returns null. Can someone point out my mistake? Any help or ways to improve this code are welcomed.
Just use the Controls collection Find method:
var aoControls = this.Controls.Find("MyControlName", true);
if ((aoControls != null) && (aoControls.Length != 0))
{
Control foundControl = aoControls[0];
}
I actually wrote some extension methods at work to do just this thing:
public static class MyExensions ()
{
public static Control FindControlRecursively (this Control control, string name)
{
Control result = null;
if (control.ID.Equals (name))
{
result = control;
}
else
{
foreach (var child in control.Children)
{
result = child.FindControlRecursively (name);
if (result != null)
{
break;
}
}
}
return result;
}
public static T FindControlRecursively<T> (this Control control, string name)
where T: Control
{
return control.FindControlRecursively (name) as T;
}
}
Note: Null checks removed for the sake of simplicity.
You can use it to find, say, a TextBox on your form like so:
public class MyForm : Form
{
public void SetSomeText ()
{
var control = this.FindControlRecursively<TextBox> ("myTextboxName");
if (control != null)
{
control.Text = "I found it!";
}
// Or...
var control2 = this.FindControlRecursively ("myTextboxName2") as TextBox;
if (control != null)
{
control2.Text = "I found this one, also!";
}
}
}
Edit
Of course, this is a depth-first algorithm, which might be slow depending on how deep your control chain is. You might rewrite it to use a breadth-first algorithm if you see that it is too slow.
A slight tweak for if you're not using System.Windows.Forms (this is what .Find(string, bool) works off too)
public static class MyExensions
{
public static Control FindControlRecursively(this Control control, string name)
{
Control result = null;
if (control.ID.Equals(name))
{
result = control;
}
else
{
for (var i = 0; i < control.Controls.Count; i++)
{
result = control.Controls[i].FindControlRecursively(name);
if (result != null)
{
break;
}
}
}
return result;
}
public static T FindControlRecursively<T>(this Control control, string name)
where T : Control
{
return control.FindControlRecursively(name) as T;
}
}
p.s. Yes I know it's an old question, but in case it helps 😁

WPF + Controls.UserControl. How to find a control inside?

In my WPF project, I have a System.Windows.Controls.UserControl control. How to find a control inside that contol ?
use VisualTree, if I understood your question correctly.
refer to msdn : http://msdn.microsoft.com/en-us/library/dd409789.aspx
In that case you would probably want to walk the visual tree, like this extension method does:
internal static T FindVisualChild<T>(this DependencyObject parent) where T : DependencyObject
{
if (parent == null)
{
return null;
}
DependencyObject parentObject = parent;
int childCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childCount; i++)
{
DependencyObject childObject = VisualTreeHelper.GetChild(parentObject, i);
if (childObject == null)
{
continue;
}
var child = childObject as T;
return child ?? FindVisualChild<T>(childObject);
}
return null;
}
It requires that you know the type of the control you are looking for.

How to set focus to a control with Caliburn.Micro MVVM

I have a form and I want to set the focus to a text box when certain user actions happen. I know the MVVM way of doing things is to bind to VM properties, however the TextBox does not have a property that will allow this to happen. What's the best way to set the focus from the VM?
I have created an IResult implementation that works quite well for achieving this. You can get the view from the ActionExecutionContext of the IResult, which you can then search (I search by name) for the control you want to focus.
public class GiveFocusByName : ResultBase
{
public GiveFocusByName(string controlToFocus)
{
_controlToFocus = controlToFocus;
}
private string _controlToFocus;
public override void Execute(ActionExecutionContext context)
{
var view = context.View as UserControl;
// add support for further controls here
List<Control> editableControls =
view.GetChildrenByType<Control>(c => c is CheckBox ||
c is TextBox ||
c is Button);
var control = editableControls.SingleOrDefault(c =>
c.Name == _controlToFocus);
if (control != null)
control.Dispatcher.BeginInvoke(() =>
{
control.Focus();
var textBox = control as TextBox;
if (textBox != null)
textBox.Select(textBox.Text.Length, 0);
});
RaiseCompletedEvent();
}
}
I have ommitted some extra code to get the view from the context when the view is a ChildWindow I can provide if you require.
Also GetChildrenByType is an extension method, here is one of many implementations available in the wild:
public static List<T> GetChildrenByType<T>(this UIElement element,
Func<T, bool> condition) where T : UIElement
{
List<T> results = new List<T>();
GetChildrenByType<T>(element, condition, results);
return results;
}
private static void GetChildrenByType<T>(UIElement element,
Func<T, bool> condition, List<T> results) where T : UIElement
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
UIElement child = VisualTreeHelper.GetChild(element, i) as UIElement;
if (child != null)
{
T t = child as T;
if (t != null)
{
if (condition == null)
results.Add(t);
else if (condition(t))
results.Add(t);
}
GetChildrenByType<T>(child, condition, results);
}
}
}
Your action would then be something like the following (invoked in Caliburn.Micro ActionMessage style).
public IEnumerable<IResult> MyAction()
{
// do whatever
yield return new GiveFocusByName("NameOfControlToFocus");
}
There is an easier way.
1º In the ViewModel add property _view as your UserControl
2º You must override OnViewLoaded of your ViewModel and
set _view to View object.
3º Set focus from any method.
public UserControlView _view { get; set; }
protected override void OnViewLoaded(object view)
{
base.OnViewLoaded(view);
_view = (UserControlView)view;
}
public void SetFocus()
{
_view.TextBox1.Focus();
}
I hope help you.

Categories

Resources