Entwickler-Ecke

Sonstiges (.NET) - Grundsätzliches herangehen // PinAbfrage


Talemantros - Mo 28.04.14 16:47
Titel: Grundsätzliches herangehen // PinAbfrage
Hallo,
ich möchte gern eine PinAbfrage in meine Applikation einbauen.
Dies soll so aussehen, dass immer wenn eine wichtige Tätigkeit ansteht (zum Beispiel Speichern) soll sich eine neue WinForm öffnen, die eine Pin Abfragt.

Aus dieser Pin resultiert aus einer Methode der Mitarbeitername, der diese Aktion versucht auszuführen.
Nun möchte ich, dass der Name und die Pin in dem Usercontrol der Form wo auf Speichern gedrückt wurde vorhanden ist um diesen weiter zu verarbeiten.

Vermutlich reden wir hier wieder von Events etc.!?
Wie müsste ich da ran gehen?

Danke

Gruß

EDIT: Habe hierzu noch mal das Beispielprogramm von TH69 http://www.bitel.net/dghm1164/programming/Kommunikation_von_2_Forms.html zu rate gezogen und versuche das für meine Zwecke zu nutzen


Talemantros - Di 06.05.14 16:09

Hallo,
wie oben geschildert möchte ich bei bestimmten Aktionen in meiner Software abfragen, ob der Mitarbeiter dies darf oder nicht.
Dazu wollte ich eine Form anzeigen lassen, die eine Pin abfrage und nach einigen Kriterien darf der Mitarbeiter dies dann oder halt nicht.
Den Namen des Mitarbeiters benötige ich dann in der Grundform, wo er die Aktion tätigen wollte.

Nun habe ich mir dazu o.g. Projekt von TH69 heruntergeladen um die Kommunikation zwischen 2 Forms zu verstehen.
Da mir Copy & Paste nicht liegt und ich gerne verstehe was ich da tue, hätte ich da mal eine Frage zu folgendem Code.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
    protected void OnUpdateText(string text)
    {
      OnUpdateText(new TextEventArgs(text));
    }

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


Mir ist an dieser Stelle nicht ganz klar, warum ich 2 Methoden brauche mit demselben Namen und für was das virtual steht bzw was die einzelnen Methoden für sich da welche Aufgabe übernehmen.
Kann jemand versuchen mir das näher zu bringen?

Danke


Ralf Jansen - Di 06.05.14 16:51

Bezüglich virtual hilft denke ich am besten ein Blick in die MSDN Hilfe [http://msdn.microsoft.com/de-de/library/9fkccyh4.aspx]. Ich könnte eh nur wiederholen was da schon steht und vermutlich auch nicht besser.

Und brauchen tust du die beiden Methoden nicht genauso wenig wie das virtual hier jetzt gerade zwingend wäre oder die Methoden protected zu machen. Ein OnEvent zu haben um einen Event zu feuern ist aber der übliche Best Practise [http://msdn.microsoft.com/en-us/library/vstudio/ms229011.aspx] Ansatz. Gerade wenn man es richtig macht und vor dem Aufrufen den EventHandler kopiert und dann den Event über die Kopie feuert so wie im gezeigten Code. Mitten im Code einfach ein


C#-Quelltext
1:
2:
if(UpdateText != null)
   UpdateText(thisnew TextEventArgs("MeinLieberText"));


anstatt einer der beiden Methodenaufrufe ginge aber auch.


Th69 - Di 06.05.14 17:12

Hallo Talemantros,

die erste Methode ist nur eine sog. "Convenience"-Methode, d.h. um Schreibarbeit beim Aufruf zu sparen.
Und die zweite ist, wie Ralf ja schon schrieb, der übliche Weg, um Events auszulösen (s.a. Event Design [http://msdn.microsoft.com/en-us/library/vstudio/ms229011%28v=vs.100%29.aspx] sowie How to: Raise Base Class Events in Derived Classes [http://msdn.microsoft.com/de-de/library/hy3sefw3.aspx]). Aber thread-safe, im Gegensatz zu dem von Ralf beschriebenen Aufruf (durch Anlegen einer lokalen Variable)!

Mittels einer Erweiterungsmethode (in einer eigenen statischen Klasse) läßt sich das sogar noch weiter verkürzen:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
/// <summary>
/// Raises an event thread-safe
/// (cause the 'this' parameter of an extension method is always handled thread-safe by the compiler)
/// </summary>
/// <typeparam name="T">type of the EventArgs parameter</typeparam>
/// <param name="eventHandler">event handler</param>
/// <param name="sender">sender object</param>
/// <param name="e">event args</param>
public static void RaiseEvent<T>(this EventHandler<T> eventHandler, object sender, T e)
    where T : EventArgs
{
    if (eventHandler != null)
    {
        eventHandler(sender, e);
    }
}


Der Aufruf verkürzt sich dann zu

C#-Quelltext
1:
UpdateText.RaiseEvent(thisnew TextEventArgs("MeinLieberText"));                    

(ohne also selber explizit auf null abprüfen zu müssen!)


Ralf Jansen - Di 06.05.14 17:26

Ich glaube der Kommentar an der Extension Method ist gelogen ;)
So kurze statische Methoden werden fast garantiert geinlined und es wird keine Methodenaufruf passieren der eine Kopie anlegt.
Insofern sollte in der Methode immer noch der Handler explizit kopiert werden da man sich nicht sicher sein kann das da bereits automatisch kopiert wurde.

Oder gibt es bei Extension Method da eine Sonderbehandlung vom Compiler die ich nicht kenne?

Edit : Gerade ausprobiert und die Extension Method ist nicht im StackTrace, wurde also geinlined und this nicht kopiert.



Quelltext
1:
2:
3:
   at ConsoleApplication15.Program.Handler(Object sender, EventArgs e)
   at ConsoleApplication15.Program.Main(String[] args)
Drücken Sie eine beliebige Taste . . .



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:
33:
34:
class Program
{
    static void Main(string[] args)
    {
        var tmp = new EventRaiser();
        tmp.Event += Handler;
        tmp.ThrowEvent(new object());
    }

    static void Handler(object sender, EventArgs e)
    {
        Console.WriteLine(new StackTrace().ToString());
    }
}

public class EventRaiser
{
    public EventHandler<EventArgs> Event;

    public void ThrowEvent(object sender)
    {
        Event.RaiseEvent(sender, EventArgs.Empty);
    }
}

public static class ExtensionMethods
{
    public static void RaiseEvent<T>(this EventHandler<T> eventHandler, object sender, T e) where T : EventArgs
    {
        var ev = eventHandler;
        if (ev != null)
            ev(sender, e);
    }        
}


Th69 - Di 06.05.14 19:26

Oha, da habe ich ja was angestoßen... ;-)

Bzgl. thread-safe sollte ich wohl etwas zurückhaltender sein - hier ist nämlich nur die Vermeidung einer möglichen NullReferenceException gemeint.

Ich habe mich hauptsächlich auf die Aussagen von Jon Skeet [http://stackoverflow.com/users/22656/jon-skeet] (Autor von "C# in Depth") berufen, s. z.B. Raising C# events with an extension method - is it bad? [http://stackoverflow.com/a/231536]

Aber es gibt eine Reihe von anderen Ansichten über Thread-Safety bzgl. der verschiedenen Versionen, s. z.B. Threadsafe Events [http://www.codeproject.com/Articles/37474/Threadsafe-Events] (Tenor: alle sind nicht thread-safe!).

Weitere Diskussionen gibt es z.B. unter
C# Events and Thread Safety [http://stackoverflow.com/questions/786383/c-sharp-events-and-thread-safety]
Raise event thread safely - best practice [http://stackoverflow.com/questions/3668953/raise-event-thread-safely-best-practice]
...

Fazit: Jetzt weiß ich auch nicht mehr, was ich glauben und verwenden soll?! :gruebel:
Am besten also gar kein Multithreading bei Events verwenden!


Talemantros - Di 06.05.14 20:35

Hmm. Nicht böse sein aber ich bin jetzt nicht weiter.
Ich glaube das lese ich noch einige Male :-)


Ralf Jansen - Di 06.05.14 20:58

Besser du ignorierst es einfach ;) Das sind die bösen Details bei der Multithread-Programmierung die dich jetzt nicht stören brauchen. Und selbst dann sind die auch eher akademischer Natur.

Th69 hat folgendes geschrieben:
Fazit: Jetzt weiß ich auch nicht mehr, was ich glauben und verwenden soll?! :gruebel:

a.) den delegaten zum feuern kopieren
b.) In den EventHandler Methoden auf sich selbst prüfen ob man den disposed ist.
Das Problem das eine EventHandler Methode so noch nach ihrem Abhängen vom Event aufgerufen wird halt ich für äußerst klein. Wenn dann tritt es meiner Meinung fast nur im Context des Disposens auf das mit b. aber abgefangen ist.
Ein Programmiermuster in dem sich Objekte zur Laufzeit an Event hängen und wieder abhängen unabhängig von ihrer eigene Lebensdauer ist mir noch nicht unter gekommen. Und wenn doch .. ääääh .. öhm ... lass ich mir dann was einfallen ;)


Talemantros - Mi 07.05.14 05:54

Guten Morgen,
ok dann ignoriere ich eure Unterhaltung. Die hat mich eh nur verwirrt. :-)
Ich versuche mich mal daran, dass irgendwie mit dem Werfen und Fangen umzusetzen.
Bin gespannt.

Gruß

EDIT:
user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
a.) den delegaten zum feuern kopieren
b.) In den EventHandler Methoden auf sich selbst prüfen ob man den disposed ist.
Das Problem das eine EventHandler Methode so noch nach ihrem Abhängen vom Event aufgerufen wird halt ich für äußerst klein. Wenn dann tritt es meiner Meinung fast nur im Context des Disposens auf das mit b. aber abgefangen ist.
Ein Programmiermuster in dem sich Objekte zur Laufzeit an Event hängen und wieder abhängen unabhängig von ihrer eigene Lebensdauer ist mir noch nicht unter gekommen. Und wenn doch .. ääääh .. öhm ... lass ich mir dann was einfallen ;)

Sorry, aber ich komme nicht klar. Irgendwie geht hier gar nichts und ich weiß auch nicht so richtig wie ich anfangen soll. Wärt ihr so gut mir einen Denkanstubs zu geben? :-(

Danke


Ralf Jansen - Mi 07.05.14 09:29

Das was du zitierst gehörte zum Teil den du besser ignorieren solltest :(
Vielleich hat Th69 die Macht den wenig hilfreichen Teil aus diesem Thread abzutrennen damit die Sicht für dich wieder klarer wird.


Talemantros - Do 08.05.14 08:16

Hi,
ja das wäre nett, da ich jetzt gar kein Überblick mehr habe bzw nicht weiß wie es geht :-)

Gruß


Th69 - Do 08.05.14 09:12

Hallo Talemantros,

ich hatte gestern schon die entsprechenden Beiträge von Ralf und mir in grau eingefärbt.

Hast du denn jetzt noch weitere Fragen zum Thema Ereignisse (events)?

Bezogen auf deine Hauptfrage kommt es darauf an, von wo du die Pinabfrage aufrufst. Wenn du diese innerhalb der MainForm aufrufst, dann kannst du den Pinabfrage-Dialog ja direkt aufrufen. Wenn du jedoch innerhalb deiner Business-Logik diesen aufrufen möchtest, dann solltest du (entsprechend meines Artikels) auf jeden Fall ein Ereignis dafür benutzen, so daß dann von außen festgelegt werden kann, wie der Pinabfrage-Aufruf erfolgen soll. Und bei dem Ereignis würdest du dann eine eigene EventArgs-Klasse benutzen, welche die nötigen Eigenschaften enthält (statt TextEventArgs z.B. PinEventArgs).
Wenn du die Abfrage auch abbrechbar machen möchtest, dann könntest du von CancelEventArgs [http://msdn.microsoft.com/de-de/library/system.componentmodel.canceleventargs%28v=vs.110%29.aspx] erben und nach dem Auslösen des Ereignisses die Eigenschaft Cancel abfragen.

PS:
Talemantros hat folgendes geschrieben:
Ich versuche mich mal daran, dass irgendwie mit dem Werfen und Fangen umzusetzen.

Diese Ausdrucksweise benutzt man eher für Exceptions (throw and catch). Bei Ereignissen spricht man eher von Auslösen (oder Feuern) und Behandeln (raise (or fire) and handle).
Und nur der Vollständigkeit wegen: das Hinzufügen einer Methode zu einem Ereignis (mittels +=) nennt man dann Abonnieren (subscribe).


Talemantros - Do 08.05.14 09:41

Hi,
das mit dem einfärben hatte ich nicht gesehen und danke für die Begriffsbestimmung.

Die Abfrage der Pin soll unterschiedlich sein:
Es gibt Formulare die von der Mainform aufgerufen werden und nur angezeigt werden dürfen, wenn die PinAbfrage gültig ist.

Andere Formulare werden angezeigt und die Abfrage soll beim Drücken des Speichernbuttons drücken und speichern nur ausgeführt werden, wenn die Abfrage gültig ist.

Ich versuche mich im Laufe des Tages mal an deiner Beschreibung, befürchte ich aber dass ich dafür noch nicht weit genug bin. Hatte gehofft es funktioniert mit der Kommunikation von 2 Forms :-(

Gruß


Talemantros - Fr 09.05.14 10:27

Hi,
also ich bastel seit 1 Tag daran rum und habe rein gar nichts irgendwie, geschweige den würde ich den Quellcode verstehen und bin gerade sehr gefrustet. :-(
Wäre jemand bereit mir ein Beispiel zu posten und mit Erklärungen wie der Quelltext arbeitet und so weiter?

Jetzt habe ich das mit den Modellklassen langsam verstanden und jetzt hänge ich wieder. Schon sehr frustrierend.

Würde mich freuen, wenn sich jemand meiner erbarmt :-)

VG


Ralf Jansen - Fr 09.05.14 10:41

Versuchen wir mal dein Problem genauer zu Analysieren und zu überlegen wie man das am besten angeht und dann schauen wir uns an welche technischen Hürden dabei zu nehmen sind (Vielleicht stellt sich das mit den Events ja gar nicht)

Annahmen/Fragen
a.) Es gibt per PIN zu schützende Formulare und welche die nicht zu schützen sind?
b.) Die PIN ist Userspezifisch (jeder hat eine eigene) oder Formularspezifisch( jedes Formular hat eine eigene) oder sogar die Kombination aus beidem (jeder Formular+User Kombination seine eigene PIN)
c.) Wenn die PIN Userspezifisch ist soll die dann bei jedem Formularaufruf abgefragt werden oder reicht es das einmal zu tun? Und für die Lebensdauer der Anwendung gilt das dann als authentifiziert?
d.) Wenn du ein Datenmodel hast ist eher das Model per PIN abfrage schützenswert oder tatsächlich die Formulare? Wenn dir nicht ganz klar ist wie das zu beantworten ist. Dann frage ich mal so. Sind bestimmte Objekte per PIN zu schützen (in einer anderen Frage hattest du ja sowas wie Module , Gruppen, Benutzer gehabt) oder eher Vorgänge (aka Benutzer anlegen wäre per PIN zu schützen, Benutzer bearbeiten aber zum Beispiel nicht).


Talemantros - Fr 09.05.14 11:28

Hallo Ralf,
vielen Dank schon mal

a.) Ja, es gibt Formulare die jeder einsehen kann und die nicht geschützt sind und andere Formulare benötigen der PInAbfrage.
b.) Der Pin ist UserSpezifisch. Jeder Benutzer hat einen eigenen eindeutigen Pin. Über diesen Frage ich dann in der Datenbank ab, ob der Benutzer diese Aktion ausführen darf und wenn ja will ich den Namen des Benutzers in dem neu geöffneten Formular vorhalten um seine Aktion in der Historie zu speichern.
c.) Es sollen immer andere Mitarbeiter an dem System arbeiten und daher soll die Abfrage immer erscheinen und nicht im Hintergrund vorgehalten werden. Der PC steht dann theoretisch in einem öffentlichen Bereich wo jeder halt mal was macht. (so stellt sich das meine Chefin vor)
d.) Ich hoffe dass ich dies jetzt richtig verstehe. Also ich habe die Formulare die ich bisher habe so aufgebaut, dass ein Formular quasi ein Recht ist. Mitarbeiter anlegen bearbeiten und löschen. Von daher würde ich erst mal denken, dass das Formular schützenswert ist. Lasse mich aber auch gern eines besseren belehren.

Wobei ich gerade drüber nachdenke und es vielleicht auch irgendwie doof ist, wenn ein Mitarbeiter ein Formular mit seiner Pin öffnet und dann weg muss und lässt das Fenster auf und dann ein anderer mit seinen Rechten weiter arbeitet. Wäre dann ja irgendwie sinniger, wenn die Abfrage beim Drücken des Buttons kommt (Speichern, Bearbeiten, Löschen)

Gruß


Ralf Jansen - Fr 09.05.14 11:49

Zitat:
Wäre dann ja irgendwie sinniger, wenn die Abfrage beim Drücken des Buttons kommt (Speichern, Bearbeiten, Löschen)


Das impliziert das wenn jeder erstmal alles öffnen darf er auch zumindest erstmal alle Daten sehen kann.Ein solches Vorgehen macht gefühlt nur Sinn wenn du nur steuerbare Bearbeitungsrechte hast aber keine Berechtigungsstufen die die Sichtbarkeit von Daten betrifft. Sehen darf dann erstmal jeder alles nur bearbeiten/löschen etc. dürfen nur bestimmte Leute. Jedesmal eine PIN eingeben zu müssen erscheint extrem nerv tötent. Was spricht dagen einfach die Anwendung mit Anmeldedaten/Pin zu schützen? Wenn der Mitarbeiter wechselt hat sich der folgende Mitarbeiter halt selbst an die Anwendung anzumelden und der vorherige muß sich eben abmelden? Das was du mit der PIN vorhast kann ich mir nur in einer Arbeitumgebung vorstellen wo n Leute mehr oder weniger geichzeitig an dem PC arbeiten (stelle mir das so wie sich in einem Laden Bäcker o.ä. eine Kasse teilen) wo ummelden zu lange dauern würde. Da stelle ich mir aber eine PIN eingabe auch extrem aufhaltend vor.


Talemantros - Sa 10.05.14 19:06

Hallo Ralf,
Bitte entschuldige die späte Rückmeldung.
Wäre den das Vorgehen so unterschiedlich wenn man die Pin Abfrage mal beim Drücken des Buttons und mal bem öffnen des Forms anzeigt?

Vielleicht könnten wir beides zusammen erarbeiten?

Das mit dem grundsätzlichen Handhaben der Pin Abfrage ist so gewünscht, dass diese immer abgefragt wird.

Wobei ich mich auch nicht wehren würde zu lernen wie man eine dauerhafte Anmeldung im System vorhält.

Viele Grüße
Daniel


Ralf Jansen - So 11.05.14 13:30

Zitat:
Wäre den das Vorgehen so unterschiedlich wenn man die Pin Abfrage mal beim Drücken des Buttons und mal bem öffnen des Forms anzeigt?


Wahrscheinlich nicht. Aber ich weiß zuwenig darüber was du da hast und wohin du damit willst ;)

Zitat:
Das mit dem grundsätzlichen Handhaben der Pin Abfrage ist so gewünscht, dass diese immer abgefragt wird.


Um da wirklich das richtige zu raten fehlt mir ausreichend Hintergrund.

Im allgemeinen Fall hätte man zwischen der UI (deinen Formen) und dem Datenmodell eine Logikschicht der man diese Aufgabe wohl zuschanzen würde.
Ich würde mir das gerade so vorstellen das es eine Klassenschicht gibt mit Funktionen wie Speichere, Lade, Bearbeite Objekt X,Y,Z. Diese Methoden erwarten entweder eine PIN die man mit übergibt und die von dieser Schicht dann geprüft und die gewünschte Aktion dann eben abgelehnt oder durchgeführt wird. In diesem Fall wäre der Code des Formularhandlings für die PIN Eingabe irgendwo in den anderen beteiligten Formularen hinterlegt. Oder die genannten Methoden in der Logikschicht lößen eine Event aus der die PIN erfragt worin dann das Handling für die PIN Eingabe Form liegt. Letzteres wäre etwas weniger Aufwand und würde die PIN Abfrage recht gut zentralisieren. Das Verfahren über Events ist aber nicht wirklich gut mit jeder UI Technik verknüpfbar. In einer Web Umgebung wäre das wohl eher hinderlich. Im Moment, um es nicht zu sehr zu komplizieren, würde ich dir eher raten das über einen Event zu machen. Also einen Event der erwartet das in der aufgerufenen EventHandler Methode die PIN eingetragen wird und dann im Code geprüft wird ob der PIN gültig ist.

Im folgenden eine Beispiel mit einer generischen abstrakten Basisklasse für entsprechende Klassen in der angesprochenen Logikschicht die die Aufgabe der Authorizierung durch einen Eventaufruf übernehmen würde. Die Bezeichnung Controller ist etwas belastet den kannst du auch durch was anderes ersetzen das die mehr sagt. Oder wenn dir das zu komplex (das ist eigentlich alles Infrastruktur das macht man genau 1mal) kannst du dir da auch nur den Eventaufrufteil rausklauben und in deinen bestehende Code zum laden/speichern übernehmen.


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:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
public class Employee
{
    // deine Properties
}

public class AuthorizationEventArgs : EventArgs  
{
    public string PIN { get; set; }
    public bool Cancel  { get; set; } 
}

public class ControllerBase
{
    public event EventHandler<AuthorizationEventArgs> AuthorizationNeeded;

    protected bool IsUserAuthorized
    {
        get
        {
            AuthorizationEventArgs eventArgs = new AuthorizationEventArgs() { Cancel = false, PIN = string.Empty };
            OnAuthorizationNeeded(eventArgs);

            if(eventArgs.Cancel)
                return false;

            return IsValidPIN(eventArgs.PIN);
        }
    }

    private bool IsValidPIN(string pin)
    {
        // wie du das auch imer machst um die PIN zu prüfen mach es von  hier
        return true;
    }


    private void OnAuthorizationNeeded(AuthorizationEventArgs eventArgs)
    {
        EventHandler<AuthorizationEventArgs> handler = AuthorizationNeeded;
        if (handler != null)
            handler(this, eventArgs);
    }    
}

public interface IController<T> where T : class
{
    bool Save(T obj);
    T Load(long id);
}

public abstract class Controller<T> : ControllerBase, IController<T> where T : class
{
    public bool Save(T obj)
    {
        if (!IsUserAuthorized)
            throw new NotAuthorizedException(); // return false oder ein Enum um genauer zu sagen was Sache ist
        return SaveObject(obj);
    }

    protected abstract bool SaveObject(T obj); 

    public T Load(long id)
    {
        if (!IsUserAuthorized)
            throw new NotAuthorizedException(); // Enum um genauer zu sagen was Sache ist oder return null oder ..
        return LoadObject(id);
    }

    public abstract T LoadObject(long id); 
}

public class EmployeeController : Controller<Employee>
{
    protected override bool SaveObject(Employee obj)
    {
        // deine Speicherlogik
        return true;
    }

    public override Employee LoadObject(long id)
    {
        // deine Ladelogik
        return null;
    }
}


Verwendung von irgendwo wäre dann


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
//Am Anfang der Form irgendwo den passenden Controller erzeugen den Event verdrahten und in der Form vorhalten zum speichern und laden
var controller = new EmployeeController();
controller.AuthorizationNeeded += AuthorizationNeeded;

private void AuthorizationNeeded(object sender, AuthorizationEventArgs e)
{
    e.PIN = "1234567"// hier deine sinnvolle PIN Eingabe Methodik
}

// irgendwo anders dann die Funktionen des Controllers auslösen 
controller.Save(deineLiebeEmployeeInstanz);


Talemantros - So 11.05.14 20:11

Hallo Ralf,
irgendwie ist es total deprimierend. Du gibst dir viel Mühe für guten Code und ich verstehe von Antwort zu Antwort weniger.
Ich habe deinen Code jetzt etliche Male gelesen und versteh fast nichts wie es zusammen spielt, geschweige den wie ich es bei mir einbinden müsste um eine TextBox zu haben wo der User seinen Code eingibt.

Es tut mir sehr leid, wenn ich mich jetzt zu doof anstelle ich würde es gern nur so verstehen, dass ich vielleicht irgendwann mal in der Lage bin es selber zu bauen und statt verständlicher wird es für mich nur komplexer.

Ich weiß es ist viel verlangt, aber könntest du mir das in ein Projekt mit TextBox basteln und eventuell mehr kommentieren?
Bzw. vielleicht Quellen nennen woraus ich vielleicht verstehe was es mit den Abstrakten Klassen etc und deren Zusammenspiel mit dem Interface usw. auf sich hat.

Der Bereich wo Load und SaveObject ist geht der dann nur für das Anlegen eines MItarbeiters aus den vorherigen Treats?
Ich bin überfordert. Sorry

Ich versuche es mir jetzt mal schritt für Schritt auseinder zu nehmen. :-(

EDIT:
Dadurch, dass ich deinen Code nicht verstehe und auch den Aufruf nicht richtig, bzw. nicht zuordnen kann wo die TextBox für den User wäre etc. habe ich Angst dass wir aneinander vorbei reden und du deine Zeit umsonst aufbringst.
Daher nur noch mal kurz zur Sicherheit:

Die PinEingabe soll ein eigens Fenster werden mit einer TextBox, die entweder beim Start des Forms oder beim drücken des SPeicherbutton in einigem x beliebigen Form angezeigt wird.
Sollte die Pinabfrage erfolgreich sein, wird die Funktion des Forms ausgeführt und wenn nicht unterbrochen.

Gruß


Ralf Jansen - So 11.05.14 21:01

Zitat:
Der Bereich wo Load und SaveObject ist geht der dann nur für das Anlegen eines Mitarbeiters aus den vorherigen Treats?


So hatte ich mir das gedacht ja. Aber das Konzept funktioniert natürlich für jedes Klasse die zu deinem Model gehört.
Es müsste dann eben eine Klasse von Controller<T> abgeleitet werden für die dann zu nutzende andere Klasse. Die Klassen sehen dann alle im Prinzip genauso aus wie EmployeeController.

Aber wenn ich so weit am Ziel vorbeigeschossen habe lass es uns anders versuchen.
Gib uns einen tieferen Blick auf das was du da hast und wir hier im Forum versuchen das dann in die richtige Richtung zu lenken um auch die PIN Abfrage da unter zu bringen.
Wenn wir deinen Code sehen fällt es uns leichter das passende Niveau zu treffen und nicht irgendwelche Sachen auch mit unterzubringen die dir vielleicht jetzt gerade nicht helfen.


Talemantros - So 11.05.14 21:27

Hi,
also eigentlich, dachte ich ich könnte einfach ein Form mit einer TextBox (UserControl) machen, die ich immer nach belieben entweder beim Öffnen eines Forms oder beim Drücken eines Buttons anzeigen lassen kann unabhängig von welchem Modul aus. Mitarbeiter oder später auch andere.

Und wenn die Abfrage des Pins mit der Datenbank erfolgreich ist wird das Form geöffnet oder die Funktion des Buttons ausgeführt.
Dachte dass könnte man ohne große Probleme mit der Kommunikation zwischen 2 Forms von TH69 so machen.
Daher basierte darauf auch die Grundfrage.

Viel Code in meinem Projekt außer dem hier im Forum mit Employee etc habe ich ja noch nicht.

Was genau brauchst du ? Das ganze Projekt?

Müsste gucken ob ich das hochladen kann da ich das KryptonToolkit drin habe für die Optik

Gruß


Talemantros - Mo 12.05.14 07:16

Guten Morgen,
habe nun die ganze Nacht kaum geschlafen, weil mich das alles belastet.
Wäre nicht folgendes Vorgehen vielleicht schon ausreichend?!

1.) Eine Modellklasse bauen für die Pinabfrage, die sich selber prüft, ob Daten eingegeben wurden sind.
2.) Diese mit Databinding an ein UserControl binden
3.) Die TextBox beim Verlassen eine Methode mit SQL Abfrage aufrufen lassen, ob er es darf oder nicht und den Namen des Mitarbeiters einlesen
4.) True oder False zurück liefern lassen
5.) Dieses True oder False und den Namen müsste ich dann irgendwie in die Form bekommen, die die PiNAbfrage aufgerufen hat (EVENT!?)

Oder ist das wieder völlig daneben?

EDIT: Sorry bin auf Antworten statt auf Editieren. Wenn gewollt bitte zusammenlegen.

EDIT2:
Also mein versuch ging jetzt erstmal dahin das UserControl mit der Databinding der Modelklasse zu machen und ein Event zu feuern und zu behandeln. Aber leider funktioniert schon das nicht.


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:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Recycle.Logics
{
    public class AuthorizationEventArgs : EventArgs
    {
        public string PIN { get; set; }

        public event EventHandler<AuthorizationEventArgs> AuthorizationNeeded;

        private void OnAuthorizationNeeded(AuthorizationEventArgs eventArgs)
        {
            EventHandler<AuthorizationEventArgs> ev = AuthorizationNeeded;
            if (ev != null)
                ev(this, eventArgs);
        }  

        public bool IsValid
        {
            get
            {
                return !string.IsNullOrWhiteSpace(PIN);
            }
        }
    }
}


Im UserControl bisher

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:
33:
34:
35:
36:
37:
38:
39:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Recycle.Logics;

namespace Recycle.Controls
{
    public partial class AuthorizationUserControl : UserControl
    {
        public AuthorizationUserControl()
        {
            InitializeComponent();
        }

        private void txtSecId_Leave(object sender, EventArgs e)
        {

        }

        private void AuthorizationUserControl_Load(object sender, EventArgs e)
        {
            bsPin.DataSource = new AuthorizationEventArgs();

            AuthorizationEventArgs auth = new AuthorizationEventArgs();
            auth.AuthorizationNeeded += AuthorizationNeeded;
        }

        private void AuthorizationNeeded(object sender, AuthorizationEventArgs e)
        {
            MessageBox.Show("Test");
        }
    }
}



Hatte eigentlich gehofft, dass beim Eingeben einer Pin die TestMessageBox erscheint. An der Stelle würde ich dann die PinAbfrage auf der Datenbank machen?!

EDIT3: Ich habe jetzt leider einen Termin, danach versuche ich es mal mit der INotifyPropertyChanged, die wir in der ConnectionOptions gemacht haben.
Gruß


Ralf Jansen - Mo 12.05.14 09:57

In deinem Code kann ich leider die Stelle nicht sehen wo der Event auch ausgeführt wird. Es ist auch eher ungewöhnlich den Event von den EventArgs aus zu verwalten. Wer sollte wie OnAuthorizationNeeded aufrufen? Die ist ja nebenbei auch noch privat. In der Form ist das ein Sackgasse. Die Klasse wo der Code zum speichern/laden drin steckt und die du von der Form aus aufrufen willst sollte Herr des Events sein und denn auch aufrufen können.

Zitat:
Oder ist das wieder völlig daneben?


Weiß nicht. Ich habs nicht verstanden ;) Für ein einzelnes Element (die PIN) würde ich aber eher nicht eine eigene Model Klasse basteln. Die Eingabe kommt mir jetzt auch nicht so komplex vor das eine solche Klasse zum Databinding besonders hilfreich wäre. Mach dir einfach ein Form auf der du die PIN eingeben kannst. Dann gibt der Form eine Property über die man später von wo anders die PIN aus der Form auslesen kann oder wenn du die PIN gleich von der PIN Eingabe Form aus prüfen willst dann gib der Form eine boolsche Property die zurückgibt on die PIN gültig ist oder nicht.


Talemantros - Di 13.05.14 12:06

Hi,
also so wollte ich es eigentlich zu Beginn machen, dachte aber es sei zu "unprofessionell" mit den Properties in der Form.
Nun gut ich habe mich jetzt mal damit auseinder gesetzt und auch ein ganzes Stück weiter gekommen. Leider fehlt noch eine Kleinigkeit.
(Try und Catch habe ich auch noch nicht flächendeckend erfasst, wollte erst mal dass es läuft)

In der HauptForm in der Menüleiste ruft Beispielsweise jemand Mitarbeiterverwaltung auf, welche bei mir die ID des Rechtes 5 hat.
Diese übergebe ich an die Zertifizierungsform und prüfe dort anhand eines SQL Strings, ob die eingegebene SecurityID dieses Recht ausführen darf.
Entsprechend werden die Properties gefüllt, welche ich dann in der Hauptform zur Verfügung hätte.

Nun möchte ich diesen ausgelesenen Mitarbeitername an die aufzurufende Form übergeben (aus Lernzwecken nicht mit einer Property) damit, wenn dieser in der neuen Form etwas tut sein Name in der Historie steht. Die Mitarbeiterverwaltung ist ein Form mit einem UserControl drauf welches den Speicherbutton hat. Dort soll der Name nutzbar sein.


Code für den Aufruf aus der MainForm

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:
33:
34:
        private void tsmMitarbeiterAnlegen_Click(object sender, EventArgs e)
        {
            //Prüft, ob lt. Optionen eine Zertifizierung vorgenommen werden soll
            if (MySqlSettings.GetOptionString(connStr, 2) == "Ja")
            {
                //ruf Zertifizeriungsform auf und übergibt das zu prüfende Recht an den Konstruktor
                Zertifizierung myAuth = new Zertifizierung(5);
                myAuth.ShowDialog();

                //Wenn das Fenster geschlossen wird prüfen, ob Auth erfolgreich
                if (myAuth.DialogResult == DialogResult.OK)
                {
                    //Wenn erfolgreich, dann Mitarbeiterverwaltung aufrufen
                    if (myAuth.Authorized == true)
                    {
                        Mitarbeiterverwaltung myEmployee = new Mitarbeiterverwaltung();
                        myEmployee.Show();

                        //Event feiern, welches im UserControl der  Mitarbeiterverwaltung behandelt werden soll
                        OnSendAuthentication(new AuthenticationEventArgs(myAuth.Employee));
                    }
                    else
                    {
                        MsgAusgabe.ShowError("Sie sind nicht berechtigt dieses Modul zu nutzen!");
                    }
                }
            }
                //Falls keine Zertifizierung stattfinden soll einfach anzeigen
            else
            {
                Mitarbeiterverwaltung myEmployee = new Mitarbeiterverwaltung();
                myEmployee.Show();
            }
        }


Code zum feuern des Events, welches in dem UserControl der Mitarbeiterverwaltung behandelt werden soll

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
        #region EventAuthentication
        public class AuthenticationEventArgs : EventArgs
        {
            public AuthenticationEventArgs(string text)
            {
                Text = text;
            }

            public string Text { get; set; }
        }

        public event EventHandler<AuthenticationEventArgs> SendAuthentication;

        protected virtual void OnSendAuthentication(AuthenticationEventArgs e)
        {
            EventHandler<AuthenticationEventArgs> ev = SendAuthentication;
            if (ev != null)
                ev(this, e);
        }
        #endregion


Code der Form Zertifizierung zum füllen der Propertys


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:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
namespace Recycle.Dialogs
{
    public partial class Zertifizierung : KryptonForm
    {
        private static long modul;

        public string Employee { get; set; }
        public bool Authorized { get; set; }

        public Zertifizierung(Int64 id)
        {
            InitializeComponent();
            modul = id;
            Authorized = false;
            Employee = null;
        }

        private void txtSecId_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
            {
                btnWeiter.PerformClick();
            }
        }

        private void btnWeiter_Click(object sender, EventArgs e)
        {
            if (txtSecId.Text != string.Empty)
            {
                Employee = EmployeeMethods.GetEmployeeNameFromSecId(Convert.ToInt64(txtSecId.Text));

                if (AuthorizationMethods.GetAuthorization(Convert.ToInt64(txtSecId.Text), modul) == 1)
                {
                    Authorized = true;                
                }
                else
                {
                    Authorized = false;                
                }
            }
            else
            {
                MsgAusgabe.ShowError("Bitte erfassen Sie eine SecurityID!");
            }
        }
    }
}


Prüfung, ob der Mitarbeiter dies darf


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:
    public class AuthorizationMethods
    {
        private static string connStr = DbInfo.GetMySqlConnStr();
        private static string strSql = string.Empty;

        public static int GetAuthorization(long secId, long modul)
        {
            int rueckgabe = 0;
            strSql = "Select count(b.berechtigungmodulid) from mitarbeiter m inner join berechtigungmodulgruppe b " +
                     "on m.berechtigunggruppeid = b.berechtigunggruppeid where m.secid = ?secid and b.berechtigungmodulid = ?berechtigungmodulid";
            using (MySqlConnection conn = new MySqlConnection (connStr))
            {
                using (MySqlCommand cmd = new MySqlCommand (strSql, conn))
                {
                    conn.Open();

                    cmd.Parameters.AddWithValue("?secid", secId);
                    cmd.Parameters.AddWithValue("?berechtigungmodulid", modul);

                    rueckgabe = Convert.ToInt16(cmd.ExecuteScalar());

                    conn.Close();
                }
            }

            return rueckgabe;
        }
    }


Und hier komme ich jetzt nicht weiter, da ich nicht weiß wie ich mein UserControl umbauen müsste, damit es das Event behandelt!?
Ich hatte überlegt, ob ich dann in dem UserControl eine Referenz auf die MainForm setzen muss?!
Wenn der Gedanke ok ist, würde ich leider nicht wissen wie ich die beim Aufruf der Mitarbeiterverwaltung nicht an das Form sondern die UserControl weiter gebe und wie ich es dann abonniere.


Gruß


Ralf Jansen - Di 13.05.14 12:23

Zitat:
Und hier komme ich jetzt nicht weiter, da ich nicht weiß wie ich mein UserControl umbauen müsste, damit es das Event behandelt!?


Was sollte den dieses Event tun? Ich vermute mal so wie es jetzt gelöst ist der Event unnötig geworden ist. Da du die Authorizierung vorher gemacht hast solltest du eigentlich alles was dort benötigt wird einfach in das UserControl reinschieben können (per Properties, Methoden oder wie auch immer). Also übergibt den Employee einfach an die Mitarbeiterverwaltung Form die das dann wiederum an das UserControl weiterreicht. Ich persönlich würde vermutlich die Show Methode überladen weil es sich dann einfach so sinnig im Code liest was da passiert myEmployee.Show(myAuth.Employee);.


Talemantros - Di 13.05.14 13:38

Ok, dann würde ich das so lösen mit der ShowMethode und der Überladung.

Nur mal so als Lernzweck.
Ich habe bereits verstanden, wie ich in der MainForm ein Event behandel, welches in der SubForm ausgelöst wurde.
Aus meiner Sicht wäre es bei mir andersrum?

Wie würde es den grundsätzlich funktionieren, wenn das MainForm ein Event auslöst und die Subform es behandeln soll!?

Edit: also die ShowMethode habe ich irgendwie nicht richtig überladen bekommen, da ich den Fehler nicht verstanden habe :-(
Habe es nun so gemacht!


C#-Quelltext
1:
2:
Mitarbeiterverwaltung myEmployee = new Mitarbeiterverwaltung(myAuth.Employee);
myEmployee.Show();

und habe den Konstruktor überladen!

Danke


Th69 - Di 13.05.14 14:00

Hallo,

vom Prinzip her natürlich genauso ;-)

Ich frage mich nur, ob es bei dir wirklich so gedacht ist, daß du das Mitarbeiterverwaltung-Form(ular) nicht-modal (also per Show, anstatt per ShowDialog) aufrufen möchtest? Denn dann kann man ja verschiedenene dieser Forms gleichzeitig auf haben (und sogar mit unterschiedlicher Authentifizierung!) - denn das Main-Form ist ja dann weiterhin bedienbar?

Genau darauf gehe ich ja auch in meinem Artikel ein, d.h. Ereignisse (events) benötigt man entweder bei nicht-modalen Forms oder bei einer modalen-Form, welche währenddessen Infos an die aufrufende (oder irgendeine andere) Form weitergibt. Ansonsten kann man immer direkt vor dem Aufruf einer modalen Form bzw. nach dem Schließen die Werte direkt per Eigenschaften und/oder Methoden übergeben.


Talemantros - Di 13.05.14 19:26

Hi,
danke

Mit den Events muss ich mir dann noch mal anschauen. War der Meinung habe nur die in die eine "Richtung" gesehen.

Gruß


Ralf Jansen - Di 13.05.14 19:31

Zitat:
Wie würde es den grundsätzlich funktionieren, wenn das MainForm ein Event auslöst und die Subform es behandeln soll!?


Das würde man normalerweise nicht tun. Insofern stellt sich die Frage nicht wirklich ;)

Der Event ist in diesem Context normalerweise für Rückmeldungen gedacht. Zum Beispiel die Subform muß was an die Mainform melden. Um eine sauber Abhängigkeitsbeziehung zu haben, die in Zukunft nicht zu Problemen führt, kennt einer den anderen aber nicht umgekehrt. Beziehung möglichst immer nur oneway. Hier wird die Mainform die Subform kennen aber eben nicht umgekehrt.
Wenn aber trotzdem die Subform was an die Mainform melden soll hilft ein Event da der von der Mainform verdrahtet wird. Die Mainform registriert also den Event der Subform kann damit Dinge von der Subform empfangen aber die Subform kennt weiterhin nicht die Mainform und die Klassenbeziehungen zwischen den beiden bleiben sauber.
Im umgekehrten Fall für die Kommunikation von der Mainform zur Subform bedarf es keines Events. Die Mainform kennt ja die Subform und kann Dinge die an die Subform übergeben werden einfach dahinschieben.


Talemantros - Mi 14.05.14 07:33

Danke und schönen Tag noch

Gruß