I looked into a nice way to display tooltips dynamically and I found OverLibWrapper, which was exactly what I needed.
I have all the tooltip data stored in a custom configuration section, the tooltips are bound to their respective controls during Page_Load.
I did a quick test and worked fine. The problem came up when I realized that OverLibWrapper didn't work on masterpages. Our website has uses quite a few masterpages, so taking them out isn't an option.
I was wondering if there's anything like OverLibWrapper that I could use.
EDIT:
What I'm looking for is a control to display good-looking tooltips on mouseover preferably instantly like overlib (nothing fancy because I'm just displaying raw text) in a dynamic way, because the tooltip property in ASP.NET is not very pretty and takes a while to appear. For example let's say I have a collection of Messages:
class Message
{
string ctrlid, msgtodisplay;
}
And when the page is loaded:
TooltipManager manager;
foreach(var m in messages)
{
Tooltip tltp=new Tooltip;
m.ControlID=m.ctrlid;
m.Message=m.msgtodisplay;
manager.AddTooltip(tltp);
}
So basically something that offers the functionality of Tooltip and TooltipManager.
Take a look at this:
NotesTooltip
I think this will do what you need.
Have you thought about just writing your own? Sometimes I find the things out there by other people are never quite fit for my needs.
Well I finally solved my problem:
I've used this function to find any control (works with masterpages):
public static Control FindControlRecursive(Control root, string id)
{
if (id == string.Empty)
return null;
if (root.ID == id)
return root;
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
return t;
}
return null;
}
And this method:
public static void load(Page page, string pageFileName)
{
foreach (ConfiguracionElem elem in Configuracion.GetConfig(pageFileName).Tooltips)
{
WebControl ctrl = (WebControl)FindControlRecursive(page, elem.controlid);
if (ctrl == null)
throw new ControlNotFoundException("There's no control'"+elem.controlid+"'")
else
{
ctrl.Attributes.Add("onmouseover","return overlib('"+elem.message+"');");
ctrl.Attributes.Add("onmouseout","return nd();");
}
}
}
I added the Overlib library manually to a script folder, then I iterated through my Custom Configuration Section (where my tooltip data is stored) to add the javascript attributes dynamically.
Related
I want to enable/disable controls in a Windows Forms application according to the user privileges.
Initially I thought of writing a method in each form class that would check the user credentials and then enable/disable its controls. But then I realized I could (maybe) create a static class method which would take the form as a parameter and do the job.
So I started writing it, presuming that sometimes I would like to enable the controls of just one or two panels, instead of the whole form. So, I need the parameters to be:
a varying number of panels and/or
a form class.
My difficulties with this task is that I'm getting an error trying to make the panels argument varying, and I have no idea how to set a parameter that could take any form class. All my form classes obviously inherits from Form generic class, but I don't know how to apply this.
Here's what I got:
public static void Enable(TableLayoutPanel[] containers = null)
{
if (MyOF.isEnabled)
{
return;
}
else
{
try
{
foreach (TableLayoutPanel table in containers)
{
foreach (Control control in table.Controls)
{
control.Enabled = false;
}
}
}
catch (NullReferenceException)
{
}
}
}
If we remember that the Form class derives from Control (indirectly, by deriving from ContainerControl which derives from ScrollableControl, which derives from Control), and the Enabled property belongs to the Control class, we can write a method that will enable any control's children (including the Form or TableLayoutPanel controls), since the Controls collection also belongs to the Control class:
public static void EnableChildren(Control control, bool enabled = true)
{
foreach (Control child in control.Controls)
{
child.Enabled = enabled;
}
}
And then if we also want to be able to use this with a collection of controls (as in your example), we can write an overload that takes a collection:
public static void EnableChildren(IEnumerable<Control> controls = null,
bool enabled = true)
{
if (controls == null) return;
foreach (var control in controls)
{
EnableChildren(control, enabled);
}
}
Now we can use this with a Form or a collection of TableLayoutPanel controls (or any control that has controls in it's Controls collection).
Examples of usage:
var myForm = new Form1();
EnableChildren(this); // 'this' is the current form
EnableChildren(myForm); // a separate instance of a form control
EnableChildren(tableLayoutPanel1, false); // A single TableLayoutPanel control
var tableLayoutPanels = new [] {tableLayoutPanel1, tableLayoutPanel2, tableLayoutPanel3};
EnableChildren(tableLayoutPanels); // An array of tableLayoutPanel controls
One of the simple ways I can think about what you are trying to do, is this. Let me get away for a sec here. I worked on projects where all form controls were built from Metadata. And meta came with licensing info. So, when control was placed where it should, it also was disabled or set read-only based on Metadata but the whole feature would be hidden if licensing info was restricting access to it. Coming back to your approach, this is not a bad approach and I see that this is can be done. And it can be done in 2 ways, (quickly from my head).
Use user controls as surface for the components you want to enable/disable. Create an interface
public interface IDisableableControl // make your fine name, no methods needed - marker interface
. . . . .
public class MyFineUserControl : UserControl, IDisableableControl
And in your static method that you're going to write pass the form, and find all controls that implement this interface and work them the way you want.
2.
Similarly, you can use property Tag, which is available on each control. With that, you can actually set your complex security object that can come from DB-stored metadata and then you evaluate this object stored in Tag to apply your configuration
Your method needs to be recursive
internal static void SetAllControls(Control parent)
{
// Do something with control, for example parent.Enabled = false
if (parent is IDisableableControl)
{
// here you use your logic, evaluate your parent you're dialing with and
// enable/disable correspondingly
parent.Enabled = false;
return;
}
foreach(var c in parent.Controls)
SetAllControls(c);
}
In real life, your TOP parent will be a form and will not need to be disabled, but it's certain children will. In fact, most of the time, once you found a UserControl which implements IDisableableControl that should be end of line, means, you don't need to go into children controls as they all sit on this parent and all will be disabled
I manage to accomplish what I was trying to do with the code below, which is pretty much a blend of all the helpful answers I got:
public static void EnableContainer(params Control[] containers)
{
if(containers.Count() == 0) { return; }
if (MyOF.isEnabled)
{
return;
}
else
{
try
{
foreach (var container in containers)
{
foreach (Control control in container.Controls)
{
control.Enabled = false;
}
}
}
catch (NullReferenceException)
{
}
}
}
public static void EnableForm<form>(form f) where form : Form
{
if (MyOF.isEnabled)
{
return;
}
else
{
foreach(Control control in f.Controls)
{
control.Enabled = false;
}
}
}
The community is welcome to suggest improvements as I am far from being a professional programmer. Thanks everyone once again.
I am creating a Coded UI Test on my machine (Windows 8.1) by creating all the mappings and manually writing the tests. (I do not use Action Recordings) When they run at night on my Nightly machine (Server 2012R2) the action to select my Group Lookup window actually selects my Customer Code lookup window during the test run.
Remapping the controls on my machine does not fix this, and it is not possible to remap them on the Server2012R2 machine as it is only meant to run the tests not to run Visual Studio.
The available search properties on the control are not helpful enough to differentiate the controls. Instead I tried modifying the Friendly Name property on the control and used the following code:
public void ClickControl(WinControl mycontrol, string resultId)
{
UITestControlCollection controls = mycontrol.FindMatchingControls();
foreach (UITestControl allControls in controls)
{
if (allControls.FriendlyName == resultId)
{
Mouse.Click(allControls);
}
}
}
But when I run the code it seems to not use the modified value of the Friendly Name when the program is running.
Is there a way to ensure I am selecting the correct control before I send the Mouse.Click event?
I found a work around by creating the following method. It's not as clean as I would like and I have to use the UIMap tool to find the ControlName, but the code is making my tests pass when I call it.
public static UITestControl FindControl(WinControl myControl, string controlName)
{
try
{
UITestControlCollection controls = myControl.FindMatchingControls();
foreach (UITestControl currentControl in controls)
{
if (currentControl.ControlType == ControlType.Button)
{
WinButton mycont = (WinButton)currentControl;
if (mycont.ControlName == controlName)
{
return mycont;
}
}
if (currentControl.ControlType == ControlType.Edit)
{
WinEdit mycont = (WinEdit)currentControl;
if (mycont.ControlName == controlName)
{
return mycont;
}
}
}
return myControl;
}
catch
{
return myControl;
}
}
I have an ASP.NET Web Forms page childPage.aspx with masterPage.aspx as the master page. The childPage.aspx has a user control (userControl.ascx) control defined on it. Now, I am trying to access the controls on childPage.aspx from within the user control. I have tried a handful of different approaches:
HtmlContainerControl ProductMenu = (HtmlContainerControl)Page.FindControl("ProductMenu");
HtmlContainerControl ProductMenu = (HtmlContainerControl)this.Page.FindControl("ProductMenu");
HtmlContainerControl ProductMenu = (HtmlContainerControl)Parent.FindControl("ProductMenu");
HtmlContainerControl ProductMenu = (HtmlContainerControl)this.Parent.parent.FindControl("ContaintHolder").FindControl("ProductMenu")
In above code, ProductMenu is the id of the <div runat="server" /> on childPage.aspx. Now, I am trying to access it from within my user control, but that fails to return the div.
Please help me out. How should I do this? Thanks in advance.
The reason this doesn't work is likely because the FindControl() method is not recursive. This is called out in the MSDN documentation:
This method will find a control only if the control is directly contained by the specified container; that is, the method does not search throughout a hierarchy of controls within controls.
So, for instance, Page.FindControls() will only search for controls listed in the Page.Controls collection; it won't search the Controls collection of each of those controls. As such, Page.FindControl() would only work if the ProductMenu were at the top-level of your ASPX page; if it is instead nested within, for instance, a Panel control then this code won't work.
To resolve this, you'll need to write a recursive function to crawl the control tree. For instance:
public Control FindControl(Control parentControl, string controlName) {
foreach (var childControl in parentControl.Controls) {
if (childControl.Id == controlName) return childControl;
var foundControl = FindControl(childControl, controlName);
if (foundControl != null) return childControl;
}
return null;
}
In your case, assuming you'll always be looking for an instance of an HtmlContainerControl, you could even validate the type and return a strongly typed object, should you choose. That said, if you want to keep it strongly typed while still supporting different types, you could instead use a generic:
public T FindControl<T>(Control parentControl, string controlName) where T : Control {
foreach (var childControl in parentControl.Controls) {
if (childControl.Id == controlName) return childControl;
var foundControl = FindControl<T>(childControl, controlName);
if (foundControl != null && foundControl is T) return childControl;
}
return null;
}
In addition, if you'll need to do this repeatedly, you might add this as an extension method to the Page class so it's easily accessible on multiple pages.
I've got a web page where I am dynamically creating controls during Page_Load event (this is done so because I do not know how many controls I will need until session is active and certain variables are accessible)
I need to be able to loop through these controls to find Checkbox when a button click is processed. Looping through the Form.Controls does not appear to be sufficient. I would think that Request.Form might work but it does not appear to be accessible in my C# block?
What should code for Request.Form look like? OR
Has anyone done this before with dynamically created controls?
Any insight is appreciated.
Simplified Example from MSDN:
var myControl = FindControl("NameOfControl");
if(myControl != null)
{
//do something
}
else
{
//control not found
}
Hope this helps! ;)
Your controls will be accessible trough the Controls collection of their immediate parent. Unless you add them like Page.Form.Controls.Add (myControl);, you won't find it in Page.Form.Conttrols. If you add them to a place holder, you must find them in thePlaceHolder.Controls.
LinkButton myDynamicLinkButton = new myDynamicLinkButton ();
myDynamicLinkButton.ID = "lnkButton";
myPlaceHolder.Controls.Add (myDynamicLinkButton);
//........
LinkButton otherReferenceToMyLinkButton = myPlaceHolder.FindControl ("lnkButton");
As #David said in his comment, you should probably think about using a Repeater instead. It would probably simplify your case a lot.
Since the controls might be nested in other controls, you need to search recursively. You can use this method to find the control:
public Control FindControlRecursive(Control root, string id)
{
if (root.ID == id)
{
return root;
}
foreach (Control c in root.Controls)
{
Control t = FindControlRecursive(c, id);
if (t != null)
{
return t;
}
}
return null;
}
And you can implement it this way:
CheckBox check = FindControlRecursive(Page.Form, "CheckBox1");
You should have access to Request["xyz"] anywhere in your aspx.cs code. You can either find control as described above and read it's value or do so directly from Request using the Control.UniqueID property. For example if it's a checkbox that's within the repeater then the UniqueID would look like dtgData$ctl02$txtAmount
Thanks for the insight guys. I kind of took the discussion and ran with it and found my solution that worked best for me.
foreach(String chk in Request.Form)
{
if (chk.Contains("chkRemove"))
{
int idxFormat = chk.LastIndexOf("chkRemove");
objectname = chk.Substring(idxFormat);
}
}
Turned out really all I needed was the name. The string contained a number at the end which was needed to determine a position of datatable items. Thanks for the advice!
Example code:
var div = new HtmlGenericControl("div");
div.Controls.Add(new Literal() { ID = "litSomeLit" });
var lit = (Literal)div.FindControl("litSomeLit");
Assert.IsNotNull(lit);
This code fails the assert, because lit is null. Debugging shows that div.Controls definitely contains a literal with ID of "litSomeLit." My questions are "Why?" and "Is there any way to get a control of a specific ID without doing a recursive search of div.Controls[] by hand one element at a time?"
The reason I'm doing things this way is that my actual application is not so straightforward- a method I'm writing is given a complex control with several subcontrols in a number of possible configurations. I need to access a specific control several layers down (eg, the control with ID "txtSpecificControl" might be at StartingControl.Controls[0].Controls[2].Controls[1].Controls[3]). Normally I could just do FindControl("txtSpecificControl"), but that does not seem to work when the controls were just dynamically created (as in the above example code).
Near as I can tell, there is no way to do what I'm trying to accomplish without adding the control to the page. If I had to guess, I'd say that FindControl uses the UniqueID property of the control, which generally contains the IDs of all the controls above the current one (eg OuterControlID$LowerControlId$TargetControlID). That would only get generated when the control actually gets added to the page.
Anyway, here's an implementation of recursive depth-first-search FindControl that'll work when the control is not attached to the page yet:
public static Control FindControl(Control parent, string id)
{
foreach (Control control in parent.Controls)
{
if (control.ID == id)
{
return control;
}
var childResult = FindControl(control, id);
if (childResult != null)
{
return childResult;
}
}
return null;
}
Change your code to
var div = new HtmlGenericControl("div");
Page.Controls.Add(div);
div.Controls.Add(new Literal() { ID = "litSomeLit" });
var lit = (Literal)div.FindControl("litSomeLit");
As far as i know FindControl only works when the control is in the visual tree of the page.
When you confirmed that the control was in the Controls collection, did you do that by inspecting the collection directly? FindControl() may not work in this context.
When you debug the test, is the var lit null? If so, you may have to access the member by item index instead of using the FindControl() method.