I wonder how can I reload the UserControl to it's original template from inside,
<UserControl x:Name="_UserControl">
<!-- other input controls and combos -->
<Button OnClick="Reload_UserControl"/>
</UserControl>
is there any this.reCreate() similar method?
My Answer is based on a answer posted for Windows Forms 8+ years back.
I have used the same logic from that answer and created extension method on the Root Grid to loop through and clear values of each element( Mimicking it to bring the UserControl back to its usual state)
Below is the Extension Method.
public static class Extensions
{
private static Dictionary<Type, Action<UIElement>> controldefaults = new Dictionary<Type, Action<UIElement>>()
{
{typeof(TextBox), c => ((TextBox)c).Text = String.Empty},
{typeof(CheckBox), c => ((CheckBox)c).IsChecked = false},
{typeof(ComboBox), c => ((ComboBox)c).SelectedIndex = 0},
{typeof(ListBox), c => ((ListBox)c).Items.Clear()},
{typeof(RadioButton), c => ((RadioButton)c).IsChecked = false},
};
private static void FindAndInvoke(Type type, UIElement control)
{
if (controldefaults.ContainsKey(type))
{
controldefaults[type].Invoke(control);
}
}
public static void ClearControls(this UIElementCollection controls)
{
foreach (UIElement control in controls)
{
FindAndInvoke(control.GetType(), control);
}
}
public static void ClearControls<T>(this UIElementCollection controls) where T : class
{
if (!controldefaults.ContainsKey(typeof(T))) return;
foreach (UIElement control in controls)
{
if (control.GetType().Equals(typeof(T)))
{
FindAndInvoke(typeof(T), control);
}
}
}
}
For usage, Give the RootGrid Some name. Say rootGrid. So all you need to call is
rootGrid.Children.ClearControls();
Also As mentioned in other answer, if you want to clear only specific controls, you can use
rootGrid.Children.ClearControls<TextBox>();
You can find the Extensions class on my Gist
Good Luck.
If what you mean is clearing the values entered by a user in various fields, then using MVVM makes it quite simple - you can either replace the view model with a new one and the bindings would update everything. You could also have a Clear method in the view model.
If what you mean is fixing the topology of the visual tree that might have been altered from the original - your best bet would be to replace your control with a new one.
Also worth noting - "template" is usually not a word associated with user controls. User controls define their visual tree in XAML, which is loaded every time the control is created. Templated controls use a template that is typically loaded once and applied to each control when it loads. See When to use a templated control over a UserControl? for some more potentially useful info.
Related
I have different objects: 6 buttons, Listbox, and 5 labels, those names are like lblHum, lblCo2...
I need to show or hide them together with one button click.
Is it possible (without putting objects in a panel or group boxes) to apply, for example, Hide action, within 1 method for all objects without specifying hiding for each?
Like private void(List<objects>,action)
You can write something like this:
public void HideAll(params Control[] controls)
{
foreach(var control in controls) control.Hide();
}
Call it like this:
HideAll(lblHum, lblCo2, etc);
Or for something more flexible:
public void GroupAction(Action<Control> action, params Control[] controls)
{
foreach(var control in controls) action(control);
}
And then call it like this:
GroupAction(c => c.Hide(), lblHum, lblCo2, etc);
In both cases, though, the collection is still there. You just hid it with a method. And since the method is fairly simple, I'm not sure you get much value above putting the collection directly in your code. That is, you can make a list as a member of the form class and add the controls when the form is initialized. Then you can loop over the list whenever you want.
Since all controls inherit from the same base class (i.e. Control), you can do this with a generic method and passing in an Action. For example:
public void ApplyToAll<T>(IEnumerable<T> items, Action<T> action)
{
foreach (var item in items)
{
action(item);
}
}
And call the method like this:
var items = new List<Control> { lblHum, lblCo2... etc };
ApplyToAll(items, x => x.Visible = false);
This question already has answers here:
How to get ALL child controls of a Windows Forms form of a specific type (Button/Textbox)?
(28 answers)
Closed 2 years ago.
I have a winforms project with a multi-level container setup in place. As in, I have a set of Panels on the top layer, each of those Panels has another set of Panels nested within them, and each of those sub-panels have a set of Controls (TextBoxes, CheckBoxes, etc.) in each one.
What I want is to be able to retrieve Controls of a certain type, given only the top level Panel. For example, one set of this using the names in my project would be MainPanelTruck which has a PanelTruckCheck Panel under it, which itself has 8 CheckBox nested in it. I want to be able to retrieve those 8 CheckBox given just MainPanelTruck.
I have been trying to get OfType<>() to work but as far as I can tell, it only searches among the next level down, so when I search for CheckBox using it, I get nothing as a result, because it only looks at the sub-panels.
This would be a lot easier if the Find method in ControlCollection allowed for a predicate or condition instead of a specific string as key, because it has the searchAllChildren bool which would be very handy here.
I can think of many ways I can get the items I want here, but none of them feel particularly elegant, which is what I'm seeking here. Thanks :)
You can write a simple (extension) method that returns all nested controls of given control:
public static class ControlExtensions
{
public static IEnumerable<Control> GetAllNestedControls(this Control root)
{
var stack = new Stack<Control>();
stack.Push(root);
do
{
var control = stack.Pop();
foreach (Control child in control.Controls)
{
yield return child;
stack.Push(child);
}
}
while (stack.Count > 0);
}
}
With this in hand getting all checkboxes is easy:
panel1.GetAllNestedControls().OfType<CheckBox>()
You can also pass an optional boolean flag to search all children if you want. Check the flag before pushing child controls to stack. I would rename the method in this case to the name suggested by 00110001:
IEnumerable<Control> EnumerateControls(this Control root, bool searchAllChildren)
Usage would be
panel1.EnumerateControls(searchAllChildren: true).OfType<CheckBox>()
If I understand what you are asking, you want all controls of a certain type in a given parent container and include all sub containers
Given
public IEnumerable<T> EnumerateControls<T>(Control control) where T : Control
{
var queue = new Queue<Control>(new []{ control });
while (queue.Any())
{
var controls = queue.Dequeue().Controls;
foreach (var item in controls.OfType<T>())
yield return item;
foreach (Control subControl in controls)
queue.Enqueue(subControl);
}
}
Usage
var allCheckBoxes = EnumerateControls<CheckBox>(somePanel);
TL;DR;
How can I add copy-paste capability to a complex, multiple values property that will enable me to copy the property value from one user control and paste it to another at design time?
The long story
I have created a user control (StylableControl) that has a complex property called Style.
This property contains an instance of a class called StylableControlStyles, that contains multiple instances of a class called Style, where each one holds values such as BackColor, ForeColor, Image, Gradient (another class I've created) etc'.
I've also created a custom control designer to allow editing style property for the user control. It shows a form where each style class in the style property can be edited easily.
Now I want to provide the users of this control an easy way to copy the entire content of the Style property from one instance of the user control to another instance, at design time.
I could, of course, override the ToString() method of the StylableControlStyles object to create a string representation that will encapsulate all the data saved in this object, but that would create a hugh string and of course would need a lot of work parsing it in the class converter (currenty I'm just using an ExpandableObjectConverter).
I would like to avoid that if possible.
Following Ash's advice in the comments I've used a DesignerVerb to copy and paste the Style to and from a private static member of type Style of the control designer.
So in my control designer class I have:
private static ZControlStyle _CopiedStyle;
And have added these designer verbs:
_Verbs.Add(new DesignerVerb("Copy Styles", CopyStyle));
_Verbs.Add(new DesignerVerb("Paste Styles", PasteStyle));
And the methods for copy ans paste:
private void PasteStyle(object sender, EventArgs e)
{
if (_CopiedStyle != null)
{
var toggleButton = Control as ZToggleButton;
if (toggleButton != null)
{
toggleButton.Style.FromStyle(_CopiedStyle);
}
else
{
(Control as ZControl).Style.FromStyle(_CopiedStyle);
}
}
}
private void CopyStyle(object sender, EventArgs e)
{
var toggleButton = Control as ZToggleButton;
if (toggleButton != null)
{
_CopiedStyle = toggleButton.Style;
}
else
{
_CopiedStyle = (Control as ZControl)?.Style;
}
}
I'm trying to perform an operation on every control on a page that is inherited from a masterpage. I don't know how to access the child pages controls. I have tried recursively getting to my controls like this:
private void checkControls(ControlCollection controlcollection)
{
foreach (Control control in controlcollection)
{
if (control.Controls.Count > 0)
{
Debug.WriteLine(control.GetType().ToString());
checkControls(control.Controls);
}
else
{
Debug.WriteLine(control.GetType().ToString());
}
}
The method is called like this:
protected void resettodefault()
{
checkControls(this.Page.Controls);
}
However, the only controls that are printed from this execution are:
ASP.site_master
System.Web.UI.LiteralControl
I would prefer to access my controls directly (without recursion). Otherwise, how can I modify my recursion to get to the desired page's controls?
I would suggest using a base page instead of a master page, this way your logic for iterating over controls (and whatever you will do with that afterwards) is not tied to which master page a page is using.
As far as getting all the controls on the page, because the controls are hierarchical, as is the HTML they represent, so iterating over them recursively makes sense. However if you are dead set on not recursively getting controls something like this should work:
IEnumerable<Control> GetAllControls()
{
var allControls = new List<Control>();
var currentControls = new Queue<Control>();
currentControls.Enqueue(this.Page);
while (currentControls.Count >0)
{
var c = currentControls.Dequeue();
if (!allControls.Contains(c))
{
allControls.Add(c);
if (c.Controls != null && c.Controls.Count > 0)
{
foreach (Control e in c.Controls)
{
currentControls.Enqueue(e);
}
}
}
}
return allControls;
}
The last thing to consider is the lifecycle of the page, and when you iterate over the controls. If you try to walk to control tree too early not all controls may exist.
EDIT: Updated code.
Update
For validation purposes I would highly recommend using the built in validation controls of asp.net. You can read more about them here. This has the added benefit of providing validation on the client, providing faster UI responses and easing the load off the servers.
For resetting all the textboxes. I would recommend creating a new class for this purpose, then calling upon that class when needed instead of messing with the master page:
public class UIControlsHelper
{
public static void ClearTextboxes(Page page)
{
GetAllControls(page)
.Where(x => typeof(TextBox).IsAssignableFrom(x.GetType())
.ToList()
.ForEach(x => (TextBox)x.Text = string.Empty);
}
IEnumerable<Control> GetAllControls(Page page)
// Same as above, but with the page parameter replaced.
}
}
And in any of your pages:
UIControlsHelper.ClearTextboxes(this);
To access the controls in your child page do the following steps:
1-declare a variable of the type you want to access. For example if you want to access a Label in your child page use:
Label lbl_child=this.ContentPlaceHolder1.findcontrol("your label id in child page") as Label;
Now you have your label and you are free to make changes on it. Every change on this control will be reflected on the child control.
ContentPlaceHolder1 is your contentplace holder id so change it with your content id.
public void ClearTextboxes(Page page) {
GetAllControls(page)
.Where(x => typeof(TextBox).IsAssignableFrom(x.GetType()))
.ToList()
.ForEach(x => ((TextBox)x).Enabled=false);
}
I have a form MainForm which is a Windows Forms form that contains many child controls. I want to call one function on MainForm that notifies all of its children. Does the Windows Forms form provide a means to do this? I played with update, refresh and invalidate with no success.
foreach (Control ctrl in this.Controls)
{
// call whatever you want on ctrl
}
If you want access to all controls on the form, and also all the controls on each control on the form (and so on, recursively), use a function like this:
public void DoSomething(Control.ControlCollection controls)
{
foreach (Control ctrl in controls)
{
// do something to ctrl
MessageBox.Show(ctrl.Name);
// recurse through all child controls
DoSomething(ctrl.Controls);
}
}
... which you call by initially passing in the form's Controls collection, like this:
DoSomething(this.Controls);
The answer from MusiGenesis is elegant, (typical in a good way), nice and clean.
But just to offer an alternative using lambda expressions and an 'Action' for a different type of recursion:
Action<Control> traverse = null;
//in a function:
traverse = (ctrl) =>
{
ctrl.Enabled = false; //or whatever action you're performing
traverse = (ctrl2) => ctrl.Controls.GetEnumerator();
};
//kick off the recursion:
traverse(rootControl);
No, there isn't. You must roll out your own.
On a side note - WPF has "routed events" which is exactly this and more.
You are going to need a recursive method to do this (as below), because controls can have children.
void NotifyChildren( control parent )
{
if ( parent == null ) return;
parent.notify();
foreach( control child in parent.children )
{
NotifyChildren( child );
}
}