[ABAP] ALV-Grid als Property-Grid (Parametertabelle) mit Edit-Feldern und Typprüfung

Quelle / Inspiration gefunden auf: www.tricktresor.de

* ALV-Konstanten
INCLUDE <cl_alv_control>.

CLASS lcl_main DEFINITION.
  PUBLIC SECTION.

    TYPES: ty_enum TYPE i.

    TYPES: BEGIN OF ty_param,
             name  TYPE string,
             type  TYPE ty_enum,
             text  TYPE string,
             value TYPE string,
           END OF ty_param.

    TYPES: ty_it_params TYPE STANDARD TABLE OF ty_param WITH NON-UNIQUE DEFAULT KEY.

* Enum
    CLASS-DATA: string TYPE ty_enum VALUE 1 READ-ONLY.
    CLASS-DATA: int TYPE ty_enum VALUE 2 READ-ONLY.
    CLASS-DATA: float TYPE ty_enum VALUE 3 READ-ONLY.
    CLASS-DATA: date TYPE ty_enum VALUE 4 READ-ONLY.

* Bezeichner
    CONSTANTS: co_name TYPE string VALUE 'NAME'.
    CONSTANTS: co_type TYPE string VALUE 'TYPE'.
    CONSTANTS: co_text TYPE string VALUE 'TEXT'.
    CONSTANTS: co_value TYPE string VALUE 'VALUE'.

    METHODS: constructor
      IMPORTING
        o_parent TYPE REF TO cl_gui_container.

    METHODS: init_grid.

    METHODS: add_parameter
      IMPORTING
        parameter TYPE ty_param.

    METHODS: get_params
      RETURNING VALUE(rv_it_parameters) TYPE ty_it_params.

  PROTECTED SECTION.

    DATA: o_grid TYPE REF TO cl_gui_alv_grid.
    DATA: it_params TYPE ty_it_params.

    METHODS: on_data_changed FOR EVENT data_changed OF cl_gui_alv_grid
      IMPORTING
          er_data_changed
          sender.

ENDCLASS.

CLASS lcl_main IMPLEMENTATION.

  METHOD constructor.
    o_grid = NEW #( i_parent = o_parent ).
  ENDMETHOD.

* ALV-Gitter initialisieren
  METHOD init_grid.

    DATA(it_fieldcat) = VALUE lvc_t_fcat( ( fieldname  = co_name
                                            outputlen  = 15
                                            coltext    = 'Parameter'
                                            style      = alv_style_font_bold + alv_style_color_int_group )
                                          ( fieldname  = co_type
                                            outputlen  = 5
                                            coltext    = 'Type'
                                            no_out     = abap_true )
                                          ( fieldname  = co_text
                                            outputlen  = 15
                                            coltext    = 'Text' )
                                          ( fieldname  = co_value
                                            outputlen  = 20
                                            coltext    = 'Value'
                                            edit       = abap_true
                                            fix_column = abap_true ) ).

    DATA(s_layout) = VALUE lvc_s_layo( no_toolbar = abap_true
                                       no_headers = abap_false ).

    SET HANDLER on_data_changed FOR o_grid.

* Tastenevents registrieren
    o_grid->register_edit_event( i_event_id = cl_gui_alv_grid=>mc_evt_enter ).    " Enter
    o_grid->register_edit_event( i_event_id = cl_gui_alv_grid=>mc_evt_modified ). " Eingabe, Cursortasten

* ALV-Grid selektionsbereit setzen
    o_grid->set_ready_for_input( i_ready_for_input = 1 ).

    o_grid->set_table_for_first_display( EXPORTING
                                           is_layout       = s_layout
                                         CHANGING
                                           it_outtab       = it_params
                                           it_fieldcatalog = it_fieldcat ).

  ENDMETHOD.

* Parameterliste zurückholen
  METHOD get_params.
    rv_it_parameters = it_params.
  ENDMETHOD.

* Parameter hinzufügen
  METHOD add_parameter.
    APPEND VALUE #( name  = parameter-name
                    type  = parameter-type
                    text  = parameter-text
                    value = parameter-value ) TO it_params.
  ENDMETHOD.

  METHOD on_data_changed.
* geänderte Zellen durchgehen
    LOOP AT er_data_changed->mt_good_cells ASSIGNING FIELD-SYMBOL(<fs_changed>).
      IF <fs_changed> IS ASSIGNED.
* Zeile x aus der iTab it_params rausholen und daraus die Zelle anhand des Spaltennamens (Feldnamens) holen
        TRY.
            ASSIGN COMPONENT <fs_changed>-fieldname OF STRUCTURE it_params[ <fs_changed>-row_id ] TO FIELD-SYMBOL(<fs_param>).

            IF <fs_param> IS ASSIGNED.
* Änderungswert in die Zelle der iTab (it_params) rückschreiben
              CASE it_params[ <fs_changed>-row_id ]-type.
                WHEN lcl_main=>string.
                  <fs_param> = <fs_changed>-value.
                WHEN lcl_main=>int.
* Typprüfung für Int
                  IF cl_abap_matcher=>create( pattern = '^[-+]?[0-9]*$'
                                              text = <fs_changed>-value
                                              ignore_case = abap_true )->match( ) = abap_true.

                    <fs_param> = <fs_changed>-value.
                  ELSE.
* Eingabefehler im Protokoll anzeigen
                    er_data_changed->add_protocol_entry( i_msgid     = '0K'
                                                         i_msgno     = '000'
                                                         i_msgty     = 'E'
                                                         i_msgv1     = 'Ungültiger Wert:'
                                                         i_msgv2     = <fs_changed>-value
                                                         i_msgv3     = 'Bitte einen Wert vom Typ int eingeben!'
                                                         i_msgv4     = ''
                                                         i_fieldname = <fs_changed>-fieldname
                                                         i_row_id    = <fs_changed>-row_id ).
                  ENDIF.
                WHEN lcl_main=>float.
* Typprüfung für Float
                  IF cl_abap_matcher=>create( pattern = '^[-+]?[0-9]*[,]?[0-9]+([eE][-+]?[0-9]+)?$'
                                              text = <fs_changed>-value
                                              ignore_case = abap_true )->match( ) = abap_true.

                    <fs_param> = <fs_changed>-value.
                  ELSE.
* Eingabefehler im Protokoll anzeigen
                    er_data_changed->add_protocol_entry( i_msgid     = '0K'
                                                         i_msgno     = '000'
                                                         i_msgty     = 'E'
                                                         i_msgv1     = 'Ungültiger Wert:'
                                                         i_msgv2     = <fs_changed>-value
                                                         i_msgv3     = 'Bitte einen Wert vom Typ float eingeben!'
                                                         i_msgv4     = ''
                                                         i_fieldname = <fs_changed>-fieldname
                                                         i_row_id    = <fs_changed>-row_id ).
                  ENDIF.
                WHEN lcl_main=>date.
* Typprüfung für Date
                  IF cl_abap_matcher=>create( pattern = '^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d$'
                                              text = <fs_changed>-value
                                              ignore_case = abap_true )->match( ) = abap_true.

                    <fs_param> = <fs_changed>-value.
                  ELSE.
* Eingabefehler im Protokoll anzeigen
                    er_data_changed->add_protocol_entry( i_msgid     = '0K'
                                                         i_msgno     = '000'
                                                         i_msgty     = 'E'
                                                         i_msgv1     = 'Ungültiger Wert:'
                                                         i_msgv2     = <fs_changed>-value
                                                         i_msgv3     = 'Bitte einen Datumswert im Format dd.mm.yyyy eingeben!'
                                                         i_msgv4     = ''
                                                         i_fieldname = <fs_changed>-fieldname
                                                         i_row_id    = <fs_changed>-row_id ).
                  ENDIF.
              ENDCASE.
            ENDIF.
          CATCH cx_root.
        ENDTRY.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.

ENDCLASS.

**********************************************************************
*
* Datentypen, Variablen, Konstanten
*
**********************************************************************
DATA: o_main TYPE REF TO lcl_main.

**********************************************************************
*
* SELECTION-SCREEN
*
**********************************************************************
SELECTION-SCREEN BEGIN OF SCREEN 2000.
SELECTION-SCREEN END OF SCREEN 2000.

**********************************************************************
*
* INITIALIZATION
*
**********************************************************************
INITIALIZATION.

  o_main = NEW #( o_parent = NEW cl_gui_docking_container( side = cl_gui_docking_container=>dock_at_left
                                                           extension = 500
                                                           no_autodef_progid_dynnr = abap_true ) ).

* Beispiel-Parameter einfügen
* String-Parameter
  o_main->add_parameter( VALUE #( name = 'PERSON_VOR'
                                  type = lcl_main=>string
                                  text = 'Vorname'
                                  value = 'Udo' ) ).
* String-Parameter
  o_main->add_parameter( VALUE #( name = 'PERSON_NACH'
                                  type = lcl_main=>string
                                  text = 'Nachname'
                                  value = 'Lehmann' ) ).
* Int-Parameter
  o_main->add_parameter( VALUE #( name = 'PERSON_ALTER'
                                  type = lcl_main=>int
                                  text = 'Alter'
                                  value = '34' ) ).
* Float-Parameter
  o_main->add_parameter( VALUE #( name = 'PERSON_GROESSE'
                                  type = lcl_main=>float
                                  text = 'Größe'
                                  value = '1,80' ) ).
* Date-Parameter
  o_main->add_parameter( VALUE #( name = 'PERSON_DATE'
                                  type = lcl_main=>date
                                  text = 'Geburtsdatum'
                                  value = '01.01.1980' ) ).

**********************************************************************
*
* AT SELECTION-SCREEN OUTPUT
*
**********************************************************************
AT SELECTION-SCREEN OUTPUT.

  IF o_main IS BOUND.
    o_main->init_grid( ).
  ENDIF.

**********************************************************************
*
* AT SELECTION-SCREEN
*
**********************************************************************
AT SELECTION-SCREEN.
* wenn "Ausführen" (F8) geklickt wurde
  IF sy-ucomm = 'CRET'.
    cl_demo_output=>display_data( o_main->get_params( ) ).
  ENDIF.

**********************************************************************
*
* START-OF-SELECTION
*
**********************************************************************
START-OF-SELECTION.
* leeres Selektionbild 2000 anzeigen
  CALL SELECTION-SCREEN 2000.

[ABAP] ALV-Grid als Property-Grid mit Tri-State Klickfeldern/Buttons (ON / OFF / UNDEF)

Quelle / Inspiration gefunden auf: www.tricktresor.de

Die Codeteile mit UNDEF / abap_undefined können auch auskommentiert werden, so dass sich eine Möglichkeit der Dual-State Umschaltung (ON / OFF) per Mausklick ergibt.

* ALV-Konstanten
INCLUDE <cl_alv_control>.

CLASS lcl_main DEFINITION.
  PUBLIC SECTION.

    TYPES: BEGIN OF ty_param,
             name   TYPE string,
             text   TYPE string,
             status TYPE boolean,
           END OF ty_param.

    TYPES: ty_it_params TYPE STANDARD TABLE OF ty_param WITH NON-UNIQUE DEFAULT KEY.

    TYPES: BEGIN OF ty_ui_param,
             name    TYPE string,
             text    TYPE string,
             status  TYPE icon_text,
             t_color TYPE lvc_t_scol,
             t_style TYPE lvc_t_styl,
           END OF ty_ui_param.

    TYPES: ty_it_ui_params TYPE STANDARD TABLE OF ty_ui_param.

    TYPES: ty_cell_type TYPE i.

    CONSTANTS: co_celltype_hotspot TYPE ty_cell_type VALUE 1.
    CONSTANTS: co_celltype_button TYPE ty_cell_type VALUE 2.

* Farben
    CONSTANTS: co_color_on TYPE i VALUE col_positive.
    CONSTANTS: co_color_off TYPE i VALUE col_negative.
    CONSTANTS: co_color_undef TYPE i VALUE col_total.

* Bezeichner
    CONSTANTS: co_status TYPE string VALUE 'STATUS'.
    CONSTANTS: co_on TYPE string VALUE 'ON'.
    CONSTANTS: co_off TYPE string VALUE 'OFF'.
    CONSTANTS: co_undef TYPE string VALUE 'N/A'.

* Icons
    CONSTANTS status_icon_on TYPE icon_text VALUE icon_oo_object.
    CONSTANTS status_icon_off TYPE icon_text VALUE icon_system_start_recording.
    CONSTANTS status_icon_undef TYPE icon_text VALUE icon_oo_class.
*    CONSTANTS status_icon_undef TYPE icon_text VALUE icon_led_yellow.

    METHODS: constructor
      IMPORTING
        o_parent TYPE REF TO cl_gui_container.

    METHODS: init_grid
      IMPORTING
        cell_type TYPE ty_cell_type.

    METHODS: add_parameter
      IMPORTING
        parameter TYPE ty_param.

    METHODS: get_params
      RETURNING VALUE(rv_it_parameters) TYPE ty_it_params.

  PROTECTED SECTION.

    DATA: o_grid TYPE REF TO cl_gui_alv_grid.
    DATA: it_params TYPE ty_it_params.
    DATA: it_ui_params TYPE ty_it_ui_params.

    METHODS: set_color
      IMPORTING
                status       TYPE boolean
      RETURNING VALUE(color) TYPE lvc_t_scol.

    METHODS: on_hotspot_click FOR EVENT hotspot_click OF cl_gui_alv_grid
      IMPORTING
          e_row_id.

    METHODS: on_button_click FOR EVENT button_click OF cl_gui_alv_grid
      IMPORTING
          es_col_id
          es_row_no.

ENDCLASS.

CLASS lcl_main IMPLEMENTATION.

  METHOD constructor.
    o_grid = NEW #( i_parent = o_parent ).
  ENDMETHOD.

  METHOD init_grid.

    CLEAR: it_ui_params.

* Parameter in ALV-Zeilen umwandeln
    LOOP AT it_params ASSIGNING FIELD-SYMBOL(<fs_param>).

      CASE <fs_param>-status.
        WHEN abap_true.
* ON
          APPEND VALUE #( name = <fs_param>-name
                          text = <fs_param>-text
                          status = |{ status_icon_on } { co_on }|
                          t_color = me->set_color( abap_true ) ) TO it_ui_params.
        WHEN abap_false.
* OFF
          APPEND VALUE #( name = <fs_param>-name
                          text = <fs_param>-text
                          status = |{ status_icon_off } { co_off }|
                          t_color = me->set_color( abap_false ) ) TO it_ui_params.
        WHEN abap_undefined.
* UNDEF
          APPEND VALUE #( name = <fs_param>-name
                          text = <fs_param>-text
                          status = |{ status_icon_undef } { co_undef }|
                          t_color = me->set_color( abap_undefined ) ) TO it_ui_params.

      ENDCASE.

    ENDLOOP.

    DATA: it_fieldcat TYPE lvc_t_fcat.

    IF cell_type = co_celltype_button.
      it_fieldcat = VALUE lvc_t_fcat( ( fieldname = 'NAME'
                                        outputlen = 15
                                        coltext = 'Parameter'
                                        style = alv_style_font_bold + alv_style_color_int_group )
                                      ( fieldname = 'TEXT'
                                        outputlen = 20
                                        coltext = 'Description' )
                                      ( fieldname = co_status
                                        outputlen = 10
                                        coltext = 'Switch'
                                        style = alv_style_button
                                        icon = abap_true
                                        fix_column = abap_true ) ).
    ELSE.
      it_fieldcat = VALUE lvc_t_fcat( ( fieldname = 'NAME'
                                        outputlen = 15
                                        coltext = 'Parameter'
                                        style = alv_style_font_bold + alv_style_color_int_group )
                                      ( fieldname = 'TEXT'
                                        outputlen = 20
                                        coltext = 'Description' )
                                      ( fieldname = co_status
                                        outputlen = 10
                                        coltext = 'Switch'
                                        hotspot = abap_true
                                        icon = abap_true
                                        fix_column = abap_true ) ).
    ENDIF.

    DATA(s_layout) = VALUE lvc_s_layo( stylefname = 'T_STYLE'
                                       ctab_fname = 'T_COLOR'
                                       no_toolbar = abap_true
                                       no_headers = abap_false ).

    SET HANDLER on_hotspot_click FOR o_grid.
    SET HANDLER on_button_click FOR o_grid.

    o_grid->set_table_for_first_display( EXPORTING
                                           is_layout = s_layout
                                         CHANGING
                                           it_outtab = it_ui_params
                                           it_fieldcatalog = it_fieldcat ).

  ENDMETHOD.

* Parameterliste zurückholen
  METHOD get_params.
    rv_it_parameters = it_params.
  ENDMETHOD.

* Parameter hinzufügen
  METHOD add_parameter.
    APPEND VALUE #( name   = parameter-name
                    text   = parameter-text
                    status = parameter-status ) TO it_params.
  ENDMETHOD.

* Hintergrundfarben setzen
  METHOD set_color.

    CASE status.
      WHEN abap_true.
* Grün / ON
        color = VALUE #( ( fname = co_status color-col = co_color_on ) ).
      WHEN abap_false.
* Rot / OFF
        color = VALUE #( ( fname = co_status color-col = co_color_off ) ).
      WHEN abap_undefined.
* Gelb / UNDEF
        color = VALUE #( ( fname = co_status color-col = co_color_undef ) ).
    ENDCASE.

  ENDMETHOD.

  METHOD on_hotspot_click.

* geklickte ALV-Zeile holen
    READ TABLE it_ui_params ASSIGNING FIELD-SYMBOL(<ui_param>) INDEX e_row_id-index.
* korrespondierenden Parameter ermitteln
    READ TABLE it_params ASSIGNING FIELD-SYMBOL(<param>) WITH KEY name = <ui_param>-name.

    CASE <param>-status.
      WHEN abap_true.
* ON->OFF
        <ui_param>-status = |{ status_icon_off } { co_off }|.
        <ui_param>-t_color = me->set_color( abap_false ).
        <param->-status = abap_false.
      WHEN abap_false.
* OFF->UNDEF
        <ui_param>-status = |{ status_icon_undef } { co_undef }|.
        <ui_param>-t_color = me->set_color( abap_undefined ).
        <param>-status = abap_undefined.
      WHEN abap_undefined.
* UNDEF->ON
        <ui_param>-status = |{ status_icon_on } { co_on }|.
        <ui_param>-t_color = me->set_color( abap_true ).
        <param>-status = abap_true.
    ENDCASE.

    o_grid->refresh_table_display( is_stable = VALUE lvc_s_stbl( row = abap_true
                                                                 col = abap_true )
                                   i_soft_refresh = abap_false ).
  ENDMETHOD.

  METHOD on_button_click.
* geklickte ALV-Zeile holen
    READ TABLE it_ui_params ASSIGNING FIELD-SYMBOL(<ui_param>) INDEX es_row_no-row_id.
* korrespondierenden Parameter ermitteln
    READ TABLE it_params ASSIGNING FIELD-SYMBOL(<param>) WITH KEY name = <ui_param>-name.

    CASE <param>-status.
      WHEN abap_true.
* ON->OFF
        <ui_param>-status = status_icon_off.
        <ui_param>-t_color = me->set_color( abap_false ).
        <param>-status = abap_false.
      WHEN abap_false.
* OFF->UNDEF
        <ui_param>-status = status_icon_undef.
        <ui_param>-t_color = me->set_color( abap_undefined ).
        <param>-status = abap_undefined.
      WHEN abap_undefined.
* UNDEF->ON
        <ui_param>-status = status_icon_on.
        <ui_param>-t_color = me->set_color( abap_true ).
        <param>-status = abap_true.
    ENDCASE.

    o_grid->refresh_table_display( is_stable = VALUE lvc_s_stbl( row = abap_true
                                                                 col = abap_true )
                                   i_soft_refresh = abap_false ).
  ENDMETHOD.


ENDCLASS.

**********************************************************************
*
* Datentypen, Variablen, Konstanten
*
**********************************************************************
DATA: o_main TYPE REF TO lcl_main.

**********************************************************************
*
* SELECTION-SCREEN
*
**********************************************************************
SELECTION-SCREEN BEGIN OF SCREEN 2000.
SELECTION-SCREEN END OF SCREEN 2000.

**********************************************************************
*
* INITIALIZATION
*
**********************************************************************
INITIALIZATION.

  o_main = NEW #( o_parent = NEW cl_gui_docking_container( side = cl_gui_docking_container=>dock_at_right
                                                           extension = 400
                                                           no_autodef_progid_dynnr = abap_true ) ).

  o_main->add_parameter( VALUE #( name = 'SHOW_RESULT' text = 'Ergebnisse anzeigen' status = abap_true ) ).
  o_main->add_parameter( VALUE #( name = 'HIDE_COLS' text = 'Spalten ausblenden' status = abap_false ) ).
  o_main->add_parameter( VALUE #( name = 'DELETE_ON_EXIT' text = 'Beim Beenden löschen' status = abap_undefined ) ).

**********************************************************************
*
* AT SELECTION-SCREEN OUTPUT
*
**********************************************************************
AT SELECTION-SCREEN OUTPUT.

  IF o_main IS BOUND.
* Flag cell_type triggert den Typ (Hotspot / Button) des Klick-Feldes
    o_main->init_grid( cell_type = lcl_main=>co_celltype_hotspot ).
  ENDIF.

**********************************************************************
*
* AT SELECTION-SCREEN
*
**********************************************************************
AT SELECTION-SCREEN.
* wenn "Ausführen" (F8) geklickt wurde
  IF sy-ucomm = 'CRET'.
    cl_demo_output=>display_data( o_main->get_params( ) ).
  ENDIF.

**********************************************************************
*
* START-OF-SELECTION
*
**********************************************************************
START-OF-SELECTION.
* leeres Selektionbild 2000 anzeigen
  CALL SELECTION-SCREEN 2000.

[C#] Spaltenbreite eines PropertyGrids festlegen

  • SetPropertyGridColumnWidth() simuliert das Verstellen der Spaltenbreite mit der Maus
  • das PropertyGrid muss sichtbar sein (visible == true)
  • Funktionsaufruf funktioniert nicht im Form-Konstruktor, da dort das PropertyGrid noch nicht sichtbar ist
using System;
using System.Windows.Forms;
using System.Reflection;

// statt im Konstruktor die Breite einzustellen, lieber das Ereignis Shown()
// benutzen, da dies erst eintritt, nachdem die gesamte Form schon angezeigt wurde
private void Form1_Shown(object sender, EventArgs e)
{
    this.SetPropertyGridColumnWidth(PropertyGrid1, 100);
}

// Spaltenbreite eines Propertygrids setzen
private void SetPropertyGridColumnWidth(PropertyGrid grid, int width)
{
    if (grid != null)
    {
        FieldInfo fi = grid.GetType().GetField(@"gridView", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
        if (fi != null)
        {
            Control view = fi.GetValue(grid) as Control;
            if (view != null)
            {
                // Funktion "MoveSplitterTo" holen
                MethodInfo mi = view.GetType().GetMethod(@"MoveSplitterTo", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod);
                if (mi != null)
                {
                    // Funktion "MoveSplitterTo" ausführen
                    mi.Invoke(view, new object[] { width });
                }
            }
        }
    }
}

[C#] PropertyGrid verwenden

Jeder kennt das im Visual Studio verwendete Eigenschaftsfenster und das darin liegende Steuerelement für die Manipulation der Objekteigenschaften (Properties). Man findet es in der Toolbox unter “Alle Windows Forms”.

  • Zunächst ein Beispielobjekt, ein Auto 🙂
    using System.ComponentModel;
    
    public enum ECarType
    {
        [Description("ohne Dach")]
        Cabriolet,
        [Description("mit Dach")]
        Stufenheck,
        [Description("mit Rucksack")]
        Combi
    }
    
    // Beispiel-Klassenstruktur für das später im PropertyGrid zu editierende Objekt
    // DefaultPropertyAttribute legt fest, dass Tyres standardmäßig ausgewählt ist
    [DefaultPropertyAttribute("Tyres")]
    public class CCar
    {
        private int iTyres = 4;
        private string sManufacturer = "Horch";
        private string sCarImage = string.Empty;
        private Color clCarColor = Color.Black;
        private List<double-> lConsumption = new List<double->();
        private ECarType eCarType = ECarType.Stufenheck;
    
        // das Auto hat eine bestimmte Anzahl Räder
        // mögliche Attribute für die Steuerung der PropertyGrid-Eigenschaften:
        //
        // Browsable - Objekteigenschaft im PropertyGrid anzeigen?
        // DefaultValue - Standardwert für die Eigenschaft, wenn kein Wert gesetzt
        // Description - Hilfetext für das Hilfefenster
        // Category - Kategorie (Gruppe) für die Sortierung der Eigenschaften
        // ReadOnly - steuert "nur Lesen" Eigenschaft
        // DefaultProperty - Eigenschaft als Standardeigenschaft, wird dann automatisch bei der Anzeige ausgewählt
        // DisplayName - angezeigter Propertyname
        [Browsable(true), DefaultValue(4), Description("Anzahl der Reifen"), Category("Allgemein"), DisplayName("verwendete Reifen")]
        public int Tyres
        {
            get { return iTyres; }
            set { iTyres = value; }
        }
    
        // das Auto hat natürlich einen Hersteller
        [Browsable(true), DefaultValue("Horch"), Description("Name des Herstellers"), Category("Allgemein"), DisplayName("Hersteller des Autos")]
        public string Manufacturer
        {
            get { return sManufacturer; }
            set { sManufacturer = value; }
        }
        
        // das Auto soll ein Bild zugeordnet bekommen
        // zur späteren komfortablen Bildauswahl soll ein Fileauswahl-Dialog angezeigt werden
        // dazu sollte sich eine eigene Klasse 'CFileNameEditorHelper' abgeleitet werden (s.u.)
        [Editor(typeof(CFileNameEditorHelper), typeof(System.Drawing.Design.UITypeEditor))]
        [Browsable(true), DefaultValue(""), Description("Autobild"), Category("Grafik"), DisplayName("kleines Vorschaubild")]
        public string CarImage
        {
            get { return sCarImage; }
            set { sCarImage = value; }
        }
    
        // Farbe des Autos, Farbauswahl wird automatisch angezeigt
        [Browsable(true), Description("Autofarbe"), Category("Grafik"), DisplayName("Farbe des Autos")]
        public Color CarColor
        {
            get { return clCarColor; }
            set { clCarColor = value; }
        }
    
        // Verbrauchstabelle als Liste
        // hier wird ein eigenes Editorfenster benötigt (ConsumptionListEditor), dieser muss extra definiert werden (s.u.)
        // über den TypeConverter wird einfach der Anzeigename der Liste in "Datenwerte" angepasst
        [Editor(typeof(ConsumptionListEditor), typeof(System.Drawing.Design.UITypeEditor))]
        [Browsable(true), Description("Liste mit Verbräuchen"), Category("Kurven"), DisplayName("Verbrauch")]
        [TypeConverter(typeof(MyListConverter))]
        public List<double-> Consumption
        {
            get { return lConsumption; }
            set { lConsumption = value; }
        }
    
        // Beispiel für Verwendung von Enum, Klasse für die Umwandlung bzgl. CarTypeConverter s.u.
        [Browsable(true), Description("beschreibt den Autotyp"), Category("Allgemein"), DisplayName("Typ des Autos")]
        [TypeConverter(typeof(CarTypeConverter))]
        public ECarType CarType
        {
            get { return eCarType; }
            set { eCarType = value; }
        }
    
        public CCar()
        {
        }
    }
    
  • Hilfsklasse für die Bildauswahl
    // zuvor noch Assembly System.Design unter Verweise->Verweis hinzufügen dem Projekt hinzufügen
    using System.Windows.Forms.Design;
    
    // Eigene abgeleitete FileEditor-Klasse für die Bildauswahl
    public class CFileNameEditorHelper : FileNameEditor
    {
        public CFileNameEditorHelper()
        { }
    
        // InitializeDialog überschreiben
        protected override void InitializeDialog(System.Windows.Forms.OpenFileDialog openFileDialog)
        {
            // Basisdialog initialisieren
            base.InitializeDialog(openFileDialog);
    
            // hier die benötigten Properties des Auswahldialoges setzen
            openFileDialog.CheckFileExists = true;
            openFileDialog.ShowReadOnly = false;
            openFileDialog.Title = "Bitte ein Bild auswählen";
            openFileDialog.Filter = "BMP (*.bmp)|*.bmp|JPG (*.jpg)|*.jpg|PNG (*.png)|*.png|GIF (*.gif)|*.gif|Alle Dateien (*.*)|*.*";
            openFileDialog.DefaultExt = "";
            openFileDialog.FileOk += new System.ComponentModel.CancelEventHandler(openFileDialog_FileOk);
        }
        // Eventhandler für das Schließen des File-Dialoges
        void openFileDialog_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
        {
            // sender ist unser Dialog
            System.Windows.Forms.OpenFileDialog fd = sender as System.Windows.Forms.OpenFileDialog;
    
            if (fd != null)
            {
                // hier kann noch etwas mit dem Filedialog veranstaltet werden,
                // z.B. wenn man nur den Filenamen ohne Pfad extrahieren will
                // fd.FileName = Path.GetFileName(fd.FileName);
            }
        }
    }
    
  • Hilfsklasse für den Listeneditor
    // zuvor noch Assembly System.Design unter Verweise->Verweis hinzufügen dem Projekt hinzufügen
    using System.Windows.Forms.Design;
    
    // eigene ListEditor-Klasse für das Editieren einer Liste
    public class ConsumptionListEditor : UITypeEditor
    {
        // Editstyle des Dialoges festlegen
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.Modal;
        }
    
        public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            if ((context != null) && (provider != null))
            {
                IWindowsFormsEditorService svc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
                if (svc != null)
                {
                    // eigenen Dialog erzeugen und anzeigen
                    // im Beispiel wird im Konstruktor des Dialoges die Referenz der Liste mit übergeben
                    frmDataCurve dialog = new frmDataCurve(value as List<double->);
    
                    // Dialog anzeigen und Result abfragen
                    if (svc.ShowDialog(dialog) == System.Windows.Forms.DialogResult.OK)
                    {
                    }
                }
            }
    
            return value;
        }
    }
    
  • Hilfsklasse für CarTypeConverter
    prublic class CarTypeConverter : EnumConverter
    {
        private Type _enumType;
    
        public CarTypeConverter(Type type) : base(type)
        {
            _enumType = type;
        }
    
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destType)
        {
            return (destType == typeof(string));
        }
    
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type srcType)
        {
            return (srcType == typeof(string));
        }
        
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destType)
        {
            FieldInfo fi = _enumType.GetField(Enum.GetName(_enumType, value));
    
            DescriptionAttribute dna =(DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute)); 
    
            if (dna != null) return dna.Description;
            else return value.ToString();
        }
    
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            foreach (FieldInfo fi in _enumType.GetFields())
            {
                DescriptionAttribute dna = (DescriptionAttribute)Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute)); 
    
                if ((dna != null) && ((string)value == dna.Description))  return Enum.Parse(_enumType, fi.Name);
            }
    
            return Enum.Parse(_enumType, (string)value);
        }
    }
    
  • Hilfsklasse für TypeConverter der Liste
    // zuvor noch Assembly System.Design unter Verweise->Verweis hinzufügen dem Projekt hinzufügen
    using System.Windows.Forms.Design;
    
    public class MyListConverter : TypeConverter
    {
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destType)
        {
            // wenn Liste mit Double-Werten
            if (destType == typeof(string) && value is List<double->)
            {
                return "Datenwerte";
            }
    
            // sonst Standardkonvertierung
            return base.ConvertTo(context, culture, value, destType);
        }
    }
    
  • Anwendungsbeispiel
    // Beispielobjekt erzeugen
    CCar Car = new CCar();
    
    // dem PropertyGrid ein Objekt zum Editieren zuordnen
    // propertyGrid1 - das verwendete PropertyGrid
    // Car - das zu editierende Objekt
    propertyGrid1.SelectedObject = Car;
    // im Propertygrid 'propertyGrid1' werden nun die Properties 'Tyres', 'Manufacturer' und 'CarImage'
    // in den jeweiligen Gruppen 'Allgemein' und 'Grafik' angezeigt
    // für die Auswahl der Property 'CarImage' wird ein FileDialog bereitgestellt
    // Eingaben werden typabhängig geprüft und in das Objekt zurückgeschrieben
    

Eine detailliertere Beschreibung für die Verwendung eines PropertyGrids findet man bei:

Using PropertyGrid control in .NET
Customized display of collection data in a PropertyGrid