Laden...

UIEditor

Erstellt von xbu58 vor 16 Jahren Letzter Beitrag vor 16 Jahren 1.341 Views
xbu58 Themenstarter:in
92 Beiträge seit 2006
vor 16 Jahren
UIEditor

Hallo

Ich habe eine Komponente geschrieben, welche Messageboxen verwaltet. Da diese Boxen sehr viele unterschiedliche Eigenschaften haben können, wollte ich nicht den Standard UIEditor (System.ComponentModel.Design.CollectionEditor) verwenden, sondern habe selber einen geschrieben. Soweit funktioniert das alles hervorragende. Nun habe ich aber das Problem, dass die Änderungen in meinem UIEditor keine Änderung im VisualStudio bewirkt. Die Daten sind jedoch übergeben worden. Wenn ich eine Änderung auf dem Form mache (z.B. ein Label etwas verschiebe) damit ich das Form speichern kann, werden meine Änderungen übernommen. Das heisst also, dass die Änderungen zwar eingetragen wurden, das Change-Flag im Editor jedoch nicht gesetzt wurde. Was muss ich also machen, damit auch das VisualStudio feststellt, dass etwas geändert hat?

So sieht der Aufruf meines UIEditors aus:


  [ToolboxItem(false)]
  public class MsgBoxUIEditor : System.Drawing.Design.UITypeEditor
  {
    private MsgBoxDesigner msgBoxDesigner = null; 
    private IWindowsFormsEditorService editService;
    
    public MsgBoxUIEditor()
    {
      msgBoxDesigner = new MsgBoxDesigner();
    }

    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
      if (context != null && context.Instance != null && provider != null)
      {
        editService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
        if (editService != null && value is MsgBoxItemCollection)
        {
          MsgBoxItemCollection xMsgBoxes = (MsgBoxItemCollection)Convert.ChangeType(value, context.PropertyDescriptor.PropertyType);

          msgBoxDesigner.MsgBoxes = xMsgBoxes;
          editService.ShowDialog(msgBoxDesigner);

          // nur für Test
          if (xMsgBoxes.Count > 0)
            MessageBox.Show("MsgBox1 Titel: " + xMsgBoxes[0].Title);

          editService = null;
          
          return xMsgBoxes;
        }
      }
      return null;
    }
    public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
      return System.Drawing.Design.UITypeEditorEditStyle.Modal;
    }
  }


Hat jemand eine Idee, was hier falsch ist?

Danke Gruss
Xaver

O
778 Beiträge seit 2007
vor 16 Jahren

hast du auch in der property das DesignerSerializationAttribute auf Content gesetzt? Da würde ich das Problem vermuten...

xbu58 Themenstarter:in
92 Beiträge seit 2006
vor 16 Jahren

Hallo

Ja, das Flag habe ich gesetzt. Wenn ich anstelle meines Editors den Standard-Editor nehme, funktioniert es. Das Problem muss also in meinem Editor liegen. Hast Du noch eine andere Idee?

Danke und Gruss

xbu58 Themenstarter:in
92 Beiträge seit 2006
vor 16 Jahren

Hallo

Ich habe das Problem gefunden. Es liegt daran, dass es nicht reicht, dass innerhalb der Struktur einer Klasse bzw. eines Proberty's etwas ändert. Wenn ich also auf der gleichen Struktur, welche ich in EditValue mit value bekomme Änderungen vornehmen funktioniert das offensichtlich nur bei einfachen Typen. Werden hier komplexe Typen wie Collections behandelt, erkennt VS Änderungen innerhalb dieser Strukturen nicht mehr. Die Lösung ist, dass man die Struktur umkopiert, also eine neue Collection erstellt, die daten kopiert und diese zurück gibt.
So sieht mein UIEditor nun aus:


// ---------------------------------------------------------------------------
//                       UIEditor für MsgBox-Objekte
//       Copyright by VID-Software GmbH, CH-8344 Bäretswil, Switzerland    
//                         -- All Rights Reserved --                          
// ---------------------------------------------------------------------------
//  Author: X. Bühlmann                                             
//
//  Ver. Date       Vis  Comment                                       
//  1.0  12.11.2007 xbu  
// 
// ---------------------------------------------------------------------------
using System;
using System.ComponentModel;
using System.Collections;
using System.Globalization;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using VIDLib.Components;


namespace VIDLib.Components.Design
{

  /// <summary>
  /// UIEditor für MsgBox-Objekte
  /// </summary>
  [ToolboxItem(false)]
  public class MsgBoxUIEditor : System.Drawing.Design.UITypeEditor
  {
   
    /// <summary>
    /// Constructor
    /// </summary>
    public MsgBoxUIEditor()
    {
    }
    
    /// <summary>
    /// Bearbeitet den Wert des angegebenen Enumerations-Felds. 
    /// </summary>
    /// <param name="context">Ein ITypeDescriptorContext, über den zusätzliche Kontextinformationen abgerufen werden können.</param>
    /// <param name="provider">Ein Dienstanbieterobjekt, durch das Bearbeitungsdienste angefordert werden können.</param>
    /// <param name="value">Das Objekt, dessen Wert bearbeitet werden soll. </param>
    /// <returns>Der neue Wert des Objekts. Wenn sich der Wert des Objekts nicht geändert hat, 
    /// wird hierbei dasselbe Objekt zurückgegeben, das zuvor übergeben wurde. </returns>
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
      if (context != null && context.Instance != null && provider != null)
      {
        IWindowsFormsEditorService xEditService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
        if (xEditService != null && value is MsgBoxItemCollection)
        {
          MsgBoxItemCollection xMsgBoxes = (MsgBoxItemCollection)Convert.ChangeType(value, context.PropertyDescriptor.PropertyType);

          MsgBoxDesigner xMsgBoxDesigner = new MsgBoxDesigner();
          xMsgBoxDesigner.MsgBoxes = xMsgBoxes;
          if (xEditService.ShowDialog(xMsgBoxDesigner) == DialogResult.OK)
          {
            MsgBoxItemCollection xBoxList = new MsgBoxItemCollection(xMsgBoxes.MsgBoxOwner);
            xMsgBoxDesigner.MsgBoxes.CopyTo(xBoxList);

            xMsgBoxDesigner.Dispose();

            xEditService = null;
            return xBoxList;
          }
          xEditService = null;
          return xMsgBoxes;

        }
      }
      return null;
    }
    
    /// <summary>
    /// Ruft den Editorstil ab, der von der EditValue-Methode verwendet wird.
    /// </summary>
    /// <param name="context">Ein ITypeDescriptorContext, über den zusätzliche Kontextinformationen abgerufen werden können.</param>
    /// <returns>Ein UITypeEditorEditStyle-Wert, der den von EditValue verwendeten Editorstil angibt.</returns>
    public override System.Drawing.Design.UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
      return System.Drawing.Design.UITypeEditorEditStyle.Modal;
    }
  }
}

Ich gehe jetzt also hin und erstelle eine neue Collection und kopiere die Daten aus meinem Editor in die neue Instance. Damit ich das nur mache, wenn im Editor etwas geändert hat, habe ich den DialogResult abgefangen.

Gruss
Xaver

O
778 Beiträge seit 2007
vor 16 Jahren

Tu das nicht. Wenn du das tust, dann funktioniert es recht nicht mehr. lösche lieber den INHALT der Auflistung und dann fuege die Elemente einzeln neu hinzu. Das sollte funktionieren. So wie du es jetzt machst erstellst du eine neue Collection, und das ist mit readonly properties natürlich inkompatibel. Aber mir fällt grad ein, woran es liegt. Wenn du Aenderungen an einem Objekt durchfuehren willst, dann musst du das dem DesignerHost auch mitteilen, lass dir mal ein IComponentChangeService geben und benachrichtige den, wenn du die Collection loeschst und neu befuellst.

xbu58 Themenstarter:in
92 Beiträge seit 2006
vor 16 Jahren

Wenn ich das mache, was Du geschrieben hast, funktionert es auch nicht. Das war auch meine erste Idee, ich habe also die Original-Collection gecleart und mit CopyTo (ist eine Methode, welche ich in meiner MsgBoxItemCollection definiert habe und die für jedes Objekt in der Liste eine Kopie erstellt mit new usw.) die Liste wieder an die ursprünliche Collection-Liste übertragen habe. Das Problem war genau gleich, wie wenn ich die Objekte in der Liste direkt manipuliert habe.
Ich verstehe jedoch nicht, was Du mit "lass dirmal einen IComponentChangeService geben" meinst. Den habe ich doch nicht zur Verfügung. Oder wie mache ich das?

Danke und Gruss
Xaver

O
778 Beiträge seit 2007
vor 16 Jahren

Du hast einen Serviceprovider zur Verfuegung, der nur dazu da ist, dir Serviceobjekte wie dieses zu liefern. Entwurfszeitkomponenten, Designer und so'n Kram ist alles SOA, d.h., man muss im wesentlichen nur wissen, welchen Servicetyp man gerade braucht (und ob der Provider diesen liefern kann). Leider gibt es keine Moeglichkeit rauszukriegen, welche Servicetypen VS unterstuetzt. Aber ich meine, selbst wenn, dann setzt dir jemand eine andere IDE an den Code, e.g. #dev, die diesen Service vielleicht nicht unterstuetzt, und sch wars. Lieber immer pruefen. Aber das hast du ja im Fall des IWindowsFormsEditorServices auch gemacht 🙂

xbu58 Themenstarter:in
92 Beiträge seit 2006
vor 16 Jahren

Hallo onlinegurke (was für ein verrückter Name 😄)

Mit dem IComponentChangeService funktioniert es wirklich! So sieht der Code der EditValue-Methode jetzt aus:


    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
      if (context != null && context.Instance != null && provider != null)
      {
        IComponentChangeService xChangeService = (IComponentChangeService)provider.GetService(typeof(IComponentChangeService));
        IWindowsFormsEditorService xEditService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
        if (xEditService != null && value is MsgBoxItemCollection)
        {
          MsgBoxItemCollection xMsgBoxes = (MsgBoxItemCollection)Convert.ChangeType(value, context.PropertyDescriptor.PropertyType);

          MsgBoxDesigner xMsgBoxDesigner = new MsgBoxDesigner();
          xMsgBoxDesigner.MsgBoxes = xMsgBoxes;
          if (xEditService.ShowDialog(xMsgBoxDesigner) == DialogResult.OK)
            xChangeService.OnComponentChanged(xMsgBoxes, null, null, null);

          xMsgBoxDesigner.Dispose();
          xMsgBoxDesigner = null;
          xEditService = null;
          xChangeService = null;
          return xMsgBoxes;
        }
      }
      return null;
    }

Gefällt mir so viel besser. Das Ganze rumkopiern hat mir überhaupt nicht gefallen. War mir da von Delphi her etwas besseres gewohnt. Aber mit dem Service (wenn man denn weiss, dass es so etwas gibt =)) gehts sogar besser als in Delphi.
Es wäre nur schön, wenn die Help in solchen Bereichen auch so ausführlich wie in Delphi wäre.

Besten Dank für die Hilfe und viele Grüsse aus der Schweiz
Xaver

O
778 Beiträge seit 2007
vor 16 Jahren

SOA ist an sich auch ne tolle Sache, gerade auch so, wie es Microsoft bei den Entwurfszeitmustern durchzieht, aber so dokumentiert nuetzt das halt nicht allzu viel...

zum nick:
Woher kommen eure Nicknames?

aber langsam überlege ich, mir doch einen neuen nickname auszudenken oder zumindest mal n bild von mir reinzustellen...

xbu58 Themenstarter:in
92 Beiträge seit 2006
vor 16 Jahren

Hab nichts gegen Deinem nickname einzuwenden! Ich finde es immer amüsant, welche Namen da verwendet werden. Bei Gurke kommt mir halt immer den Salat in den Sinn, den ich nicht besonders mag... 👅 . Aber das Gemüse soll ja gesund ein.

Ich habe den Code noch etwas angepasst. Nun rufe ich nicht mehr return null auf sondern return value. Scheint mir sicherer zu sein, da value ja den Wert enthalten soll, der momentan im Property steht.

Gruss
Xaver

O
778 Beiträge seit 2007
vor 16 Jahren

wenn das property auf readonly gesetzt ist, ist das egal, aber wenn du value zurückgibst, dann unterstützt dein Editor auch "normale" properties