Avatar billede bsp_andreas Nybegynder
10. september 2004 - 12:52 Der er 49 kommentarer

Reference til siden som indeholder mit usercontrol

Hej, jeg har en side som indeholder nogle forskellige usercontrols.

Nu skal jeg kunne kalde en metode på den side, som indeholder mine usercontrols - fra usercontrollet.
Hvordan får jeg fat i referencen til den side??

mvh
Avatar billede thrytter Nybegynder
10. september 2004 - 14:40 #1
Kald af metode:
Me.Parent.GetType().InvokeMember("_MetodeNavn_", Reflection.BindingFlags.InvokeMethod, System.Type.DefaultBinder, Me.Parent, Nothing)

Property  (set):
Me.Parent.GetType.GetProperty("_PropertyName_").SetValue(Me.Parent, _PropertyValue_, Nothing)
Avatar billede bsp_andreas Nybegynder
10. september 2004 - 15:07 #2
Hej. Jeg har oversat det til noget a'la (c#):

int antal_for_ordre = (int)this.Parent.GetType().InvokeMember("GetAntal_for_Ordre", System.Reflection.BindingFlags.InvokeMethod, System.Type.DefaultBinder, this.Parent, null);

Men den skriver: Exception Details: System.MissingMethodException: Method System.Web.UI.HtmlControls.HtmlForm.GetAntal_for_Ordre() not found.

Desuden kan jeg heller ikke forstå at jeg skal typecaste til int, da metoden jo skulle returnere en int??
Avatar billede thrytter Nybegynder
10. september 2004 - 15:40 #3
Funktionen GetAntal_for_Ordre skal vist være public
Avatar billede bsp_andreas Nybegynder
10. september 2004 - 15:44 #4
som den selvfølgelig også er...
Avatar billede snepnet Nybegynder
10. september 2004 - 17:11 #5
hvis du har den slags behov - er det næsten det nemmeste for dig at definere en baseform :

public class UCContainerForm : Page
{
    public virtual string GetSomeString()
    {
        throw new NotImplementedException("method in baseclass is not implemented");
    }
}

Så kan du "slå" dine egne forms over den ved :

public class SomeForm : UCContainerForm
{
  // hvor du så laver en implementering
  public override string GetSomeString()
  {
      return "somestring";
  }
}

Når du så har behov for at kalde en metode på formen fra din usercontrol, kan du bare :

string test = ((UCContainerForm)this.Page).GetSomeString();

Det ville ikke være unormalt at lave metoden i basen abstrakt, og dermed klassen abstrakt, men det er VS ikke spor glad for desværre.

Du kan opnå det samme ved at definere et interface (sådan kan jeg normalt bedst lide det med den slags - i særdeleshed hvis basen ikke har noget fallback-funktionalitet at byde ind med).

du kan sikre din usercontrol ved at spørge på :

if(!this.Page is UCContainerForm)
  // throw exception ell.

Hvis du bruger modellen med et interface, definerer du bare et sådant ved :

public interface ISomeThing
{
  string GetSomeString();
}

og sørger for at implementere det på de forms hvor du vil have mulighed for at "kalde tilbage" fra en usercontrol :

public class SomeForm : Page, ISomeThing
{
  // her tvinger kompileren dig så til at implementere interfacet :
  public string GetSomeString()
  {
      return "whatever";
  }
}

og fra userkontrollen bliver det så :

((ISomeThing)this.Page).GetSomeString();

og du kan på tilsvarende vis spørge om :

if(!this.Page is ISomeThing)
  /// ...

Du kan selvfølgelig også lave en kombination hvor du definerer interfacet, og implementerer det på en base, som du så specialiserer fra....

Men.... der er også en mulighed for at du løser opgaven mere eventbaseret, hvis du har den mulighed eller det giver mening.

Mvh
Avatar billede bsp_andreas Nybegynder
14. september 2004 - 09:46 #6
thrytter: det fungerer jo desværre ikke, men ellers var det vist lige præcis den løsning jeg ledte efter!

snepnet: Jeg forstår desværre ikke ret meget af det du skriver, men det virker nu også utrolig omstændigt at skulle lave base-classes og begynde nedarvning, bare for at kunne kalde en metode.

Det kan da ikke passe at jeg ikke kan få fat i en reference til den aspx-side som mit usercontrol(ascx) befinder sig på????

Kan man måske gøre det via ascx's contructor : noget med at man sender "this" med ind til ascx fra aspx-siden???
Avatar billede snepnet Nybegynder
14. september 2004 - 10:03 #7
hvis du bare skal have en reference til den side du befinder dig på, kan det gøres med this.Page, og du kan uden problemer trisse igennem sidens kontroltræ - f.eks.

this.Page.Controls[1].Controls.Add(new LiteralControl("hej der"));

Page.Controls[1] vil typisk være dit form-element (men du kan ikke væer sikker), og ovenstående kode vil derfor smide teksten "hej der" ind på formen.

this.Page er dog ikke en reference du kan bruge til noget særligt, hvis formålet er at du vil kalde en bestemt metode du selv har oprettet.
Du kan sagtens smide this med over i en konstruktør, men det vil ikke give dig yderlige da du allerede kan nå det eksisterende Page-objekt som nævnt.

Hvis du ønsker at kalde specifikke metoder, bliver du nødt til at kende typen, eller forsøge dig via reflection (som du selv smed noget kode til tidligere).

At kende typen, ville så indebære at du i din usercontrol skrev noget i stil med :
((SomeSpecialPage)this.Page).SomeSpecialMethod();
Altså typecast af page-objekt til den bestemte type, men hvis du skriver kode som det, kan du jo ikke bruge din usercontrol på andre sider end den helt specifikke - og så kan det sådan set være ligemeget med at lave en kontrol.

Der er som jeg skrev flere forskellige måder du kan forbedre muligheder for at genbruge din kontrol - men det kræver altså at der er nogle typer der bliver nødt til at kende hinanden.
(jeg synes bestemt ikke det er mindre omstændigt at benytte reflection for at kommunikere mellem egne forms og kontroller, end at definere en baseklasse).

Som jeg også skrev er der en mulighed for at du løser problemet mere eventbaseret... Altså at din kontrol kan affyre events, som du så kan abbonere på på siden - og så gøre det der nu giver mening på den aktuelle side, ved det aktuelle event.
(på fuldstændig samme måde som du håndterer et click-event fra en knap).

Jeg vil gerne komme med eksempler, hvis du er interesseret, og jeg vil også meget gerne prøve at omformulere det jeg har skrevet, hvis du ikke kan få det til at give mening.

Mvh.
Avatar billede snepnet Nybegynder
14. september 2004 - 10:20 #8
Hvis det "bare" er en bestem type funktionalitet du har brug for at din kontroller kan trække på, kunne du også oprette dig en lille hjælpeklasse :

public class SomeHelperClass
{
  public static int Multiply(int someNumber, int someOtherNumber)
  {
      return someNumber * someOtherNumber);
  }
}

Den kan du så kalde fra hvor du vil ved :

int result = SomeHelperClass.Multily(3,5);
Avatar billede snepnet Nybegynder
14. september 2004 - 10:29 #9
Og så tror jeg for øvrigt (når jeg tænker nærmere over det) ikke at dette vil fungere fra en kontrol :
this.Page.Controls[1].Controls.Add(new LiteralControl("hej der"));

Du vil givetvis for en fejlmeddelelse hvis du prøver at modificere din kontainers kontroltræ, men det er - som du måske kan udlede af det jeg har skrevet - også hvad man kan begragte som et skidt design.

Kan du ikke skrive hvad du helt præcist har brug for... Så skal jeg komme med et bud.

mvh
Avatar billede bsp_andreas Nybegynder
14. september 2004 - 10:57 #10
hejsa

jeg har løst problemet indtil videre ved at have en propertie i mit usercontrol som jeg sætter ovre fra aspx-siden som: usercontrol.propertie = this.Page;

Som du korrekt skriver bliver jeg nødt til at lave et typecast til den specifikke page-type. Det er der desværre ikke noget at gøre ved lige pt. - men jeg tænkte på om man ikke kunne lave noget dynamisk halløj?? At når jeg nu har referencen til siden - kan man så ikke finde typen på siden/refencen???

f.eks.
reference = this.page;
reference."Get_Class_Navn"() - for at kunne lave typecastet dynamisk skal jeg jo egentlig kun bruge class-navnet på siden( altså i dette tilfælde 'reference')
Avatar billede snepnet Nybegynder
14. september 2004 - 11:50 #11
hej igen andreas :o)

du behøver som nævnt ikke eksplicit at angive this.Page... Du kan fra enhver kontrol (altså typer med base eller bases base i System.Web.UI.Control) komme til page-objektet med this.Page.

this.Page er derfor fuldt ud gyldig fra den kode du har i din usercontrol.

der er masser af muligheder for at få oplyst typen, men hvis du skal bruge den til at kalde metoder, skal du stadig på et tidspunkt lave en
.MetodeNavn(); som du stadig kun kan gøre hvis du har den specifikke type i koden.
(altså... at bruge reflektion er jo stadig en mulighed).

Men... kan du ikke skrive hvad du helt præcist vil gøre... Så skal jeg sende et eksempel tilbage til dig.
(altså... hvad er det du har brug for at kalde fra kontrollen - på den side kontrollen sidder på... Det er meget sansynligt at der er en nem og enkelt shortcut til det).

mvh
Avatar billede thrytter Nybegynder
14. september 2004 - 14:57 #12
bsp_andreas>> Hvis mit eksempel ikke fungerer for dig, skyldes det måske at vi ikke bruger samme udviklingsværktøj - bruger selv Visual Studie VB.NET, og arbejder rent i code-behind eller hvad det nu kaldes.
Avatar billede bsp_andreas Nybegynder
14. september 2004 - 15:31 #13
snepnet ->

således ser noget af mit system ud.

jeg har min aspx.cs-side:

namespace BSPsys.Tilbud
{
  public class OpretTilbud : System.Web.UI.Page
  {
    protected BSPsys.Controls.tilbud_fiksTamp_userControl Tilbud_fiksTamp_userControl1;
    private int tilbudsnr;

    private void Page_Load(object sender, System.EventArgs e)
    {
      Tilbud_fiksTamp_userControl1.SiteReference = this.Page;
    }

    public int GetAntal_for_Ordre()
    {
      return Int32.Parse( TextBox8.Text);
    }
  }
}

og jeg har min ascx.cs-side:

namespace BSPsys.Controls
{
  public class tilbud_fiksTamp_userControl : System.Web.UI.UserControl
  {
    private System.Web.UI.Page _referenceTilSide;

    public System.Web.UI.Page SiteReference
    {
      get { return _referenceTilSide; }
      set { _referenceTilSide = value; }
    }

    private void Calculate()
    {
      int antal_for_ordre = ((BSPsys.Tilbud.OpretTilbud)SiteReference).GetAntal_for_Ordre();
    }
  }
}

Dette er således jeg løser problemet nu. Ved at have en propertie i mit usercontrol som kan indeholde en page-reference, kan jeg tilgå en metode på aspx-siden. Altså:
  int antal_for_ordre = ((BSPsys.Tilbud.OpretTilbud)SiteReference).GetAntal_for_Ordre();

Det er netop metoden GetAntal_for_ordre(), som befinder sig på aspx-siden, som jeg gerne vil kalde. Og kaldet skal komme inde fra ascx-filen.

Du må sige til hvis du skal bruge mere info. Håber det er forståeligt.

mvh
Avatar billede bsp_andreas Nybegynder
14. september 2004 - 15:37 #14
thrytter >> jeg bruger selv VS.NET, men koder dog i C#. Netop derfor har jeg prøvet at oversætte det du skrev til noget i c#, men kan åbenbart ikke få det til at køre.

Det skal ikke gøre nogen forskel hvilket udviklingsværktøj man bruger - det eneste som kan forandre noget er koden. Og at du skriver code-behind, betyder kun at du vælger at placere din kode i en aspx.vb-fil (i stedet for at skrive det direkte i din aspx-fil - under script-tags) Jeg bruger også code-behind.
Avatar billede thrytter Nybegynder
14. september 2004 - 16:00 #15
Vil nu mene at udviklingsværktøjet og code-behind eller ikke code-behind kan gøre en forskel, men vil ikke lige lægge hovedet på blokken...
Avatar billede snepnet Nybegynder
14. september 2004 - 17:27 #16
oversættelse kan fikses her :
http://www.developerfusion.com/utilities/convertcsharptovb.aspx
(oversætter både den ene og den anden vej).

med hensyn til din kode... du behøver ikke at angive referencen selv - du kan nøjes med :
namespace BSPsys.Controls
{
  public class tilbud_fiksTamp_userControl : System.Web.UI.UserControl
  {
    private void Calculate()
    {
      int antal_for_ordre = ((BSPsys.Tilbud.OpretTilbud)this.Page).GetAntal_for_Ordre();
    }
  }
}

men hvad der er lidt mere væsentligt... så har du ikke vist hvad det er der gør at metoden skal kaldes...
opstår problemet fordi du ønsker at få kørt koden når der f.eks. trykkes på en knap der er placeret i din usercontrol ?

der er nogle andre ting omkring det, men inden jeg plaprer videre, må du gerne lige svare på ovenstående, og eventuelt skrive et par ord om formålet med din usercontrol (altså hvad den skal bruges til).

mvh
Avatar billede thrytter Nybegynder
14. september 2004 - 20:44 #17
bsp_andreas>> Mit VB eksempel virker ikke i C#, medmindre der refereres til this.Page istedet for this.Parent dvs

int antal_for_ordre = (int) this.Page.GetType().InvokeMember("GetAntal_for_Ordre", System.Reflection.BindingFlags.InvokeMethod, System.Type.DefaultBinder, this.Page, null);
Avatar billede snepnet Nybegynder
14. september 2004 - 20:53 #18
hej thrytter :o)
af ren interesse... hvorfor vil du bruge reflection i sådan en situation ?
mvh
Avatar billede snepnet Nybegynder
14. september 2004 - 21:02 #19
hvad skulle være forskellen på .Parent i VB og C# ?
Avatar billede thrytter Nybegynder
14. september 2004 - 21:41 #20
Troede heller ikke at der var den store forskel på .Parent i VB og C#, men det er der tilsyneladende. Mit VB eksempel virker med både .Parent og .Page, men kun sidstnævnte virker i C#.

C#
this.Parent.GetType() returnerer System.Web.UI.HtmlControls.HtmlForm
this.Page.GetType() returnerer ASP.default_aspx (sidens navn = default.aspx)

VB
Me.Page.GetType() returnerer _ASP.default_aspx
Me.Parent.GetType() returnerer _ASP.default_aspx

Eksemplet med Reflection var bare min første indskydelse.
Avatar billede snepnet Nybegynder
14. september 2004 - 22:03 #21
Hvis det er tilfældet, må man sige at det er en temmelig grundlæggende forskel ?!? jeg arbejder normalt ikke i VB - så jeg skal ikke spille klog på det, men det ville være voldsomt hvis dine eksempler er helt identiske.....
Jeg tror lige jeg prøver det selv.
Avatar billede snepnet Nybegynder
14. september 2004 - 22:10 #22
Hmmm... Jeg kan ikke genskabe differensen her. Hvordan har du gjort ?

Jeg for nøjagtig samme output med disse to :

// VB
Public Class SomeControl
    Inherits System.Web.UI.UserControl

#Region " Web Form Designer Generated Code "

    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

    End Sub

    Private designerPlaceholderDeclaration As System.Object

    Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        InitializeComponent()
    End Sub

#End Region

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Response.Write(Me.Parent.GetType().ToString())
        Response.Write("<br>")
        Response.Write(Me.Page.GetType().ToString())
    End Sub

End Class


// C#
namespace EXP2
{
    using System;
    using System.Data;
    using System.Drawing;
    using System.Web;
    using System.Web.UI.WebControls;
    using System.Web.UI.HtmlControls;

    public class SomeControl1 : System.Web.UI.UserControl
    {

        private void Page_Load(object sender, System.EventArgs e)
        {
            Response.Write(this.Parent.GetType().ToString());
            Response.Write("<br>");
            Response.Write(this.Page.GetType().ToString());
        }

        override protected void OnInit(EventArgs e)
        {
            InitializeComponent();
            base.OnInit(e);
        }
       
        private void InitializeComponent()
        {
            this.Load += new System.EventHandler(this.Page_Load);
        }
    }
}
Avatar billede thrytter Nybegynder
14. september 2004 - 22:30 #23
namespace CTest
{
  using System;
  using System.Data;
  using System.Drawing;
  using System.Web;
  using System.Web.UI.WebControls;
  using System.Web.UI.HtmlControls;

  public class ucTest : System.Web.UI.UserControl
  {
    private void Page_Load(object sender, System.EventArgs e)
    {       
      Response.Write(this.Parent.GetType().ToString());
      Response.Write("<br>");
      Response.Write(this.Page.GetType().ToString());
    }
                   
    override protected void OnInit(EventArgs e)
    {       
      InitializeComponent();
      base.OnInit(e);
    }
               
    private void InitializeComponent()
    {           
      this.Load += new System.EventHandler(this.Page_Load);
    }       
  }
}

giver outputtet
  System.Web.UI.HtmlControls.HtmlForm
  ASP.default_aspx
Avatar billede snepnet Nybegynder
14. september 2004 - 22:34 #24
det skal den jo også ....
Avatar billede thrytter Nybegynder
14. september 2004 - 22:46 #25
Ja, du har ret - og det skal det også give i VB.

VB eksemplet var testet i et projekt, hvor UserControllen var indsat på en .aspx side som nedarver fra en base-class, der sørger for den grundlæggende funktionalitet og rendering af HtmlForm'en.

Da de enkelte sider i dette projekt ikke indeholder en HtmlForm, vil Me.Parent.GetType() og Me.Page.GetType() give samme resultat!
Avatar billede snepnet Nybegynder
14. september 2004 - 22:53 #26
ahh... så giver det mere mening :o)
det havde også været en klam en, hvis der lå sådan en forskel og lurede ;o)
Avatar billede bsp_andreas Nybegynder
15. september 2004 - 08:58 #27
Det du skriver med at jeg ikke behøver at angive referencen selv: betyder det, at jeg ikke behøver at oprette "SiteReference"???


Kaldet som skal aktivere Calculate-metoden i ascx-filen kan komme både inde fra usercontrollet, men kan også komme som fra aspx-siden. Fra aspxsiden vil det være at brugeren trykker på en knap. Fra ascx kan det både være at brugeren trykker på en knap, men også hvis brugeren indtaster noget (altså event-driven).

Der er op mod 10-15 usercontrols placeret på aspx-siden - og det er vigtigt at hver af dem kan kalde en/flere metoder på aspx-siden. Næsten hvert usercontrol har en Calculate-metode. Det er meget vigtigt at hvert usercontrol's calculate() kan kaldes når brugeren trykker på en knap på aspx-siden. Men kan også være vigtigt hvis et andet usercontrol updates, at de andre så også odpateres.

Hvert usercontrol indeholder f.eks. et datagrid, hvori data kan indtastes, og når brugeren har indtastet/eller ændret data som står der i forvejen, skal en sum/felt(ikke i db) opdateres - enten ved at brugeren trykker på en knap ("opdater") eller ved et "onchangeEvent" på input-feltet. Den laver simpelthen en slags excel-beregning af en masse felter, som den udregner en sum for.

Måske lidt indviklet, men meningen er der måske. eller....?
Avatar billede snepnet Nybegynder
15. september 2004 - 11:11 #28
det der vil jeg rigtig gerne skrive et eksempel til dig på, men det kan først blive i aften... hvad siger du til det ?
mvh
Avatar billede bsp_andreas Nybegynder
15. september 2004 - 11:17 #29
jamen det vil jeg da ikke sige nej tak til :)

Og jeg kan godt vente til i aften, men kan ikke give feedback før i morgen.

På forhånd tak.
Avatar billede snepnet Nybegynder
15. september 2004 - 11:21 #30
super - det siger vi bare så :o)
mvh
Avatar billede snepnet Nybegynder
15. september 2004 - 23:52 #31
hej andreas.

Jeg har lavet et lille eksempel... du kan bare oprette en form der hedder bsp_andreas.aspx og så paste hele baduljen ind så du kan prøve det.
(de eneste du skal skifte er det namespace der står øverst... til det der står i din code-behind når du opretter formen).
Altså namespace EXP2 skal skiftes ud med det der står i den code-behind du for oprettet).

Hvis du synes der er meget der ser uvant ud for dig... så prøv at ignorere det til at starte med - og så bare check ud hvordan siden virker.

Fidusen er - at du fra en knap på formen kan få eksekverer alle dine calculate-metoder i dine egne kontroller, og hvis i en kontrol eksekverer calculate, kan det afstedkomme at calculate bliver kaldt i alle andre kontroller (det var sådan jeg forstod det du skrev).

Jeg sætter 10 ens kontroller ind - men glem det for nu.... det kunne lige så godt være 10 forskellige (jeg startede bare lige med en enkelt).

Jeg håber du kan se lidt af fidusen, og ellers spørg spørg spørg.

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace EXP2
{
    // delegate
    public delegate void CalculateEventHandler(object sender, CalculationEventArgs e);

    // argument til brug i forbindelse med beregningsevent
    public class CalculationEventArgs : System.EventArgs
    {
        // bare privat variabel til resultatet
        private int result;

        // property til resultatet
        public int Result
        {
            get{return result;}
        }

        // kun en konstruktør der tager et resultat må anvendes
        public CalculationEventArgs(int result)
        {
            this.result = result;
        }
    }

    // interface for "beregnere"
    public interface ICalculator
    {
        event CalculateEventHandler Calculation;
        int Result{get;}
    }

    // bare en side
    public class bsp_andreas : System.Web.UI.Page
    {
        protected HtmlForm Form1;
        protected Button btnCalculateAll, btnClear;
        protected TextBox txtResult;

        private int sumOfCalculations = 0;

        private void Page_Load(object sender, System.EventArgs e)
        {
            // vi sætter en stak beregnere ind
            for(int i=0;i<10;i++)
            {
                Calculator calc = new Calculator();
                calc.Calculation += new CalculateEventHandler(calc_Calculation);
                Form1.Controls.Add(calc);
            }

            // så vi kan se resultatet
            txtResult = new TextBox();       
            txtResult.TextMode = TextBoxMode.MultiLine;
            txtResult.ReadOnly = true;
            txtResult.BackColor = Color.LightBlue;
            txtResult.Width = 232;
            txtResult.Height = 400;
            Form1.Controls.Add(txtResult);

            // knap til udførelse af beregning
            btnCalculateAll = new Button();
            btnCalculateAll.Text = "Calculate All";
            btnCalculateAll.Width = 116;
            btnCalculateAll.Click += new EventHandler(btnCalculateAll_Click);
           
            // knap til at slette indholdet af resutatboksen
            btnClear = new Button();
            btnClear.Text = "Clear";
            btnClear.Width = 116;
            btnClear.Click += new EventHandler(btnClear_Click);
           
            // ind med sagerne
            Form1.Controls.Add(new LiteralControl("<br>"));
            Form1.Controls.Add(btnCalculateAll);
            Form1.Controls.Add(btnClear);
        }

        // std
        override protected void OnInit(EventArgs e)
        {
            InitializeComponent();
            base.OnInit(e);
        }
       
        // std
        private void InitializeComponent()
        {   
            this.Load += new System.EventHandler(this.Page_Load);
        }

        // håndterer når der er udført en beregning i en kontrol
        private void calc_Calculation(object sender, CalculationEventArgs e)
        {
            Refresh();
        }

        // håndter at der trykkes på "beregn-det-hele-knappen"
        private void btnCalculateAll_Click(object sender, EventArgs e)
        {
            Refresh();
        }

        // opdaterer resultatet
        private void Refresh()
        {
            // starter med at nulstille resultatet
            sumOfCalculations = 0;

            // her høster vi så værdien fra alle beregnere der er proppet direkte på formen
            foreach(Control c in this.Form1.Controls)
            {
                if(c is ICalculator)
                {
                    sumOfCalculations += ((ICalculator)c).Result;
                    txtResult.Text += "\n" + c.ToString();
                }
            }

            // og indsætter værdien
            this.txtResult.Text += "\nIALT : " + sumOfCalculations.ToString();
        }

        // tømmer bare resultatboksen
        private void btnClear_Click(object sender, EventArgs e)
        {
            this.txtResult.Text = String.Empty;
        }
    }   

    // beregner kontrol
    public class Calculator : Panel, ICalculator, INamingContainer
    {
        // private variabel til resultatet
        private int _result;

        // angiver om der bør genberegnes
        private bool _isDirty = true;

        // kontroldeklerationer
        protected TextBox txtVal1, txtVal2, txtResult;
        protected Button btnCalculate;

        // event der kan abboneres på udefra
        public event CalculateEventHandler Calculation;

        // property så resultatet kan hentes "udefra" - der beregnes hvis det er nødvendigt
        public int Result
        {
            get
            {
                EnsureChildControls();
                if(_isDirty)
                    DoCalculation();
                return _result;
            }
        }

        protected override void CreateChildControls()
        {
            // den første tekstboks
            txtVal1 = new TextBox();
            txtVal1.Text = "1";
            txtVal1.Width = 50;
            txtVal1.TextChanged += new EventHandler(txtVal_TextChanged);

            // den anden tekstboks
            txtVal2 = new TextBox();
            txtVal2.Text = "1";
            txtVal2.Width = 50;
            txtVal2.TextChanged += new EventHandler(txtVal_TextChanged);

            // tekstboks til resultatet
            txtResult = new TextBox();
            txtResult.ReadOnly = true;
            txtResult.Width = 50;
            txtResult.BackColor = Color.Silver;
           
            // knap så beregningen kan foretages fra kontrollen
            btnCalculate = new Button();
            btnCalculate.Text = "Calculate";
            btnCalculate.Click += new EventHandler(btnCalculate_Click);

            // og de indsættes
            this.Controls.Add(txtVal1);
            this.Controls.Add(txtVal2);
            this.Controls.Add(txtResult);
            this.Controls.Add(btnCalculate);

            // og basen kaldes (std)
            base.CreateChildControls ();
        }

        // bare en convenience-metode til at affyre eventet
        private void FireCalculation(CalculationEventArgs e)
        {
            if(Calculation != null)
                Calculation(this, e);
        }

        // når der klikkes på knappen - ønskes beregningen udført,
        // og hvis der skulle være nogle der lytter - får de at vide at beregningen er udført
        private void btnCalculate_Click(object sender, EventArgs e)
        {
            DoCalculation();
            FireCalculation(new CalculationEventArgs(this.Result));
        }   
   
        // sørger bare for at beregne værdien
        private void DoCalculation()
        {
            _result = int.Parse(txtVal1.Text) * int.Parse(txtVal2.Text);
            _isDirty = false;
            this.txtResult.Text = _result.ToString();
        }

        // angiver at der skal genberegnes fordi teksten er skiftet
        private void txtVal_TextChanged(object sender, EventArgs e)
        {
            _isDirty = true;
        }

        // overskrivninga af tostring() så den returnerer regnestykket
        public override string ToString()
        {
            return String.Format("{0}*{1}={2}", txtVal1.Text, txtVal2.Text, Result);
        }
    }
}
Avatar billede snepnet Nybegynder
15. september 2004 - 23:55 #32
(og så håber jeg for øvrigt at eksemplet ikke er helt skævt i forhold til den opgave du forsøger at løse :o)
mvh
Avatar billede bsp_andreas Nybegynder
17. september 2004 - 09:26 #33
hej igen.
jeg fik desværre ikke tid til at se på det i går, men sidder og kigger lidt på det nu, lige inden morgenmaden :)

Det ser ud til at være noget jeg sagtens kan bruge - da det er super smart at man kan aktivere alle control's på samme tid.

Det må jo være en form for pattern du bruger til at opbygge det der. Hvad hedder det?? Listener... eller noget???

Jeg arbejder videre med det.

Kan det også bruges til at; hente en variabel (eller kalde en metode som returnerer en variabel) fra aspx-siden?? Det er fordi at nogle af controllerne skal bruge en variabel, som befinder sig på aspx-siden - til at udføre beregningen.

mvh og tak :)
Avatar billede snepnet Nybegynder
17. september 2004 - 10:17 #34
alletiders :o)
jeg vil gerne vente med at sende et eksempel på det sidste - indtil du har fået den første udgave "op under neglene". det er helt bevidst at det er udeladt indtil videre.

men hvis du har ovenstående op at køre på din æske - skal jeg gerne udvide lidt i aften :o)

mvh
Avatar billede bsp_andreas Nybegynder
17. september 2004 - 11:11 #35
Hej - jeg har lidt problemer med at få det til at køre. Det brokker sig og siger at jeg ikke har implementeret interfacet - men det er mig og maskinen nu ikke enige om...

Jeg har lige et par spm.:
1) hvor skal jeg skrive delegate't??
2) hvor skal jeg skrive interfacet??

Jeg kunne godt tænke mig at, især interfacet, lå i en fil for sig.
Jeg er ikke så glad for at det hele står i en lang fil.

pt. ser mit system nogenlunde således ud:
BSPsys.Controls.tlb_repro_userControl.ascx = som er det control, der skal implementere ICalculator
BSPsys.Tilbud.OpretTilbud.aspx.cs = som er selve siden

Begge skal jo have adgang til interfacet, så spørgsmålet er om jeg ikke skal oprette en mappe i roden, til interfacet: BSPsys.Interfaces - og placere det her??
Avatar billede snepnet Nybegynder
17. september 2004 - 11:28 #36
det skal du bare gøre helt som du synes bedst.... den eneste grund til at jeg havde lagt det hele i den samme var at det så skulle være nemmere at paste, og efterfølgende få til at virke :o)
når ud i VS skriver ", <interfacenavn> efter en klasse - plejer man at kunne trykke på tabulatoren, hvorefter stubs for det der ligger under interfacet bliver skrevet ind i klassen.

Når du implementerer et interface - er det det samme som at stille krav om at der er noget bestemt der skal implementeres.
Avatar billede bsp_andreas Nybegynder
17. september 2004 - 14:11 #37
halløj - nu er den der NÆSTEN!!! Juuhuu, tror jeg.

Men jeg har ET problem:

foreach(Control c in this.Form1.Controls)
{
  if(c is ICalculator)
  {
    sumOfCalculations += ((ICalculator)c).Result;
    Response.Write(sumOfCalculations + "\n");
    txtResult.Text += "\n" + c.ToString();
  }
}

jeg indsætter ikke mine controls programmatisk med: Form1.Controls.Add(calc);
De bliver bare indsat på aspx-siden.
Derfor kan jeg ikke finde "this.Form1"

hvad gør jeg??? Jeg har prøvet med .page og alt muligt, men den finder ikke min controller i metoden. Altså den kommer aldrig ind i "if(c is ICalculator)"
Avatar billede snepnet Nybegynder
17. september 2004 - 14:17 #38
du skriver den bare oppe i toppen sammen med dine andre deklerationer :

protected HtmlForm Form1;

(VS kalder selv formen for Form1 som standard)
Avatar billede snepnet Nybegynder
17. september 2004 - 14:18 #39
(det er bare en af den slags ting der ikke sker automatisk... du skal selv gøre ovenstående hvis du vil tilgå formen programmatisk)
Avatar billede bsp_andreas Nybegynder
17. september 2004 - 14:21 #40
jodelihuhu!!!

Det er simpelthen super fedt kode-design, når det fungerer.

:)
Avatar billede snepnet Nybegynder
17. september 2004 - 15:27 #41
det var godt at den faldt i god jord :o)
jeg kan ikke komme til at lave det før iaften - men hvis du stadig gerne vil have det udvidet med en mulighed for at kalde tilbage på siden - siger du bare til.
mvh
Avatar billede bsp_andreas Nybegynder
17. september 2004 - 15:32 #42
jamen jeg er helt sikkert interesseret i at kunne kalde den anden vej også. Det ville være rigtig cool, da jeg som beskrevet, skal bruge en variabel som findes på siden og flere af controllerne skal have adgang til den.

Jeg er self. også villig til at hæve pointene, så du i det mindste får lidt ud af dit store arbejde.
Avatar billede snepnet Nybegynder
17. september 2004 - 15:34 #43
jeg smider det ud til dig i aften andreas :o)
mvh
Avatar billede bsp_andreas Nybegynder
17. september 2004 - 15:37 #44
det lyder rigtig godt - men får muligvis først tid på mandag.
skal jo også have rettet alle mine controllere til nu :)
Avatar billede snepnet Nybegynder
17. september 2004 - 15:55 #45
det er bare iorden :o)
Avatar billede snepnet Nybegynder
18. september 2004 - 23:55 #46
(nu skrev du jo det der med mandag.... det er derfor jeg ikke har lagt noget ud endnu :o)
Avatar billede snepnet Nybegynder
19. september 2004 - 00:15 #47
Så er der lidt frisk kode... Jeg har bare udvidet det lidt - og poster hele eksemplet her igen :´

fidusen i koden er, at såfremt ICalculatoren sidder på en side der implementerer ICalculatorContainer, vil der blive sat en lille meddelelse om hvor stor en del af den samlede sum, resultatet fra den ICalculator man har klikket i udgør.
Resultatet beregnes selvfølgelig udfra den samlede sum, som hentes fra den ICalculatorContainer den sidder på.

Det er denne kode her det drejer sig om :

if(this.Page is ICalculaterContainer)
{
    double partOfSum = ((double)this.Result / (double)(((ICalculaterContainer)this.Page).Total)) * 100;
    lblPartOfSum.Text = String.Format("Udgør {0}% af det hele", partOfSum.ToString("c"));
}
(plus selvfølgelig det, at siden implementerer ICalculatorContainer).

hvis siden ikke implementerer ICalculatorContainer - sker der ikke noget ved det... man må bare undvære oplysningen.

jeg håber du kan få det til at hænge sammen.

mvh

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace EXP2
{
    // delegate
    public delegate void CalculateEventHandler(object sender, CalculationEventArgs e);

    // argument til brug i forbindelse med beregningsevent
    public class CalculationEventArgs : System.EventArgs
    {
        // bare privat variabel til resultatet
        private int result;

        // property til resultatet
        public int Result
        {
            get{return result;}
        }

        // kun en konstruktør der tager et resultat må anvendes
        public CalculationEventArgs(int result)
        {
            this.result = result;
        }
    }

    // interface for "beregnere"
    public interface ICalculator
    {
        event CalculateEventHandler Calculation;
        int Result{get;}
    }

    // og et nyt interface til en beregningscontainer
    public interface ICalculaterContainer
    {
        int Total{get;}
    }

    // bare en side
    public class bsp_andreas : System.Web.UI.Page, ICalculaterContainer
    {
        protected HtmlForm Form1;
        protected Button btnCalculateAll, btnClear;
        protected TextBox txtResult;

        private int sumOfCalculations = 0;

        // her er så den property der er defineret under interfacet,
        // og som en calculator kunne finde på at spørge på.
        public int Total
        {
            get{return sumOfCalculations;}
        }

        private void Page_Load(object sender, System.EventArgs e)
        {
            // vi sætter en stak beregnere ind
            for(int i=0;i<10;i++)
            {
                Calculator calc = new Calculator();
                calc.Calculation += new CalculateEventHandler(calc_Calculation);
                Form1.Controls.Add(calc);
            }

            // så vi kan se resultatet
            txtResult = new TextBox();       
            txtResult.TextMode = TextBoxMode.MultiLine;
            txtResult.ReadOnly = true;
            txtResult.BackColor = Color.LightBlue;
            txtResult.Width = 232;
            txtResult.Height = 400;
            Form1.Controls.Add(txtResult);

            // knap til udførelse af beregning
            btnCalculateAll = new Button();
            btnCalculateAll.Text = "Calculate All";
            btnCalculateAll.Width = 116;
            btnCalculateAll.Click += new EventHandler(btnCalculateAll_Click);
           
            // knap til at slette indholdet af resutatboksen
            btnClear = new Button();
            btnClear.Text = "Clear";
            btnClear.Width = 116;
            btnClear.Click += new EventHandler(btnClear_Click);
           
            // ind med sagerne
            Form1.Controls.Add(new LiteralControl("<br>"));
            Form1.Controls.Add(btnCalculateAll);
            Form1.Controls.Add(btnClear);
        }

        // std
        override protected void OnInit(EventArgs e)
        {
            InitializeComponent();
            base.OnInit(e);
        }
       
        // std
        private void InitializeComponent()
        {   
            this.Load += new System.EventHandler(this.Page_Load);
        }

        // håndterer når der er udført en beregning i en kontrol
        private void calc_Calculation(object sender, CalculationEventArgs e)
        {
            Refresh();
        }

        // håndter at der trykkes på "beregn-det-hele-knappen"
        private void btnCalculateAll_Click(object sender, EventArgs e)
        {
            Refresh();
        }

        // opdaterer resultatet
        private void Refresh()
        {
            // starter med at nulstille resultatet
            sumOfCalculations = 0;

            // her høster vi så værdien fra alle beregnere der er proppet direkte på formen
            foreach(Control c in this.Form1.Controls)
            {
                if(c is ICalculator)
                {
                    sumOfCalculations += ((ICalculator)c).Result;
                    txtResult.Text += "\n" + c.ToString();
                }
            }

            // og indsætter værdien
            this.txtResult.Text += "\nIALT : " + sumOfCalculations.ToString();
        }

        // tømmer bare resultatboksen
        private void btnClear_Click(object sender, EventArgs e)
        {
            this.txtResult.Text = String.Empty;
        }
    }   

    // beregner kontrol
    public class Calculator : Panel, ICalculator, INamingContainer
    {
        // private variabel til resultatet
        private int _result;

        // angiver om der bør genberegnes
        private bool _isDirty = true;

        // kontroldeklerationer
        protected TextBox txtVal1, txtVal2, txtResult;
        protected Button btnCalculate;

        // label til angivelse af hvor stor en del denne værdi udgører af summen af Calculators på formen
        protected Label lblPartOfSum;

        // event der kan abboneres på udefra
        public event CalculateEventHandler Calculation;

        // property så resultatet kan hentes "udefra" - der beregnes hvis det er nødvendigt
        public int Result
        {
            get
            {
                EnsureChildControls();
                if(_isDirty)
                    DoCalculation();
                return _result;
            }
        }

        protected override void CreateChildControls()
        {
            // den første tekstboks
            txtVal1 = new TextBox();
            txtVal1.Text = "1";
            txtVal1.Width = 50;
            txtVal1.TextChanged += new EventHandler(txtVal_TextChanged);

            // den anden tekstboks
            txtVal2 = new TextBox();
            txtVal2.Text = "1";
            txtVal2.Width = 50;
            txtVal2.TextChanged += new EventHandler(txtVal_TextChanged);

            // tekstboks til resultatet
            txtResult = new TextBox();
            txtResult.ReadOnly = true;
            txtResult.Width = 50;
            txtResult.BackColor = Color.Silver;
           
            // knap så beregningen kan foretages fra kontrollen
            btnCalculate = new Button();
            btnCalculate.Text = "Calculate";
            btnCalculate.Click += new EventHandler(btnCalculate_Click);

            // label
            lblPartOfSum = new Label();

            // og de indsættes
            this.Controls.Add(txtVal1);
            this.Controls.Add(txtVal2);
            this.Controls.Add(txtResult);
            this.Controls.Add(btnCalculate);
            this.Controls.Add(lblPartOfSum);

            // og basen kaldes (std)
            base.CreateChildControls ();
        }

        // bare en convenience-metode til at affyre eventet
        private void FireCalculation(CalculationEventArgs e)
        {
            if(Calculation != null)
                Calculation(this, e);
        }

        // når der klikkes på knappen - ønskes beregningen udført,
        // og hvis der skulle være nogle der lytter - får de at vide at beregningen er udført
        private void btnCalculate_Click(object sender, EventArgs e)
        {
            DoCalculation();
            FireCalculation(new CalculationEventArgs(this.Result));
            if(this.Page is ICalculaterContainer)
            {
                double partOfSum = ((double)this.Result / (double)(((ICalculaterContainer)this.Page).Total)) * 100;
                lblPartOfSum.Text = String.Format("Udgør {0}% af det hele", partOfSum.ToString("c"));
            }
        }   
   
        // sørger bare for at beregne værdien
        private void DoCalculation()
        {
            _result = int.Parse(txtVal1.Text) * int.Parse(txtVal2.Text);
            _isDirty = false;
            this.txtResult.Text = _result.ToString();
        }

        // angiver at der skal genberegnes fordi teksten er skiftet
        private void txtVal_TextChanged(object sender, EventArgs e)
        {
            _isDirty = true;
        }

        // overskrivninga af tostring() så den returnerer regnestykket
        public override string ToString()
        {
            return String.Format("{0}*{1}={2}", txtVal1.Text, txtVal2.Text, Result);
        }
    }
}
Avatar billede snepnet Nybegynder
02. oktober 2004 - 02:45 #48
hej andreas :o)
hvordan ser det ud her ?
mvh
Avatar billede snepnet Nybegynder
09. oktober 2004 - 15:57 #49
hej andreas :o)
jeg smider et svar her hvis du kunne bruge det til noget.
mvh
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester