124 votes

Comment puis-je prendre plus de contrôle dans ASP.NET?

Je suis en train de construire un très, très simple "micro-webapp" qui je pense sera d'intérêt pour un peu de Débordement de Pile'rs si jamais je faire cela. Je suis héberger sur mon C# dans la Profondeur du site, qui est à la vanille ASP.NET 3.5 (c'est à dire pas de MVC).

L'écoulement est très simple:

  • Si un utilisateur entre dans l'application avec une URL qui permet de ne pas spécifier tous les paramètres (ou si aucun d'entre eux ne sont pas valides) je veux juste afficher à l'utilisateur des contrôles d'entrée. (Il y en a seulement deux.)
  • Si un utilisateur entre dans l'application avec une URL qui n' ont tous les paramètres requis, je veux afficher les résultats et les contrôles de saisie (modifier les paramètres)

Voici mon auto-imposé des exigences (mélange de design et de mise en œuvre):

  • Je veux que la soumission à l'utiliser plutôt que de POSTER, surtout si les utilisateurs peuvent mettre en signet la page facilement.
  • Je ne veux l'URL de l'air idiot après la présentation, avec des extérieurs, de pièces et de morceaux. Juste les URL et les paramètres réels s'il vous plaît.
  • Idéalement, j'aimerais éviter l'obligation de JavaScript. Il n'y a aucune bonne raison pour cela dans cette application.
  • Je veux être en mesure d'accéder aux commandes pendant le temps de rendu et de définir des valeurs etc. En particulier, je veux être en mesure de définir les valeurs par défaut des contrôles pour les valeurs de paramètre passé dans, si ASP.NET ne peut pas le faire automatiquement pour moi (dans les autres restrictions).
  • Je suis heureux de faire tous les paramètre de validation de moi-même, et je n'ai pas besoin de beaucoup dans la façon de server side events. Il est vraiment simple de tout mettre sur chargement de la page au lieu de fixer des événements à des boutons etc.

La plupart de ce qui est bon, mais je n'ai pas trouvé de moyen de complètement retirer le viewstate et de garder le reste de la durée de la fonctionnalité. En utilisant le post de ce blog, j'ai réussi à éviter d'avoir une réelle valeur pour le viewstate - mais il finit toujours par en tant que paramètre dans l'URL, ce qui a l'air vraiment laid.

Si je fais un simple formulaire HTML au lieu d'un ASP.NET forme (c'est à dire prendre runat="server") alors je n'ai pas toute la magie de viewstate - mais alors je ne peux pas accéder par programmation des contrôles.

Je pourrais faire tout cela en ignorant la plupart des ASP.NET et la construction d'un document XML avec LINQ to XML, et la mise en œuvre de IHttpHandler. Qui se sent un peu faible niveau, mais.

Je me rends compte que mes problèmes pourraient être résolus par la relaxation des contraintes (par exemple, à l'aide de POST et de ne pas se soucier de l'excédent de paramètre) ou à l'aide de ASP.NET MVC, mais sont mes exigences vraiment déraisonnable?

Peut-être ASP.NET juste ne pas l'échelle vers le bas pour ce type d'application? Il existe une alternative probable cependant: je suis juste stupide, et il y a parfaitement moyen simple de le faire que je n'en ai pas trouvé.

Toute pensée, toute personne? (Cue commentaires de la façon dont les puissants sont tombés, etc. C'est très bien - j'espère que je n'ai jamais prétendu être un ASP.NET expert, que la vérité est tout le contraire...)

76voto

Dan Herbert Points 38336

Cette solution va vous donner l'accès par programme pour les commandes dans leur intégralité, y compris tous les attributs sur les contrôles. Aussi, seules les valeurs de zone de texte apparaîtra dans l'URL lors de la présentation donc votre requête GET à l'URL sera plus "utile"

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="JonSkeetForm.aspx.cs" Inherits="JonSkeetForm" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Jon Skeet's Form Page</title>
</head>
<body>
    <form action="JonSkeetForm.aspx" method="get">
    <div>
        <input type="text" ID="text1" runat="server" />
        <input type="text" ID="text2" runat="server" />
        <button type="submit">Submit</button>
        <asp:Repeater ID="Repeater1" runat="server">
            <ItemTemplate>
                <div>Some text</div>
            </ItemTemplate>
        </asp:Repeater>
    </div>
    </form>
</body>
</html>

Puis dans votre code-derrière vous, vous pouvez faire tout ce dont vous avez besoin sur PageLoad

public partial class JonSkeetForm : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        text1.Value = Request.QueryString[text1.ClientID];
        text2.Value = Request.QueryString[text2.ClientID];
    }
}

Si vous ne voulez pas un formulaire qui a runat="server", alors vous devriez utiliser les contrôles HTML. Il est plus facile de travailler avec. Juste utiliser des balises HTML et de mettre runat="server" et de leur donner une IDENTITÉ. Ensuite, vous pouvez y accéder par programme et code sans ViewState.

Le seul inconvénient est que vous n'aurez pas accès à de nombreux "utile" ASP.NET les contrôles serveur comme GridViews. J'ai inclus un Repeater dans mon exemple parce que je suis en supposant que vous souhaitez avoir les champs sur la même page que les résultats et (à ma connaissance) Repeater est le seul contrôle lié aux données qui seront exécutés sans runat="server" d'attribut dans la balise Form.

12voto

Dylan Beattie Points 23222

Vous êtes certainement (à mon humble avis) sur la bonne voie en n'utilisant pas runat="server" dans votre balise de FORMULAIRE. Cela signifie simplement que vous aurez besoin pour extraire des valeurs de la Demande.QueryString directement, même si, comme dans cet exemple:

Dans l' .page aspx lui-même:

<%@ Page Language="C#" AutoEventWireup="true" 
     CodeFile="FormPage.aspx.cs" Inherits="FormPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>ASP.NET with GET requests and no viewstate</title>
</head>
<body>
    <asp:Panel ID="ResultsPanel" runat="server">
      <h1>Results:</h1>
      <asp:Literal ID="ResultLiteral" runat="server" />
      <hr />
    </asp:Panel>
    <h1>Parameters</h1>
    <form action="FormPage.aspx" method="get">
    <label for="parameter1TextBox">
      Parameter 1:</label>
    <input type="text" name="param1" id="param1TextBox" value='<asp:Literal id="Param1ValueLiteral" runat="server" />'/>
    <label for="parameter1TextBox">
      Parameter 2:</label>
    <input type="text" name="param2" id="param2TextBox"  value='<asp:Literal id="Param2ValueLiteral" runat="server" />'/>
    <input type="submit" name="verb" value="Submit" />
    </form>
</body>
</html>

et dans le code-behind:

using System;

public partial class FormPage : System.Web.UI.Page {

        private string param1;
        private string param2;

        protected void Page_Load(object sender, EventArgs e) {

            param1 = Request.QueryString["param1"];
            param2 = Request.QueryString["param2"];

            string result = GetResult(param1, param2);
            ResultsPanel.Visible = (!String.IsNullOrEmpty(result));

            Param1ValueLiteral.Text = Server.HtmlEncode(param1);
            Param2ValueLiteral.Text = Server.HtmlEncode(param2);
            ResultLiteral.Text = Server.HtmlEncode(result);
        }

        // Do something with parameters and return some result.
        private string GetResult(string param1, string param2) {
            if (String.IsNullOrEmpty(param1) && String.IsNullOrEmpty(param2)) return(String.Empty);
            return (String.Format("You supplied {0} and {1}", param1, param2));
        }
    }

L'astuce ici est que nous utilisons ASP.NET les Littéraux à l'intérieur de la value="" attributs de la saisie de textes, de sorte que les zones de texte eux-mêmes n'ont pas à runat="server". Les résultats sont ensuite enveloppés à l'intérieur d'un ASP:Panel, et la propriété Visible définie au chargement de la page en fonction de si vous voulez afficher tous les résultats ou pas.

2voto

user134706 Points 895

Bon Jon, le viewstate question première:

Je n'ai pas vérifié si il ya toute sorte de code interne de changement depuis la version 2.0, mais voici comment j'ai géré de se débarrasser de l'état d'affichage il y a quelques années. En fait cachée champ est codé en dur à l'intérieur de HtmlForm de sorte que vous devriez tirer votre nouveau et à une étape dans son rendu à effectuer les appels par vous-même. Notez que vous pouvez également laisser __eventtarget et __ _ _ eventtarget si vous vous en tenez à la plaine de vieux contrôles de saisie (ce qui, j'imagine que vous voulez, car il contribue également à ne nécessitant pas de JS sur le client):

protected override void RenderChildren(System.Web.UI.HtmlTextWriter writer)
{
    System.Web.UI.Page page = this.Page;
    if (page != null)
    {
        onFormRender.Invoke(page, null);
        writer.Write("<div><input type=\"hidden\" name=\"__eventtarget\" id=\"__eventtarget\" value=\"\" /><input type=\"hidden\" name=\"__eventargument\" id=\"__eventargument\" value=\"\" /></div>");
    }

    ICollection controls = (this.Controls as ICollection);
    renderChildrenInternal.Invoke(this, new object[] {writer, controls});

    if (page != null)
        onFormPostRender.Invoke(page, null);
}

Si vous obtenez ces 3 statique MethodInfo et faites sauter ce viewstate partie ;)

static MethodInfo onFormRender;
static MethodInfo renderChildrenInternal;
static MethodInfo onFormPostRender;

et voici votre formulaire type constructeur:

static Form()
{
    Type aspNetPageType = typeof(System.Web.UI.Page);

    onFormRender = aspNetPageType.GetMethod("OnFormRender", BindingFlags.Instance | BindingFlags.NonPublic);
    renderChildrenInternal = typeof(System.Web.UI.Control).GetMethod("RenderChildrenInternal", BindingFlags.Instance | BindingFlags.NonPublic);
    onFormPostRender = aspNetPageType.GetMethod("OnFormPostRender", BindingFlags.Instance | BindingFlags.NonPublic);
}

Si je suis votre question, vous voulez aussi de ne pas utiliser de POST que l'action de vos formulaires alors, voici comment vous pouvez le faire:

protected override void RenderAttributes(System.Web.UI.HtmlTextWriter writer)
{
    writer.WriteAttribute("method", "get");
    base.Attributes.Remove("method");

    // the rest of it...
}

Je suppose que c'est à peu près tout. Permettez-moi de savoir comment il va.

EDIT: j'ai oublié la Page viewstate méthodes:

Si votre Formulaire personnalisé : HtmlForm obtient son tout nouveau abstraite (ou pas) Page : Système.Web.L'INTERFACE utilisateur.Page :P

protected override sealed object SaveViewState()
{
    return null;
}

protected override sealed void SavePageStateToPersistenceMedium(object state)
{
}

protected override sealed void LoadViewState(object savedState)
{
}

protected override sealed object LoadPageStateFromPersistenceMedium()
{
    return null;
}

Dans ce cas, j'ai joint les méthodes de cause vous ne pouvez pas le sceau de la Page (même si elle n'est pas abstraite, Scott Guthrie va l'envelopper dans un autre encore :P) mais vous pouvez sceller votre Formulaire.

1voto

tvanfosson Points 268301

Avez-vous pensé à ne pas éliminer le POST mais plutôt à rediriger vers une URL GET appropriée lorsque le formulaire est POSTé? C'est-à-dire accepter à la fois GET et POST, mais sur POST, créer une requête GET et y rediriger. Cela pourrait être géré soit sur la page, soit via un HttpModule si vous souhaitiez le rendre indépendant de la page. Je pense que cela rendrait les choses beaucoup plus faciles.

EDIT: Je suppose que vous avez EnableViewState = "false" sur la page.

1voto

Mehrdad Afshari Points 204872

Je voudrais créer un module HTTP qui gère le routage (similaire à la MVC, mais pas sophistiqué, juste un couple if des déclarations) et le remettre à l' aspx ou ashx pages. aspx est préféré, car il est plus facile de modifier le modèle de page. Je ne voudrais pas utiliser WebControls dans la aspx cependant. Juste Response.Write.

Par ailleurs, pour simplifier les choses, vous pouvez faire la validation des paramètres dans le module (qu'il partage de code avec le routage sans doute) et enregistrez - HttpContext.Items , puis de les rendre dans la page. Cela fonctionne un peu comme le MVC sans la cloche et de sifflets. C'est ce que j'ai fait beaucoup de choses avant ASP.NET MVC jours.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X