Laden...

Ansicht-Designer soll Code nicht ausführen

Erstellt von Tom vor 14 Jahren Letzter Beitrag vor 14 Jahren 10.104 Views
T
Tom Themenstarter:in
433 Beiträge seit 2006
vor 14 Jahren
Ansicht-Designer soll Code nicht ausführen

Ich habe das Problem das ich beim Ansicht-Designer nicht möchte das ein gewisser Teil meines Codes ausgeführt wird. Es handelt sich hierbei um eine externe Komponente die ich bei Programmstart initialisieren muss.
Den Aufruf den ich nicht ausführen möchte, steckt derzeit im OnLoad Ereignis eines UserControls.

Gibt es eine Möglichkeit dem Visual Studio Designer mitzuteilen diesen Code nicht auszuführen?

Gruß,
Tom

S
248 Beiträge seit 2008
vor 14 Jahren

Hallo Tom,

vielleicht hilft dir dies weiter.

Component.DesignMode-Eigenschaft

Spooky

T
Tom Themenstarter:in
433 Beiträge seit 2006
vor 14 Jahren

Danke Spook, das hilft mir schon mal sehr weiter.

Tom

5.299 Beiträge seit 2008
vor 14 Jahren

Jo, das kannstemal ausprobieren. Aber grade auf UserControls habich schlechte Erfahrungen gemacht mit Component.DesignMode(), wenn das Ucl in ein anneres ucl eingebaut wird.
Da habich iwo diesen Hack aufgeschnappt:


using System;

namespace System.Windows.Forms {
   /// <summary>System.ComponentModel.Component.DesignMode ist buggy</summary>
   public class MyDesignMode {

      private bool _DesignMode = Application.StartupPath.EndsWith("\\Common7\\IDE");
      private static MyDesignMode Singleton = new MyDesignMode();
      private MyDesignMode() { Application.ApplicationExit += ApplicationExit; }
      private void ApplicationExit(object Sender, EventArgs e) { _DesignMode = true; }

      public static bool True { get { return Singleton._DesignMode; } }
      public static bool False { get { return !Singleton._DesignMode; } }
   }
}


Aufzurufen mit:


if(MyDesignMode.True())machsgut();
//...
if(MyDesignMode.False())machsbesser();

je wie mans braucht.

Der frühe Apfel fängt den Wurm.

F
10.010 Beiträge seit 2004
vor 14 Jahren

 public static bool True { get { return Singleton._DesignMode; } }
      public static bool False { get { return !Singleton._DesignMode; } }

Ist nicht dein Ernst?

2.891 Beiträge seit 2004
vor 14 Jahren

Hallo zusammen,

das Problematische mit DesignMode ist, dass es im Konstruktor des UserControls noch nicht verfügbar ist. Eine Lösung dafür gibt's unter [gelöst] Ausführung von Code im Usercontrol im Designemodus verhindern?.

Gruß,
dN!3L

T
Tom Themenstarter:in
433 Beiträge seit 2006
vor 14 Jahren

Da steckt der Teufel ja mal wieder im Detail.

Laut dem anderen Thread gibt es auch die Möglichkeit System.ComponentModel.LicenseManager.UsageMode abzufragen.

Mit den ganzen Stichwörtern hab ich jetzt mal ein bisschen googeln können und bin zu folgendem Schluss gekommen:*Component.DesignMode kann nicht im Konstruktor abgefragt werden, da es wohl von den Parent Objekt übertragen wird. *Component.DesignMode liefert in verschachteteln UserControls (immer?) false zurück *der Rest der Welt nutzt wohl den StartupPath Hack, oder geht über ProcessName *LicenseManager.UsageMode kommt nur selten vor, und es gibt vereinzelte Meinungen das es zu Problemen ähnlich wie bei Component.DesignMode kommen kann

Ich implementier jetzt mal eine Klasse für unser Projekt, das auf LicenseManager.UsageMode geht. Sollte das zu Problemen führen werde ich mir wohl komplett was anderes ausdenken, da ich vom Startup/ProcessName Hack nicht so überzeugt bin. Pfade und Versionen können sich ja irgendwann mal ändern.

Vielen Dank nochmal für die vielen Ideen.

Tom

5.299 Beiträge seit 2008
vor 14 Jahren
  
public static bool True { get { return Singleton._DesignMode; } }  
public static bool False { get { return !Singleton._DesignMode; } }  
  

Ist nicht dein Ernst?

Jo, sieht ziemlich schräg aus. Aber guck dir an, wies aufgerufen wird


if(MyDesignMode.True())machsgut();
//...
if(MyDesignMode.False())machsbesser();

das ist das kürzeste und leserlichste, wassich zustande gebracht hab.
Man tippt "myd" und ab da hat man Intellisense.

Edit (Antwort auf pdelvo weiter unten):
stimmt, also


if(MyDesignMode.True)machsgut();
//...
if(MyDesignMode.False)machsbesser();

...wassich natürlich noch ein fitzelchen besser finde 🙂

Der frühe Apfel fängt den Wurm.

1.346 Beiträge seit 2008
vor 14 Jahren

Außerdem sind 'True' und 'False Properties und keine Methoden.

Gruß pdelvo

5.299 Beiträge seit 2008
vor 14 Jahren

LicenseManager.UsageMode kommt nur selten vor, und es gibt vereinzelte Meinungen das es zu Problemen ähnlich wie bei Component.DesignMode kommen kann

Ja stimmt, wennichmichrecht erinnere, machtedas denselben Käse - aber gut, wenndedas nochmal verifizierst.

Der frühe Apfel fängt den Wurm.

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo ErfinderDesRades,

äh, eine Property, die True heißt, aber true oder false sein kann, ist doch, sorry, vollkommener Mist. Die Property muss sinnvollerweise DesignMode heißen. Von mir aus können die Properties auch On und Off heißen, aber doch nicht True und False.

herbivore

5.742 Beiträge seit 2007
vor 14 Jahren

äh, eine Property, die True heißt, aber true oder false sein kann, ist doch, sorry, vollkommener Mist.

Ich freue mich schon auf:


public sealed class MyControl : Control
{
    private bool isNotInDesignMode;

    public MyControl()
    {
         if (MyDesignMode.True == false)
              this.isNotInDesignMode = true;
         else
              this.isNotInDesignMode = false;   
    }
}

*SCNR* 😁

5.299 Beiträge seit 2008
vor 14 Jahren

Gibt es irgendeine Möglichkeit, diesen "Mist" falsch zu verstehen?


if(MyDesignMode.True)machsgut();

Ich finde nicht, und damit machtes einen guten Job.

Ich brauche eine globale Information, ob die Solution im DesignMode läuft, wahr oder falsch, und wollte das möglichst kurz haben.
Ich finde keine kürzere Art das zu formulieren, weil c# unbedingt einen ersten Bezeichner, und dann etwas mit '.' angehängtes erfordert.
In VB könnte man schreiben


If MyDesignMode Then

EndIf

Auch das explizite Benennen von True / False findich praktisch


if(!MyDesignMode.DesignMode)machsgut();

ist weniger leserlich, insbesondere, wenn noch der else-Block dazukommt.
Ich guck halt auch danach, doppelte Negationen zu vermeiden.

@winSharp93: Ich verstehe nicht, was das soll - was soll die Variable "isNotInDesignMode"?

Der frühe Apfel fängt den Wurm.

5.742 Beiträge seit 2007
vor 14 Jahren

@winSharp93: Ich verstehe nicht, was das soll - was soll die Variable "isNotInDesignMode"?

Ein zugegebenermaßen unqualifizierter Kommentar, wozu so etwas im schlimmsten Fall (kombiniert mit schlechtem Codingstyle) führen kann.

Aber egal.

J
237 Beiträge seit 2008
vor 14 Jahren

Wie wär's mit Operatorenüberladung?

Grüße, JasonDelife.

Beim Programmieren löst man die Probleme, die man nicht hätte, programmierte man nicht.

F
10.010 Beiträge seit 2004
vor 14 Jahren

@ErfinderDesRades:
Nicht immer ist gut gemeint auch gut gemacht.

5.299 Beiträge seit 2008
vor 14 Jahren

Aha.

Willst du damit iwas sagen? 😉

So, im ernst mal: Man musses ja nicht so machen.
Ist halt unkonventionell, undich hab das Gefühl, das isses, was den einen oder anderen da dran stört.
Aber ist doch auch eine Gelegenheit, sich mal sehr genaue (meinetwegen kleinliche) Gedanken um Code-Design zu machen, und die Ziele
Kürze, Lesbarkeit, Schreibbarkeit (Intellisense), Verstehbarkeit, Eindeutigkeit
sind m.e. in diesem Fall nicht besser umsetzbar.

On/Off könntemans noch benamen, damit keine Property da ist, die True heißt, aber false sein kann (was für die Praxis nicht von Belang ist).

Binnich damals nicht drauf gekommen, auf die Idee, vlt. weils bei On/Off im Gegensatz zu True/False bisserl so klingt, als ließe sich das zur Laufzeit umschalten oderso.

Der frühe Apfel fängt den Wurm.

T
Tom Themenstarter:in
433 Beiträge seit 2006
vor 14 Jahren

Über Code-Design kann man immer streiten, sollte aber nicht Gegenstand dieses Threads hier sein.

Ich habe die vergangen 2 Tage genutzt um mich tiefer mit der Problematik auseinanderzusetzen.

[B][URL]Component.DesignMode[/URL]:[/B]
Component.DesignMode ist nur sehr eingeschränkt zu verwenden.
[QUOTE]Der Indikator für den Entwurfsmodus wird in der  ISite gespeichert, daher ist der Wert dieser Eigenschaft false, wenn Component keine ISite zugeordnet ist.[/QUOTE]
Soll heissen, der Wert ist wenn in einem Konstruktor abgefragt wird immer false.
Sowie wenn kein Container verwendet wird der ISite implementiert.
Also nichts für mich, ab in die Tonne wenn ich mich nicht drauf verlassen kann!
[B][URL]LicenseManager.UsageMode[/URL]:[/B]
Der LicenseManager prüft den CurrentContext. Auch hier gibt es wieder eine Einschränkung von Seiten Microsoft.
[QUOTE]Wenn diese Eigenschaft keinen  CurrentContext-Wert finden kann, wird  Runtime zurückgegeben.[/QUOTE]
Es gibt hierbei auch Szenarien in der Visual Studio IDE in denen der CurrentContext wohl leer ist.
Zumal es auch möglich ist diesen CurrentContext anderweitig zu setzen. Dies scheinen wohl einige Anbieter von Controls zu machen um Ihre Lizenzpolitik umsetzen zu können.
Nächster Wurf in die Tonne!
[B]StartupPath/ProcessName Hack:[/B]
Bei dem Hack wird überprüft in welchem StartupPath man sich befindet. Vorrangig wird nach Common7 im Path geschaut und dann davon ausgegangen das man sich im DesignMode befindet. Pfade ändern sich -> Tonne.

Bei dem ProcessName Hack wird überprüft ob der derzeitge Prozess den Namen devenv enthält. Schon ab VS.NET 2008 hat sich das geändert und man müsste auf vshost prüfen. Keiner weiss wie sich die IDE 2010 verhält -> Tonne.
[B]Assemblies überprüfen:[/B]
Schlussendlich habe ich einen interessanten Artikel gefunden der sich mit der Problematik des DesignModes tiefer beschäftigt. Dabei hatte der Author die Idee zu überprüfen ob die EntryAssembly die Anwendung an sich ist, oder ob sie von der IDE stammt.

VS.NET liefert im DesignMode bei Assembly.GetEntryAssembly null zurück. Damit lies sich jetzt schon mal arbeiten.

Ich werde die Thematik aus reiner Neugier noch weiter überprüfen und nach etwas Recherche hier auch dementsprechend Code veröffentlichen.

Ich hoffe ich konnte jetzt schon aufzeigen das die ersten drei Methoden nur sehr unzureichend sind.

Derzeit würde ich dazu tendieren Assembly.GetEntryAssembly auf null zu überprüfen.

Bis die Tage,
Tom

T
Tom Themenstarter:in
433 Beiträge seit 2006
vor 14 Jahren

Ich habe jetzt einen Niederländer gefunden der auf seinem Blog Undermyhat schon einen Artikel zum Thema DesignMode veröffentlich hat.

Nach einer kurzen Kontaktaufnahme hat er sich auch dazu bereit erklärt den Code unter dem sehr aufsführlichen Artikel zu veröffentlichen.
Er geht die bisher veröffentlichten Lösungen systematisch durch und zeigt die jeweiligen Schwachstellen auf. Seine Lösung basiert auf die Überprüfung der referenzierten Assemblies. Aber liest bitte selbst nach 😃

Da er die Implementation mit Linq vorgenommen hat und wir derzeit nur 2.0 einsetzen werde ich den Code für unser Team umschreiben.

Mit seiner Idee scheint er wohl auch richtig zu liegen, da ich bisher immer richtige Ergebnisse bekam.

Gruß,
Tom

5.299 Beiträge seit 2008
vor 14 Jahren

Hmm.
Probierma


namespace UnderMyHat.ToolChest {
   public partial class Form1 : Form {

      public Form1() {
         InitializeComponent();
         MessageBox.Show(ExecutionMode.IsDesignMode.ToString());
      }
   }
}

Wennichs mit F5 laufenlasse, gibtesmir true aus.

Der frühe Apfel fängt den Wurm.

T
Tom Themenstarter:in
433 Beiträge seit 2006
vor 14 Jahren

Hallo ErfinderDesRades,

gut das du dich meldest.

Welche VS.NET Version verwendest du denn? Und wie hast du den Code von UnderMyHat eingebunden?

Wir verwenden hier VS.NET 2005 und ich habe es gerade noch einmal mit den DEBUG sowie RELEASE DLLs ausprobiert und bei mir gibt er false zurück.

Möchte das Problem gerne lösen, da wir wie gesagt die Lösung auch intern verwenden wollen.

Gruß,
Tom

5.299 Beiträge seit 2008
vor 14 Jahren

Der frühe Apfel fängt den Wurm.

T
Tom Themenstarter:in
433 Beiträge seit 2006
vor 14 Jahren

Hallo,

probier mal die DLLs als Referenz in einem Windows-Form Projekt einzubinden.
Dann sollte es klappen.

Ich denke der Aufruf innerhalb der selben Assembly funktioniert auf Grund der Untersuchung der referenzierten Assemblies nicht.

Gruß,
Tom

5.299 Beiträge seit 2008
vor 14 Jahren

Möchte das Problem gerne lösen, da wir wie gesagt die Lösung auch intern verwenden wollen.

Ja, jetzt meldets korrekt. Ich hab aber noch mehr Bedenken:
Im Designer wird bei vielen (allen?) Änderungen an Controls alles neu initialisiert. Und der Designer ist eh nicht besonders schnell.
Und beim Initialisieren würde dann DesignMode abgefragt. Und da jedesmal die EntryAssembly loaden, und rekursiv alle davon abhängigen loaden - ist mir ziemlich Overkill.
Es scheint auch iwie gecashet zu sein, dass nicht jedesmal der Assembly-Loade-Tanz losgeht, das müsste man mal iwie testen, ob der Cashe auch funktioniert, oder ob das auch beim neu initialisieren verloren geht.
Das hier macht mich bisserl misstrauisch:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace UnderMyHat.ToolChest {
   class ExecutionMode {
      
      static ExecutionMode() {
         Log.Write("Inside a call to CCTOR for {0}, {1}", currentTypeAssembly.Name, currentTypeAssembly.Version);
         logAllReferencedAssemblies();

         //// try to find a matching assembly in the referenced assembly chain
         string typeAssemblyName = currentTypeAssembly.FullName;
         var result = from asmName in entryAssembly.GetRecursiveReferencedAssemblyNames()
                      where asmName.FullName == typeAssemblyName
                      select asmName;

         // if the assembly for the type ExecutionMode is not found
         // the "parent assembly" is the designer and has the assembly loaded
         // but not referenced. Otherwise, if found, we are in user mode.
         IsDesignMode = result.Count() == 0;
         IsUserMode = !IsDesignMode;
      }


      private static void logAllReferencedAssemblies() {
         if (IsReleaseMode)            return;
         Log.Write("Listing all referenced assemblies:");
         Assembly asm = Assembly.GetEntryAssembly();
         if (asm == null) {
            Log.Write("No entry assembly found");
         } else {
            Log.Write("Entry assembly is: {0}, {1}", asm.GetName().Name, asm.GetName().Version);
            foreach (AssemblyName asmName in asm.GetRecursiveReferencedAssemblyNames()) {
               Log.Write("Referenced assembly: {0}, {1}", asmName.Name, asmName.Version);
            }
         }
      }
   }

      internal static List<AssemblyName> GetRecursiveReferencedAssemblyNames(this Assembly assembly) {
         Dictionary<string, AssemblyName> assemblyList = new Dictionary<string, AssemblyName>();
         return assembly.GetRecursiveReferencedAssemblyNames(assemblyList).Values.ToList();
      }

   }
}


erstmal durchläufter die Assembly-Lade-Rekursion, nur um die Namen zu loggen.
Kurz darauf durchläufters nochmal, diesmal, um festzustellen, ob die eigene Assembly dabei ist.
Vor allem schlecht findich, dass die Rekursion nicht abgebrochen wird, sobald eine Assembly matcht.
Also ich glaub jedenfalls, dass Assembly.Load(AssemblyName) recht lahm ist, und auch ordentlich Speicher konsumiert (wenn auch nur, bis der GC es wieder wegräumt).

gut, das sind Statische Methoden, zur Laufzeit würden die nur einmal durchlaufen. Aber die Designzeit ist schon bisserl besonders.

Na, evtl. ist das Loggen ja grade der Test, wie oft das Zeug aufgerufen wird, nur habich leider noch nicht raus, wo man den Log nachlesen kann.

Edit: Und aus dem produktiv eingesetzten Code sollte m.E. das Logging auch entfernt werden.

Der frühe Apfel fängt den Wurm.

T
Tom Themenstarter:in
433 Beiträge seit 2006
vor 14 Jahren

Hi,

Assemblies dynamisch laden ist wirklich sehr langsam.
Ich weiss jetzt auch nicht 100% wie der Designer arbeitet, aber ich vermute das er die statics nicht verwirft.
Müsste man wohl mal testen.

Ich werde das sowieso noch für unser Team hier auf 2.0 umschreiben, dabei würde ich auch auf das Logging verzichten.

Kannst ja bei dir auch entfernen, wenn du denkst das du es nicht brauchst.

Die Log Datei ansich schreibt er ins Ausführungsverzeichnis. Siehe Klasse Log.cs im Projekt.

Gruß,
Tom

5.299 Beiträge seit 2008
vor 14 Jahren

Hier übrigens die eigentliche Rekursion:



      private static Dictionary<string, AssemblyName> GetRecursiveReferencedAssemblyNames(this Assembly assembly, Dictionary<string, AssemblyName> assemblyList) {
         if (assembly == null)
            return assemblyList;

         foreach (AssemblyName assemblyName in assembly.GetReferencedAssemblies()) {
            if (!assemblyList.ContainsKey(assemblyName.FullName)) {
               assemblyList.Add(assemblyName.FullName, assemblyName);

               // load inside a try/catch because it can raise an exception File Not Found (see below)
               try {
                  Assembly.Load(assemblyName).GetRecursiveReferencedAssemblyNames(assemblyList);
               } catch (Exception) {
                  // both WindowsBase, version 3.0.0.0 and vjslib, version 2.0.0.0 (perhaps others, too)
                  // throw an exception on Mono: 
                  // "Could not load file or assembly 'WindowsBase' or one of its dependencies. The system cannot find the file specified.
                  // Log.Write("Error while loading {0}: {1}", assemblyName, e.Message);
               }
            }
         }
         return assemblyList;
      }

Er füllt dort ein Dictionary<string, Assembly>
Also ich würde das neu entwickeln, und dabei auch gucken, ob eine List<Assembly> nicht ausreichend ist.
Ich würde auch keine Rekursion verwenden, sondern eine Iterative Traversion, weil man die problemlos abbrechen kann, sobald ein Match gefunden ist.

Zu Iterative Traversion gugge Bäume durchlaufen mit Rekursion

Grad findich folgende interessante Asssembly-Property:


      //
      // Zusammenfassung:
      //     Ruft den Hostkontext ab, mit dem die Assembly geladen wurde.
      //
      // Rückgabewerte:
      //     Ein System.Int64-Wert, der den Hostkontext angibt, mit dem die Assembly geladen
      //     wurde, sofern vorhanden.
      [ComVisible(false)]
      public long HostContext { get; }

vllt. kann man daraus einen ganz neuen Ansatz basteln, um festzustellen, ob eine Assembly im Designer läuft.

Der frühe Apfel fängt den Wurm.

5.299 Beiträge seit 2008
vor 14 Jahren

Grad eine neue Idee mit Designmode: Returne, wenn möglich den Designmode von OpenForms[0].
Wenns nicht möglich ist, isses Runtime.



namespace System.Windows.Forms {
   /// <summary>System.ComponentModel.Component.DesignMode ist buggy</summary>
   public class MyDesignMode {
      private static FormCollection _OpenForms = Application.OpenForms;

      public static bool True {
         get {
            if (_OpenForms.Count == 0) return false;
            var site = _OpenForms[0].Site;
            return site == null ? false : site.DesignMode;
         }
      }
      public static bool False { get { return !True; } }
   }
}


Der frühe Apfel fängt den Wurm.