Autor Beitrag
Talemantros
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 444
Erhaltene Danke: 2

Win7 Proff 64bit
C# (VS2013)
BeitragVerfasst: Mo 02.02.15 19:43 
Hallo,
ich habe eine Form "Warenrundlauf", welche beim Starten 2 UserControls lädt.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
        private void Warenrundlauf_Shown(object sender, EventArgs e)
        {
            try
            {
                    WarenrundlaufUserControl controlDetail = new WarenrundlaufUserControl();
                    controlDetail.Dock = DockStyle.Top;
                    Controls.Add(controlDetail);

                    WarenrundlaufScanUserControl controlScan = new WarenrundlaufScanUserControl();
                    controlScan.Dock = DockStyle.Top;
                    Controls.Add(controlScan);
            }
            catch (Exception ex)
            {
                MySqlError.SetError(ex.Message, this.ToString(), Global.GetCurrentFunctionName());
                MsgAusgabe.ShowError(ex.Message);
            }
        }


Die UserControl WarenrundlaufScan beinhaltet eine TextBox, die beim VErlassen den eingegeben String in einen Long umwandeln soll und diesen an die andere USerControl geben soll.

Dazu habe ich in der UserControl, die die Daten empfangen soll eine Property gemacht.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
        public WarenrundlaufUserControl()
        {
            InitializeComponent();
        }

        private void WarenrundlaufUserControl_Load(object sender, EventArgs e)
        {
            //Test
           //bsArticleDetails.DataSource =  ArticleMethods.GetOktabinerDetails(16)
           //lblNaechsterSchritt.Text = ArticleMethods.GetStringNextProcessStep(16, ArticleMethods.GetLongActuellSchritt(16)+1);
        }

        public long ArticleId
        {
            set
            {
                bsArticleDetails.DataSource = ArticleMethods.GetOktabinerDetails(value);
            }
        }


Nun ist es mir in der UserCOntrol zum Scannen aber nicht möglich die Property auszuwählen

ausblenden C#-Quelltext
1:
WarenrundlaufUserControl.ArticleId //Geht leider nicht					


Und mit folgendem Code passiert auch nichts:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
        private void txtScanfeld_Leave(object sender, EventArgs e)
        {
            WarenrundlaufUserControl myControl = new WarenrundlaufUserControl();
            myControl.ArticleId = ArticleMethods.GetLongIDFromArticle(txtScanfeld.Text);
        }


Kann mir da mal jemand behilflich sein?

Danke

Gruß
Daniel
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mo 02.02.15 20:30 
Hallo Talemantros,

deine beiden Ansätze sind typische Anfängerfehler, welche ich in meinen Artikel zur "Kommunikation von 2 Forms" auch erwähne.
Ich hatte dir schon mal bei deinem Thema Button Enabled wenn Textboxen geändert werden den Link dazu gepostet. ;-)

Bei ersterem Code von dir mußt du natürlich eine (Member-)Variable vom Typ WarenrundlaufUserControl haben, um darauf zuzugreifen.

Bei deinem zweiten Code erzeugst du ja ein neues (unsichtbares) UserControl und schreibst in dessen Eigenschaft hinein.

Am besten, du liest (noch) mal meinen Artikel und nimmst den Ansatz mit dem Erzeugen eines eigenen Ereignisses:
- WarenrundlaufScanUserControl wirft dann das Ereignis
- die Form Warenrundlauf abonniert das Ereignis und setzt dann die Eigenschaft in dem anderen UserControl WarenrundlaufUserControl
Talemantros Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 444
Erhaltene Danke: 2

Win7 Proff 64bit
C# (VS2013)
BeitragVerfasst: Di 03.02.15 10:54 
Hallo TH69,
ja leider ist dies immernoch ein Teil, den ich bisher nicht so gut verstanden habe.
Ich versuche noch mal mein Glück und melde mich wieder mit dem Resultat.

Danke

Gruß
Daniel
Talemantros Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 444
Erhaltene Danke: 2

Win7 Proff 64bit
C# (VS2013)
BeitragVerfasst: Di 03.02.15 18:48 
Hallo zusammen,
ich habe nun eine "Lösung" bzw. einen Ansatz.
Funktionieren tut er, aber würde mich über Anregungen freuen und Kommentare.

In dem UserControl "WarenrundlaufUserControl" welches das Event wirft habe ich folgendes analog dem Tutorial.

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
    public partial class WarenrundlaufScanUserControl : UserControl
    {
        public WarenrundlaufScanUserControl()
        {
            InitializeComponent();
        }

        private void txtScanfeld_Leave(object sender, EventArgs e)
        {
            OnReadScan(new TextEventArgs(txtScanfeld.Text));
        }

        public class TextEventArgs : EventArgs
        {
            public TextEventArgs(string text)
            {
                Text = text;
            }

            public string Text { get; set; }
        }

        public event EventHandler<TextEventArgs> ReadScan;

        protected virtual void OnReadScan(TextEventArgs e)
        {
            EventHandler<TextEventArgs> ev = ReadScan;
            if (ev != null)
                ev(this, e);
        }

    }


In der Form, auf dem beide UserControls sitzen.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
    public partial class Warenrundlauf : KryptonForm
    {
        WarenrundlaufUserControl controlDetail = new WarenrundlaufUserControl();

        public Warenrundlauf()
        {
            InitializeComponent();
        }

        private void Warenrundlauf_Shown(object sender, EventArgs e)
        {
                    controlDetail.Dock = DockStyle.Top;
                    Controls.Add(controlDetail);

                    WarenrundlaufScanUserControl controlScan = new WarenrundlaufScanUserControl();
                    controlScan.ReadScan += UpdateReadScan; //Ereignis abonniert
                    controlScan.Dock = DockStyle.Top;
                    Controls.Add(controlScan);
        }

        void UpdateReadScan(object sender, WarenrundlaufScanUserControl.TextEventArgs e)
        {
            controlDetail.ArticleId = ArticleMethods.GetLongIDFromArticle(e.Text); //setzt die Property
            controlDetail.LoadData(); //Führt die Anzeige aus! Ok so?!
        }
}


Das empfangende UserControl hat dann zum Ausführen:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
        public long ArticleId { get; set; }

        public void LoadData()
        {
            bsArticleDetails.DataSource = ArticleMethods.GetOktabinerDetails(ArticleId);
            lblNextStep.Text = ArticleMethods.GetStringNextProcessStep(ArticleId, ArticleMethods.GetLongActuellStep(ArticleId) + 1);
        }


Kann man das so lassen, oder würde man das ausführen anders gestalten?!

Danke

VG
Daniel
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Di 03.02.15 19:37 
Sieht gut aus.

2 Sachen die man überlegen kann
a.) Wenn LoadData immer beim Ändern der ArtikelId ausgeführt werden soll. Würde ich das vielleicht eher in den Setter von ArtikelId einbauen anstatt explizit selbst auszurufen;
b.) Sich fragen ob ein UserControl die Datenquelle kennen sollte. Ich würde eher auf der Form das Laden der Daten in UpdateReadScan erledigen und die Daten dann ins UserControl schieben. Ist aber vermutlich eher eine philosophisches Ding. Für mich fühlt sich das falsch an das ein UserControl so ~viel~ kann.


Edit: Ich habe nochmal auf deinen Code im ersten Beitrag geschaut
das hier MySqlError.SetError(ex.Message, this.ToString(), Global.GetCurrentFunctionName()); läßt mich befürchten das in GetCurrentFunctionName irgendein böses StrackTrace walking passiert.
Das ist prinzipiell in 4.5 nicht mehr nötig es gibt das CallerMemberName Attribut. (Es gibt auch andere Attribute wie CallerFilePath oder CallerLineNumber die beim typischen Exception loggen helfen)

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
MySqlError.SetError(ex.Message, this.ToString()); // ohne 3.ten Parameter aufrufen das Attribut füllt das passende automatisch.

static class MySqlError
{
    public static  void SetError(string message, string whatever, [CallerMemberName] string functionName = "")
    { 
        //.. deine bisherige Implementierung
    }
}


Zuletzt bearbeitet von Ralf Jansen am Di 03.02.15 20:30, insgesamt 1-mal bearbeitet
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4764
Erhaltene Danke: 1052

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Di 03.02.15 19:52 
:zustimm:

Zu deiner Anzeige kann ich wenig sagen, da ich den Rest der Klasse nicht kenne.
Persönlich hätte ich jetzt eher an eine Methode ShowArticleData(long articleId) gedacht - ich weiß ja nicht, wofür die Eigenschaft ArticleId bei der Klasse sonst noch benutzt wird.
Alternativ ginge natürlich auch, daß beim Setter der ArticleId automatisch die Anzeige aktualisiert wird.

Ralf war jetzt schneller, aber auch sein 2. Punkt ist eine Überlegung wert (für mich sind UserControls auch eher dumme Dinger, welche von außen mit Daten gefüttert werden - insbesondere in Hinblick auf Wiederverwendbarkeit).
Talemantros Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 444
Erhaltene Danke: 2

Win7 Proff 64bit
C# (VS2013)
BeitragVerfasst: Mi 04.02.15 08:43 
Guten Morgen,
vielen Dank euch beiden schon mal für eure Anmerkungen.
Ich habe gestern Abend nochmal alles überflogen und versucht nachzuvollziehen und ihr kennt mich mittlerweile bestimmt gut genug, dass ich es nicht so einfach stehen lassen kann und mal nachhacken muss.

@Ralf
du hast Recht, dass hinter Global.GetCurrentFunctionName() ein StackTrace steht.

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
        public static string GetCurrentFunctionName()
        {
            StackTrace stackTrace = new StackTrace();
            StackFrame stackFrame = stackTrace.GetFrame(1);
            MethodBase methodBase = stackFrame.GetMethod();

            return methodBase.ToString();
        }


Das hatte ich mir so ergoogelt und da es gemacht hatte was ich wollte nicht weiter hinter fragt. Wenn du aber sagst, dass es "böse" dann werde ich das entsprechend anpassen auf deinen Vorschlag.

Ich habe mal versucht LoadData auszuführen, wenn der Setter verändert wird.

Meintet ihr das z.B.: so?:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
        public long ArticleId
        {
            get
            {
                return ArticleId;
            }
            set
            {
                ArticleId = value;
                LoadData();
            }
        }


Da hätte ich jetzt das Problem, dass beim setzen des Setters das Programm abstürzt und nicht mehr reagiert.

Zitat:

b.) Sich fragen ob ein UserControl die Datenquelle kennen sollte. Ich würde eher auf der Form das Laden der Daten in UpdateReadScan erledigen und die Daten dann ins UserControl schieben. Ist aber vermutlich eher eine philosophisches Ding. Für mich fühlt sich das falsch an das ein UserControl so ~viel~ kann.


Habe da gestern Abend drüber nachgedacht und kam aber zu keinem Ergebnis wie das gehen sollte.
Wenn ich die BindingSource statt in dem UserControl in der Form habe und dort fülle, müsste ich dies zum UserControl schieben.
Beispielsweise ca so vermute ich  userControl.Property = BindingSource.DataSource as Klasse
Wie kann ich dann einzelnen Steuerelementen Sagen, welche Werte sie anzeigen sollen?! Das hatte ich bisher ja immer über die BindingSource und den Eigenschaften des Steuerelements getan?!

Zum Guten Schluss hätte ich noch was mit der Kommunikation zwischen 2 Forms:
Im Moment hatte ich es ja mehr oder weniger "abgetippt". Da ich es aber gern verstehen möchte um es auch wieder aus dem Kopf zu bauen, habe ich mir gestern nochmal alles angeschaut.

Das meiste versteh ich auch, mir fehlt nur folgender Teil "schwer"

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
         
protected virtual void OnReadScan(TextEventArgs e)
        {
            EventHandler<TextEventArgs> ev = ReadScan;
            if (ev != null)
                ev(this, e);
        }


Ich habe hier doch eine Methode, die als Parameter die Klasse übergeben bekommt, die als Event deklariert ist.
Innerhalb dieser Methode erstelle ich eine neue Instanz der Klasse und "binde" sie an das Event

Wenn ich diese  ev Instanz fülle versteh ich das  this nicht.

Gruß
Daniel

Edit:
Es hängt sich auf auf wenn ich LoadData umbaue zu

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
        public long ArticleId
        {
            get
            {
                return ArticleId;
            }
            set
            {
                ArticleId = value;
                ShowArticleID(ArticleId);
            }
        }

        private void ShowArticleID(long articleId)
        {
            bsArticleDetails.DataSource = ArticleMethods.GetOktabinerDetails(articleId);
            lblNaechsterSchritt.Text = ArticleMethods.GetStringNextProcessStep(articleId, ArticleMethods.GetLongActuellSchritt(articleId));
        }
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4701
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 04.02.15 10:47 
Zitat:
Meintet ihr das z.B.: so?:


Für ArtikelId mußt du wieder eine private Variable verwenden (im Code kannst du ja ruhig weiter auch intern auf die Property zugreifen). So wie du das jetzt machst hast du dir in getter und setter eine endlos Rekursion gebastelt weil du wieder auf dich selbst zugreifst

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
private articleId;
public long ArticleId
{
    get
    {
       return articleId;
    }
    set
    {
       articleId = value;
       ShowArticleID(ArticleId);
    }
}


Zitat:
Wenn ich die BindingSource statt in dem UserControl in der Form habe und dort fülle, müsste ich dies zum UserControl schieben.


Du solltest eigentlich im UserControl nur eine Methode veröffentlichen der man die Daten übergibt. Also vermutlich das was ArticleMethods.GetOktabinerDetails holt. In der Methode kannst du doch die Sachen wieder an BindingSourcen oder was auch immer zuweisen nichts anderes als das was du jetzt machst. Nur das UserControl muß eben selbt kein Wissen mehr über ArticleMethods haben was aus Erfahrung eher besser ist.

Zitat:
Ich habe hier doch eine Methode, die als Parameter die Klasse übergeben bekommt, die als Event deklariert ist.


Du übergibst OnReadScan die EventArgs und nicht den Event.

Zitat:
Innerhalb dieser Methode erstelle ich eine neue Instanz der Klasse und "binde" sie an das Event


Nein? Du erstellst eine Kopie des Events ReadScan mit Namen ev. Dann prüfst du ob an ev auch EventHandler hängen. Und wenn ja führst du den Delgaten aus (rufst alle EventHandler auf).

Das zuweisen zu ev (du könntest auch ReadScan direkt aufrufen) und damit das kopieren des Events ist nur zur Sicherheit in Multithreadumgebungen gedacht. Wenn parallel jemand zu ReadScan weitere Delegaten hinzufügen oder wegnehmen würde kracht es sonst. Ein Event (bzw. Delegat) kannst du dir als Liste von Methoden denken. Und ev(this, e) führt alle diese Methoden nacheinander aus. Dafür werden die Methoden enumeriert (wie bei einem foreach) und die Listen darf man während dem Enummerieren nicht ändern (und wenn man es dürfte hätte da immer noch schwierige Effekte) . Die Zuweisung zu ev erstellt also eine Kopie vom ReadScan Delegaten. Änderungen an ReadScan könen somit keine Probleme mehr auslösen da sie keinen Einfluss auf ev haben udn da ev eine lokale Variable ist kommt da keiner dran. Enummerieren des Events ist also sicher möglich. Die Signatur deiner Methoden ist (Sender, EventArgs). This wird also für den Sender verwendet der im EventHandler ankommt und e halt für die EventArgs.
Talemantros Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 444
Erhaltene Danke: 2

Win7 Proff 64bit
C# (VS2013)
BeitragVerfasst: Mi 04.02.15 20:42 
Vielen Dank zusammen und einen schönen Abend