I want to manipulate all Controls within my form using a foreach loop, I currently am using the following method:
foreach (Control c in this.Controls)
{
ComponentResourceManager resourceManager = new ComponentResourceManager(typeof(Main));
resourceManager.ApplyResources(c, c.Name, new CultureInfo(lang));
}
The problem with this foreach, is that Controls within Containers aren't included.
How Can I include all the Controls within the Containers too?
I would like to do this in 1 or maybe 2 foreach loops.
Controls
Add the following class to your project:
public static class ControlExtensionMethods
{
public static IEnumerable<Control> GetOffsprings(this Control #this)
{
foreach (Control child in #this.Controls)
{
yield return child;
foreach (var offspring in GetOffsprings(child))
yield return offspring;
}
}
}
It will return all sub controls of the caller:
var offsprings = this.GetOffsprings();
You can use it in the loop:
foreach (Control c in this.GetOffsprings())
{
// Do action
}
Menu items
Add the following class to your project:
public static class MenuStripExtensionMethods
{
public static IEnumerable<ToolStripItem> GetSubItems(this ToolStrip #this)
{
foreach (ToolStripItem child in #this.Items)
{
yield return child;
foreach (var offspring in child.GetSubItems())
yield return offspring;
}
}
public static IEnumerable<ToolStripItem> GetSubItems(this ToolStripItem #this)
{
if (!(#this is ToolStripDropDownItem))
yield break;
foreach (ToolStripItem child in ((ToolStripDropDownItem) #this).DropDownItems)
{
yield return child;
foreach (var offspring in child.GetSubItems())
yield return offspring;
}
}
}
It will return all sub menu items of the caller:
var allMenuItems = this.menuStrip1.GetSubItems()
Make it recursive:
private static void ApplyResources(ComponentResourceManager resourceManager,
CultureInfo cultureInfo,
Control container)
{
foreach (var control in container.Controls)
{
resourceManager.ApplyResources(control, control.Name, cultureInfo);
ApplyResources(resourceManager, cultureInfo, control);
}
}
And use it like this:
var resourceManager = new ComponentResourceManager(typeof(Main));
var cultureInfo = new CultureInfo(lang);
ApplyResources(resourceManager, cultureInfo, this);
Related
How do I read a paragraph from rich text box in WPF and display it in a Message Box?
If you want to iterate through all the paragraphs in a RichTextBox, then the following static classes containing extension methods provide the necessary methods:
public static class FlowDocumentExtensions
{
public static IEnumerable<Paragraph> Paragraphs(this FlowDocument doc)
{
return doc.Descendants().OfType<Paragraph>();
}
}
public static class DependencyObjectExtensions
{
public static IEnumerable<DependencyObject> Descendants(this DependencyObject root)
{
if (root == null)
yield break;
yield return root;
foreach (var child in LogicalTreeHelper.GetChildren(root).OfType<DependencyObject>())
foreach (var descendent in child.Descendants())
yield return descendent;
}
}
Once you have collected all the paragraphs in a FlowDocument, to convert a single paragraph to text, you can do:
var text = new TextRange(paragraph.ContentStart, paragraph.ContentEnd).Text;
And example of how to put these together is:
foreach (var paragraph in canvas.Document.Paragraphs())
{
MessageBox.Show(new TextRange(paragraph.ContentStart, paragraph.ContentEnd).Text);
}
Is that what you want?
Update
If for whatever reason you are uncomfortable using extension methods, you could always use traditional c# 2.0 static methods:
public static class FlowDocumentExtensions
{
public static IEnumerable<Paragraph> Paragraphs(FlowDocument doc)
{
return DependencyObjectExtensions.Descendants(doc).OfType<Paragraph>();
}
}
public static class DependencyObjectExtensions
{
public static IEnumerable<DependencyObject> Descendants(DependencyObject root)
{
if (root == null)
yield break;
yield return root;
foreach (var child in LogicalTreeHelper.GetChildren(root).OfType<DependencyObject>())
foreach (var descendent in child.Descendants())
yield return descendent;
}
}
And
foreach (var paragraph in FlowDocumentExtensions.Paragraphs(mainRTB.Document))
{
MessageBox.Show(new TextRange(paragraph.ContentStart, paragraph.ContentEnd).Text);
}
I have a custom method GetControlsOfType that looks like this:
public static List<TControl> GetControlsOfType<TControl>(this Control control) where TControl : Control
{
return (from n in control.Controls.Cast<Control>().Descendants(c => c.Controls.Cast<Control>())
where n is TControl
select (TControl)n).ToList();
}
It depends on this extension:
static public IEnumerable<T> Descendants<T>(this IEnumerable<T> source,
Func<T, IEnumerable<T>> DescendBy)
{
foreach (T value in source)
{
yield return value;
foreach (T child in DescendBy(value).Descendants<T>(DescendBy))
{
yield return child;
}
}
}
If I have a control structure like this (RadEditor is just a Telerik control for making rich text):
MyWebPage.aspx
- MyUserControl1.ascx
- MyRadEditor1
- MyUserControl2.ascx
- MyRadEditor2
If MyUserControl2.ascx calls the method below, I see both MyRadEditor1 and 2.
public static void SetRadEditors(this UserControl control, object entity)
{
control.GetControlsOfType<RadEditor>().ForEach(editor =>
{
// Should only show MyRadEditor2, but shows MyRadEditor1 and MyRadEditor2
});
}
Can anyone help me understand why this occurs?
Not sure right now (too tired to debug.. :-).
Anyway, try this:
public static IEnumerable<T> FindControlsRecursive<T>(this Control root)
{
foreach (Control child in root.Controls)
{
if (child is T)
{
yield return child;
}
if (child.Controls.Count > 0)
{
foreach (Control c in child.FindControlsRecursive<T>())
{
yield return c;
}
}
}
}
I have a treeview with many items
When I expand an item and its childs and after I called refresh method, that expanded item childs becomes collapsed.
Can you give me an idea how to store the selected node and after refresh, to call the Expand method again...
I tried :
TreeNode selectedNode = new TreeNode();
TreeNode selectedNode = SelectedTreeNode(); //method which takes selected node from TV
RestoreFolderTreeWithLastSelectedItem(); //method which initialise treeview object
//here I want to call Expand() method but it not exists to TreeNode.
For treeview, I used Windows.Controls.treeView namespace
I am beginner...
Thank you
I will explain you via images
I expanded all items:
If I press F5 or call Refresh function (RestoreFolderTreeWithLastSelectedItem();) then they will be collapsed like as:
instead of first image...
I'm using this with a WinForms TreeView. It saves the nodes expanded state between refreshes:
// Save the path of the expanded tree branches
var savedExpansionState = treeView1.Nodes.GetExpansionState();
treeView1.BeginUpdate();
// TreeView is populated
// ...
// Once it is populated, we need to restore expanded nodes
treeView1.Nodes.SetExpansionState(savedExpansionState);
treeView1.EndUpdate();
Here is the code to achieve this:
public static class TreeViewExtensions
{
public static List<string> GetExpansionState(this TreeNodeCollection nodes)
{
return nodes.Descendants()
.Where(n => n.IsExpanded)
.Select(n => n.FullPath)
.ToList();
}
public static void SetExpansionState(this TreeNodeCollection nodes, List<string> savedExpansionState)
{
foreach (var node in nodes.Descendants()
.Where(n => savedExpansionState.Contains(n.FullPath)))
{
node.Expand();
}
}
public static IEnumerable<TreeNode> Descendants(this TreeNodeCollection c)
{
foreach (var node in c.OfType<TreeNode>())
{
yield return node;
foreach (var child in node.Nodes.Descendants())
{
yield return child;
}
}
}
}
Again this is a very old issue, but again this might help.
It builds on the above samples, but also preserves the scroll position and selection, and written as an extension class.
These are restored as well as possible upon restore - that is, if the selected node has been removed, then its parent will be selected / scrolled to.
// To use:
//
// var expState = tv.GetExpandedNodesState(TreeView);
// TreeView.Nodes.Clear();
// ... reload...
// tv.RestoreTreeViewState(TreeView, expState);
public static class TreeViewExtensions
{
public static TreeViewState GetExpandedNodesState(this TreeView tree)
{
var expandedNodesList = new List<string>();
foreach (TreeNode node in tree.Nodes)
{
UpdateExpandedList(ref expandedNodesList, node);
}
return new TreeViewState(expandedNodesList, tree.TopNode, tree.SelectedNode);
}
public static void RestoreExpandedNodesState(this TreeView tree, TreeViewState state)
{
tree.BeginUpdate();
foreach (TreeNode node in tree.Nodes)
{
foreach (var nodeState in state.ExpandedNodes)
{
ExpandNodes(node, nodeState);
}
}
tree.TopNode = findNodeFromPath(tree, state.TopNodePath);
tree.SelectedNode = findNodeFromPath(tree, state.SelectedNodePath);
tree.Focus();
tree.EndUpdate();
}
static TreeNode findNodeFromPath(TreeView tree, string path)
{
if (string.IsNullOrWhiteSpace(path))
return null;
List<string> elements = path.Split(tree.PathSeparator.ToCharArray()).ToList();
TreeNode curNode = tree.Nodes.findByText(elements[0]);
if (curNode == null)
return null;
foreach (string element in elements.Skip(1))
{
if (curNode.Nodes.findByText(element) != null)
curNode = curNode.Nodes.findByText(element);
else
break;
}
return curNode;
}
static TreeNode findByText(this TreeNodeCollection tnc, string text)
{
foreach (TreeNode node in tnc)
if (node.Text == text)
return node;
return null;
}
static void UpdateExpandedList(ref List<string> expNodeList, TreeNode node)
{
if (node.IsExpanded) expNodeList.Add(node.FullPath);
foreach (TreeNode n in node.Nodes)
{
if (n.IsExpanded)
UpdateExpandedList(ref expNodeList, n);
}
}
static void ExpandNodes(TreeNode node, string nodeFullPath)
{
if (node.FullPath == nodeFullPath) node.Expand();
foreach (TreeNode n in node.Nodes)
{
if (n.Nodes.Count > 0)
ExpandNodes(n, nodeFullPath);
}
}
}
public class TreeViewState
{
public TreeViewState(List<string> expandedNodes, TreeNode topNode, TreeNode selectedNode)
{
this.ExpandedNodes = expandedNodes;
this.TopNodePath = topNode != null ? topNode.FullPath : null;
this.SelectedNodePath = selectedNode != null ? selectedNode.FullPath : null;
}
public readonly List<string> ExpandedNodes = null;
public readonly string TopNodePath = "";
public readonly string SelectedNodePath = "";
}
It's very simple.
First, before refreshing, we must store ids of expanded nodes:
//CollectExpandedNodes(tree.Nodes); - call of function
//this recursive function save ids of expanded nodes to expandedNodeIds
//List<int> expandedNodeIds = new List<int>(); - list for storage id
private void CollectExpandedNodes(TreeListNodes nodes)
{
foreach (TreeListNode node in nodes)
{
if (node.Expanded) expandedNodeIds.Add(node.Id);
if (node.HasChildren) CollectExpandedNodes(node.Nodes);
}
}
And then, after refreshing you tree, you must expand nodes whose IDs are stored in a expandedNodeIds:
expandedNodeIds.ForEach((id) =>
{
TreeListNode node = tree.FindNodeByID(id);
if (node != null) node.Expanded = true;
});
This is old post, but I think if someone will come here, this code fragment will be useful...
To save TreeView State possible with this simple code:
//you need to have 4 methods for it and call two of them...
//code where treeview needs to save and restore
var expState = GetAllExpandedNodesList(TreeView);
TreeView.Nodes.Clear();
//do something else...
RestoreTreeViewState(TreeView, expState);
//end of treeview save/restore section
private static void UpdateExpandedList(ref List<string> expNodeList, TreeNode node)
{
if (node.IsExpanded) expNodeList.Add(node.FullPath);
foreach (TreeNode n in node.Nodes)
{
if (n.IsExpanded) UpdateExpandedList(ref expNodeList, n);
}
}
private static List<string> GetAllExpandedNodesList(TreeView tree)
{
var expandedNodesList = new List<string>();
foreach (TreeNode node in tree.Nodes)
{
UpdateExpandedList(ref expandedNodesList, node);
}
return expandedNodesList;
}
private static void ExpandNodes(TreeNode node, string nodeFullPath)
{
if (node.FullPath == nodeFullPath) node.Expand();
foreach (TreeNode n in node.Nodes)
{
if (n.Nodes.Count >0) ExpandNodes(n, nodeFullPath);
}
}
private static void RestoreTreeViewState(TreeView tree, List<string> expandedState)
{
foreach (TreeNode node in tree.Nodes)
{
foreach (var state in expandedState)
{
ExpandNodes(node, state);
}
}
}
Is there an elegant way of doing this? Perhaps with Linq?
For something like this:
List<ControlCollection> list = { ... }
List<Control> merged = list.MergeAll();
EDIT: The final collection will be single dimensional in a sense that all controls will be there, not in a nested way.
How about this:
public static void Append(this System.Windows.Forms.Control.ControlCollection collection, System.Windows.Forms.Control.ControlCollection newCollection)
{
Control[] newControl = new Control[newCollection.Count];
newCollection.CopyTo(newControl, 0);
collection.AddRange(newControl);
}
Usage:
Form form1 = new Form();
Form form2 = new Form();
form1.Controls.Append(form2.Controls);
This will flatten the control tree:
public static void FlattenAndAppend(this System.Windows.Forms.Control.ControlCollection collection, System.Windows.Forms.Control.ControlCollection newCollection)
{
List<Control> controlList = new List<Control>();
FlattenControlTree(collection, controlList);
newCollection.AddRange(controlList.ToArray());
}
public static void FlattenControlTree(System.Windows.Forms.Control.ControlCollection collection, List<Control> controlList)
{
foreach (Control control in collection)
{
controlList.Add(control);
FlattenControlTree(control.Controls, controlList);
}
}
Linq has Concat and Union methods. Is one of those what you're looking for?
Tree extension method
static class TreeExtensions
{
public static IEnumerable<R>TraverseDepthFirst<T, R>(
this T t,
Func<T, R> valueselect,
Func<T, IEnumerable<T>> childselect)
{
yield return valueselect(t);
foreach (var i in childselect(t))
{
foreach (var item in i.TraverseDepthFirst(valueselect, childselect))
{
yield return item;
}
}
}
}
Usage:
Control c = root_form_or_control;
var allcontrols = c.TraverseDepthFirst(
x => x,
x => x.Controls.OfType<Control>())).ToList();
var merged = (from cc in list
from Control control in cc
select cc)
.ToList();
or (same thing with explicit LINQ method calls):
var merged = list.SelectMany(cc => cc.Cast<Control>()).ToList();
[EDIT] Reflecting the newly added nesting requirement:
static IEnumerable<T> FlattenTree<T>(
this IEnumerable<T> nodes,
Func<T, IEnumerable<T>> childrenSelector)
{
foreach (T node in nodes)
{
yield return node;
foreach (T child in childrenSelector(node))
{
yield return child;
}
}
}
var merged = list.SelectMany(cc => cc.Cast<Control>())
.FlattenTree(control => control.Controls.Cast<Control>())
.ToList();
I need to set the height of every textbox on my form, some of which are nested within other controls. I thought I could do something like this:
private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)
{
foreach (Control control in rootControl.Controls)
{
if (control.Controls.Count > 0)
{
// Recursively search for any TextBoxes within each child control
foreach (TextBox textBox in FindTextBoxes(control))
{
yield return textBox;
}
}
TextBox textBox2 = control as TextBox;
if (textBox2 != null)
{
yield return textBox2;
}
}
}
Using it like this:
foreach(TextBox textBox in FindTextBoxes(this))
{
textBox.Height = height;
}
But of course the compiler spits its dummy, because foreach expects an IEnumerable rather than an IEnumerator.
Is there a way to do this without having to create a separate class with a GetEnumerator() method?
As the compiler is telling you, you need to change your return type to IEnumerable. That is how the yield return syntax works.
Just to clarify
private static IEnumerator<TextBox> FindTextBoxes(Control rootControl)
Changes to
private static IEnumerable<TextBox> FindTextBoxes(Control rootControl)
That should be all :-)
If you return IEnumerator, it will be a different enumerator object each time call that method (acting as though you reset the enumerator on each iteration). If you return IEnumerable then a foreach can enumerate based on the method with the yield statement.
// Generic function that gets all child controls of a certain type,
// returned in a List collection
private static List<T> GetChildTextBoxes<T>(Control ctrl) where T : Control{
List<T> tbs = new List<T>();
foreach (Control c in ctrl.Controls) {
// If c is of type T, add it to the collection
if (c is T) {
tbs.Add((T)c);
}
}
return tbs;
}
private static void SetChildTextBoxesHeight(Control ctrl, int height) {
foreach (TextBox t in GetChildTextBoxes<TextBox>(ctrl)) {
t.Height = height;
}
}
If you are given an enumerator, and need to use it in a for-each loop, you could use the following to wrap it:
static public class enumerationHelper
{
public class enumeratorHolder<T>
{
private T theEnumerator;
public T GetEnumerator() { return theEnumerator; }
public enumeratorHolder(T newEnumerator) { theEnumerator = newEnumerator;}
}
static enumeratorHolder<T> toEnumerable<T>(T theEnumerator) { return new enumeratorHolder<T>(theEnumerator); }
private class IEnumeratorHolder<T>:IEnumerable<T>
{
private IEnumerator<T> theEnumerator;
public IEnumerator<T> GetEnumerator() { return theEnumerator; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return theEnumerator; }
public IEnumeratorHolder(IEnumerator<T> newEnumerator) { theEnumerator = newEnumerator; }
}
static IEnumerable<T> toEnumerable<T>(IEnumerator<T> theEnumerator) { return new IEnumeratorHolder<T>(theEnumerator); }
}
The toEnumerable method will accept anything that c# or vb would regard an acceptable return type from GetEnumerator, and return something that can be used in foreach. If the parameter is an IEnumerator<> the response will be an IEnumerable<T>, though calling GetEnumerator on it once will likely yield bad results.