Avete presente quando caricate la pagina di google quanto sia comodo avere subito il focus sulla textbox di ricerca?
Lo script da usare è estremamente semplice:
dove IdControlloLatoClient è l'id del controllo a cui dare il focus, tassativamente ottenuto con la proprietà ClientID.
Lo script va poi registrato con il metodo RegisterStartupScript.
Per risolvere la questione in modo più elegante ho buttato giù un piccolo custom control che registra questo script.
[DefaultProperty("FocusControl"), ToolboxData("<{0}:FirstFocus runat=server>")]
public class FirstFocus : Control
{
private string StartScript = "\r\n
";
protected Control FindNestedControl(Control root, string name)
{
foreach(Control ctl in root.Controls)
{
if(ctl.ID == name)
return ctl;
if(ctl.HasControls())
return FindNestedControl(ctl, name);
}
return null;
}
protected string GetControlRenderID(string name)
{
//Control ctl = FindControl(name);
Control ctl = FindNestedControl(Page, name);
if (ctl == null)
return "";
return ctl.ClientID;
}
protected override void OnPreRender(EventArgs e)
{
if(FocusControl == string.Empty)
return;
string CID = GetControlRenderID(FocusControl);
if(CID != string.Empty)
Page.RegisterStartupScript(ClientID, StartScript + "document.all['" + CID + "'].focus();" + EndScript);
}
///
/// Il control ID da specificare nel caso degli UserControl è quello dello UserControl
/// Poi è necessario che lo UserControl faccia l'override della proprietà ClientID
/// e restituisca al posto del proprio quello del controllo che si vuole guadagni il focus
///
[Category("Behaviour"),
DefaultValue(""),
Description("Control you wish will gain control at load time"),
TypeConverter(typeof(AllPageControlsConverter))]
public string FocusControl
{
get
{
string ctl = ViewState["FocusControl"] as string;
return (ctl == null) ? string.Empty : ctl;
}
set
{
ViewState["FocusControl"] = value;
}
}
}
Per rendere più gradevole l'uso del controllo ho poi costruito un UITypeConverter per scegliere a design time, con una combo nella property toolbox, il controllo a cui dare il focus.
public class AllPageControlsConverter : StringConverter
{
private object[] GetControls(ITypeDescriptorContext context)
{
IContainer container = context.Container;
ComponentCollection coll = container.Components;
ArrayList list = new ArrayList();
foreach (IComponent comp in coll)
{
if (!(comp is Control))
continue;
Control ctl = (Control) comp;
if ((ctl.ID != null) && (ctl.ID.Length != 0) && (ctl.ID != (((Control)context.Instance)).ID))
{
list.Add(string.Copy(ctl.ID));
}
}
list.Sort(Comparer.Default);
return list.ToArray();
}
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
if (context == null || context.Container == null)
return null;
object[] ctls = GetControls(context);
if (ctls != null)
return new TypeConverter.StandardValuesCollection(ctls);
return null;
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return false;
}
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
}
Infine un ultimo particolare: e se il controllo a cui dare il focus è contenuto all'interno di uno UserControl? La risposta è semplice, basta fare l'override nello user control della proprietà ClientID e restituire quello del controllo 'principale'. In questo modo, indicando lo user control come destinatario del focus, verrà letto il ClientID del controllo contenuto nello User Control.
/// proprietà ClientID dello User Control
public override string ClientID
{
get
{
return txtUsername.ClientID; // txtUsername è il controllo contenuto nello User COntrol a cui si vuole dare il focus
}
}
Ci sono sicuramente degli spunti di miglioramento, come il piccolo spreco nel viewstate per conservare il nome del controllo. Ma a me è bastato così ...