Laden...

Länge definierten String als benutzerdefinierten Datentyp für MaskedTextBox erstellen

Erstellt von ChrisProg vor 7 Jahren Letzter Beitrag vor 7 Jahren 2.801 Views
ChrisProg Themenstarter:in
174 Beiträge seit 2009
vor 7 Jahren
Länge definierten String als benutzerdefinierten Datentyp für MaskedTextBox erstellen

Hallo zusammen,

die Frage ist hier schon mehrfach aufgetaucht, aber doch nie beantwortet worden ...

Ausgangslage:

ein MaskedTextBox mit folgenden Vorgaben:

Mask = "CCC CC CCC CCCCC";
CutCopyMaskFormat = MaskFormat.ExcludePromptAndLiterals;
TextMaskFormat = MaskFormat.ExcludePromptAndLiterals;
ValidatingType = typeof(string);
PromptChar = ' ';

den ValidatingType "typeof(string)" habe ich hier genommen, damit die Methode TypeValidationCompleted überhaupt durchlaufen wird.

Nur: bei diesem ValidatingType ist e.IsValidInput immer true, ganz gleich ob es jetzt 16 Stellen oder nur drei sind, deshalb mus ich wohl lt. MS einen benutzedefinierten Datentyp erstellen.

Ich finde einige Beispiel wie ich verschieden Felder zu einer Gruppe zusammenfügen kann, aber nirgendwo einen Hinweis, wie ich es schaffe, einen in Länge definierten String (alles andere prüft die Mask ja schon ...) zu erstellen - wenn das überhaupt geht.

Das wäre mit Sicherheit ja auch für die Überprüfung einer IBAN hilfreich ...

Für Anregungen und Hilfe dankbar...

Christian

Hinweis von Coffeebean vor 7 Jahren

Niemand kann mit "benutzerdefinierter Datentyp" als Titel etwas anfangen. Ich habe dein Problem nicht ganz verstanden, habe aber mal den Titel nach besten Wissen und Gewissen geändert. Bitte änder ihn nochmal, wenn du ihn korrigieren willst.

[Hinweis] Wie poste ich richtig?

3.003 Beiträge seit 2006
vor 7 Jahren

Eine Parse-Methode hast du natürlich bereitgestellt?

If you want to use your own custom data types with ValidatingType, you must implement a static Parse method that takes a string as a parameter. This method must be implemented with one or both of the following signatures:
public static Object Parse(string)
public static Object Parse(string, IFormatProvider)

MaskedTextBox.ValidatingType-Eigenschaft

LaTino
(übrigens definiert IBAN eine Prüfsumme, also würde ich nicht direkt versuchen, das über die Länge zu verifizieren)

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

ChrisProg Themenstarter:in
174 Beiträge seit 2009
vor 7 Jahren

Ganz ehrlich: nein !

An diesem Punkt verstehe ich es nicht mehr ...

Ich habe den entsprechenden Passus zwar auch gelesen, aber ich verstehe nicht, was oder warum ich einen String "parsen" soll/muß - es bleibt doch ein String ?(

Er hat doch nur ein paar Leerstellen zur Formatierung hinzubekommen, aber es bleibt ein String.

Zu IBAN: natürlich weiß ich, das eine IBAN über die Prüfsumme definiert wird, aber ich weiß auch das sie in D immer 23 Stellen und international min 16 Stellen (Belgien) und max. 30 Stellen haben kann, das kann man ja auch schon vorher abprüfen.

MfG Christian

3.003 Beiträge seit 2006
vor 7 Jahren

Dein Verständnisproblem rührt daher, dass du die IBAN als string begreifen möchtest.

Das ist sie nicht, weil sie eben ein paar Eigenschaften zusätzlich zu string hat - feste Länge, Checksumme, was auch immer.

Also schreib dir eine Klasse, die intern mit einem String arbeitet. In diesem internen String speicherst du die IBAN. Stell dieser Klasse eine statische Methoden Parse(string) bereit. In dieser Parse-Methode wirfst du eine Ausnahme, wenn der String eben keine IBAN ist - überprüf dort also Länge, Checksumme und was dir sonst noch einfällt.

Übergib den Typ deiner neuen Klasse dann dem ValidatingType.

LaTino
"Habe ich nicht verstanden und deshalb ignoriert" - ja, nun, so wird das nicht 😉

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

16.828 Beiträge seit 2008
vor 7 Jahren

Du kannst doch Problemlos den Event TypeValidationCompleted dazu nehmen die Validierung durchzuführen.
Steht doch sogar im Beispiel, das Latino verlinkt hat.

Dein typeof(string) wird alleine daran scheitern, dass string keine statische Methode "Parse" hat; DateTime - wie im Beispiel - schon.
Bleibt Dir also nur das Eingreifen im Event oder eben ne eigene Klasse zu schreiben, die Parse implementiert.

public class IBanXYZ
{

   public static IBanXYZ Parse(String input)
   {
     // validierung ungültig:
     return null;

     // validierung gültig:
     return IBanXYZ(...);
    }

}

Und dann eben

alidatingType = typeof(IBanXYZ);
ChrisProg Themenstarter:in
174 Beiträge seit 2009
vor 7 Jahren

So dank der beiden Ausführungen, habe ich das nun auch verstanden 🙂

Das mit der IBAN war eigentlich nur als Beispiel gedacht, in diesem (aktuellen) Fall handelt es um eine Ohrmarke (nur so am Rande...)

@LaTino:

"Habe ich nicht verstanden und deshalb ignoriert" - ja, nun, so wird das nicht 😉 genau deshalb habe ich ja dieses Thema eröffnet, um es zu verstehen - dafür ist doch das Forum auch da, oder nicht 🤔

MfG Christian

3.003 Beiträge seit 2006
vor 7 Jahren

@LaTino:

"Habe ich nicht verstanden und deshalb ignoriert" - ja, nun, so wird das nicht 😉
genau deshalb habe ich ja dieses Thema eröffnet, um es zu verstehen - dafür ist doch das Forum auch da, oder nicht 👶

Du nimmst mich da ernster, als ich verdiene - deinem ursprünglichen Posting war nicht anzusehen, dass der Abschnitt mit der Parse()-Methode dein Verständnisproblem war, daher mein halb amüsierter Satz 😃.

Die MaskedTextBox nimmt ihren ValidatingType, und geht einfach davon aus, dass dieser Typ eine statische Methode Parse() hat, die sie dann aufruft. Das ist das Schlechte an assumptions - wenn sie funktionieren, wirken sie ein bisschen wie Magie, und wenn man sie nicht kennt und sie nicht funktionieren, ist man erst einmal ratlos. Insofern ist die MaskedTextBox einfach ein bisschen schlecht designt. Jetzt kennst du die getroffene Prämisse mit dem Parse() ja, dann löst sich das Problem in Wohlgefallen auf.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

ChrisProg Themenstarter:in
174 Beiträge seit 2009
vor 7 Jahren

Hallo nochmals,

nachdem ich mir nun diese Klasse geschrieben habe

public class ohrmarke
    {
        string x_ohrmarke;

        public static ohrmarke Parse(string xx_input)
        {
            if (xx_input.Length < 13)
            {
                // validierung ungültig
                return null;
            }
            else
            {
                return new ohrmarke();
            }

        }
    }

und diesen Typ auch bei der MaskedTextBox als

ValidatinType = typeof(ohrmarke)

hinterlegt habe, hätte ich jetzt eigentlich erwartet, das beim return null das Ereignis TypeValidationCompleted nicht mehr ausgelöst wird - MaskedTextBox.TypeValidationCompleted - wird es aber dennoch und auch e.IsValidInput ist true ...

Was habe ich da denn jetzt wieder nicht verstanden ???

MfG Christian

3.003 Beiträge seit 2006
vor 7 Jahren

Ohne es ausprobiert zu haben gehe ich davon aus, dass e.IsValidInput true ist, wenn die Parse-Methode etwas zurückgibt. null ist auch etwas, nur ziemlich wenig davon 😉.

Versuch's mal mit dem Werfen einer Ausnahme.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

ChrisProg Themenstarter:in
174 Beiträge seit 2009
vor 7 Jahren

Hallo LaTino,

funktioniert leider auch nicht, wenn ich

 throw new ArgumentException();

statt

return null;

schreibe, erhalte ich einen Fehler "ArgumentException wurde nicht vom Benutzercode behandelt. Der Wert liegt außerhalb des erwarteten Bereichs." - was ich dahingehend interpretiere, das die MaskedTextBox eine Exception nicht verarbeiten kann , richtig ?

MfG Christian

3.003 Beiträge seit 2006
vor 7 Jahren

Bei solchen Fragen empfiehlt sich, wenn die Dokumentation nichts weiter hergibt, ein Blick in die ReferenceSource:


/// <devdoc>
        ///     Type of the object to be used to parse the text when the user leaves the control. 
        ///     A ValidatingType object must implement a method with one fo the following signature:
        ///         public static Object Parse(string)
        ///         public static Object Parse(string, IFormatProvider)
        ///     See DateTime.Parse(...) for an example.
        /// </devdoc>

...dann schauen wir doch ma, wie empfohlen, in die DateTime.Parse-Methode:


 DateTimeResult result = new DateTimeResult();       // The buffer to store the parsing result.
            result.Init();
            if (TryParseExact(s, format, dtfi, style, ref result)) {
                return result.parsedDate;
            }
            else {
                throw GetDateTimeParseException(ref result);
            }

...und dann nochmal in die Methode GetDateTimeParseException:


private static Exception GetDateTimeParseException(ref DateTimeResult result) {
            switch (result.failure) {
                case ParseFailureKind.ArgumentNull:
                    return new ArgumentNullException(result.failureArgumentName, Environment.GetResourceString(result.failureMessageID));
                case ParseFailureKind.Format:
                    return new FormatException(Environment.GetResourceString(result.failureMessageID));
                case ParseFailureKind.FormatWithParameter:
                    return new FormatException(Environment.GetResourceString(result.failureMessageID, result.failureMessageFormatArgument));
                case ParseFailureKind.FormatBadDateTimeCalendar:
                    return new FormatException(Environment.GetResourceString(result.failureMessageID, result.calendar));
                default:
                    Contract.Assert(false, "Unkown DateTimeParseFailure: " + result);
                    return null;
 
            }

...was die Vermutung nahelegt, dass wir eine FormatException werfen sollten.

LaTino
PS: referencesource: http://referencesource.microsoft.com/

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

3.003 Beiträge seit 2006
vor 7 Jahren

Doppelpost, weil das in einem Edit nicht untergehen soll:

  • die Signatur der Parse-Methode sollte object Parse(string value) sein
  • der eingehende String in der Methode wird der komplette String inkl. Platzhalter der MaskedTextBox sein
  • FormatException() funktioniert bei mir wunderbar, wobei ich andere jetzt gar nicht ausprobiert habe

//code-behind des Forms
 public Form1()
{
    InitializeComponent();
    maskedTextBox1.ValidatingType = typeof(IbanString);
}
private void maskedTextBox1_TypeValidationCompleted(object sender, TypeValidationEventArgs e)
{
    var result = e.ReturnValue as IbanString;
    button1.Text = result.Value;
}

//Klasse IbanString
public class IbanString
{
    private string _value;

    public static object Parse(string value)
    {
        if(value.Length != 20) //simpelster vergleich, die maskedtextbox hat 16 zeichen+4 platzhalter
            throw new FormatException("kdhji");
        return new IbanString(value.Replace(" ", ""));
    }

    public IbanString(string value)
    {
        _value = value;
    }

    public string Value => _value.Substring(0,12);
}

LaTino
EDIT: bisschen formatierung

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

ChrisProg Themenstarter:in
174 Beiträge seit 2009
vor 7 Jahren

wobei hier aber ja als default null zurückgegeben wird (und schon sind wir wieder am Anfang 🤔 )

Aber: irgendetwas übersehe ich hier (oder verstehe es nicht ...)

Ich habe mal deinen Quellcode übernommen und bekomme auch hier bei

return new IbanString(value.Replace(" ", ""));

eine Fehlermeldung > Fehlermeldung:

FormatException wurde nicht von Benutzercode behandelt

Liegt es vielleciht daran, das ich

Mask = "CCC CC CCC CCCCC";
CutCopyMaskFormat = MaskFormat.ExcludePromptAndLiterals;
TextMaskFormat = MaskFormat.ExcludePromptAndLiterals;

vorbelegt habe ???

MfG Christian

3.003 Beiträge seit 2006
vor 7 Jahren

Nein, es liegt daran, dass dein Visual Studio bei in deinem Code unbehandelten Ausnahmen anhält. Das ist eine debugger-Einstellung. Ich weiss auch nicht, wo du eine Rückgabe von null siehst oO.

LaTino
EDIT: um's nochmal klar zu sagen, der Code oben von mir ist direkt aus einem funktionierenden Beispiel-WinForms-Projekt. "Geht nicht" lass ich nicht gelten.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

ChrisProg Themenstarter:in
174 Beiträge seit 2009
vor 7 Jahren

na hier in der Prozedur (im default-Zweig):

private static Exception GetDateTimeParseException(ref DateTimeResult result) {
            switch (result.failure) {
                case ParseFailureKind.ArgumentNull:
                    return new ArgumentNullException(result.failureArgumentName, Environment.GetResourceString(result.failureMessageID));
                case ParseFailureKind.Format:
                    return new FormatException(Environment.GetResourceString(result.failureMessageID));
                case ParseFailureKind.FormatWithParameter:
                    return new FormatException(Environment.GetResourceString(result.failureMessageID, result.failureMessageFormatArgument));
                case ParseFailureKind.FormatBadDateTimeCalendar:
                    return new FormatException(Environment.GetResourceString(result.failureMessageID, result.calendar));
                default:
                    Contract.Assert(false, "Unkown DateTimeParseFailure: " + result);
                    return null;

            }

Du hast völlig Recht, es funktioniert auch im Debugger, wenn ich die entsprechende Ausnahme deaktiviere...

Aber kann man das nicht hinbekommen trotz aktivierter Ausnahme ?

Denn die Frage die jetzt auftaucht, ist doch: ist das "sauberer Quellcode" oder etwas was man so eigentlich nicht machen sollte, denn damit habe ich doch jede Möglichkeit beendet, diesen Fehler (egal wo im Quellcode) mittels Debugger zu finden ...

Gedanklich denke ich an einen Befehl der nur innerhalb der Klasse wirksam ist ...

Unter FoxPro hatten wir diese Möglichkeit mit Hilfe von Set-Systembefehlen.

MfG Christian

16.828 Beiträge seit 2008
vor 7 Jahren

Aktivierte Ausnahme sind dazu da, dass sie trotz Behandlung / Unterdrückung im Debugger auftauchen.
Die Anwendung stürzt aber deswegen nicht ab. In dem Fall ist das by design.

3.003 Beiträge seit 2006
vor 7 Jahren

Ob das jetzt sauber ist, oder nicht, darüber könnte man prima streiten. Wenn du mich fragst: ich halte die Art und Weise, wie MaskedTextbox ihren Inhalt "validiert" (eigentlich: interpretiert/parst), für groben Unfug. Andere sehen vielleicht kein Problem damit, dass eine Exception stillschweigend erwartet UND geschluckt wird. Die Tatsache, dass der Debugger in seiner Standardeinstellung darüber stolpert, würde ich als Argument für meine Sicht der Dinge herhalten lassen 😉.

Das ändert aber nichts an der Tatsache, dass die Vorgehensweise für dieses Control nunmal so ist, wie sie ist. Wenn's arg stört, kann man sich immer noch ein eigenes Control von Textbox ableiten.

Im Normalfall ist man mit den drei Schritten gut bedient:

  • Klasse schreiben mit statische Parse-Methode schreiben
  • MaskedTextBox.ValidationType einstellen
  • TypeValidationCompleted behandeln

Manche Dinge nimmt man besser einfach hin, wenn der Aufwand, sie zu korrigieren, in keinem Verhältnis zum Nutzen steht. Und dem Benutzer wird herzlich egal sein, wie deine TextBox da nun genau funktioniert.

LaTino
PS: der default-Zweig dürfte, wenn ich den Quellcode mal so durchstöbere, in allen üblichen Fällen nicht erreicht werden. Ist nur als FallBack für Unvorhergesehenes gedacht.

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

ChrisProg Themenstarter:in
174 Beiträge seit 2009
vor 7 Jahren

Danke für schnelle Hilfe und das Brainstorming ... 👍 👍

Christian