The following example provides a property sheet containing all the available controls, including user-defined properties of a custom component.

Enter the following text into the IDL Editor:

; Property Sheet Demo
;
; This program contains these sections of code:
;
; (1) Definition of the IDLitTester class which is used to configure and
;     populate the property sheet.
; (2) Methods for handling the user-defined data type.
; (3) Event handlers and main widget program.
 
;================================================
; Definition of the IDLitTester class.
 
;------------------------------------------------
; IDLitTester
;
; Superclasses:
; IDLitComponent
;
; Subclasses:
; none
;
; Interfaces:
; IIDLProperty
;
; Intrinsic Methods:
; none (because it contains no objects)
 
;------------------------------------------------
; IDLitTester::Init
 
FUNCTION IDLitTester::Init, _REF_EXTRA = _extra
 
  COMPILE_OPT IDL2
   
  ; Initialize the superclass.
  IF (self->IDLitComponent::Init() NE 1) THEN $
    RETURN, 0
     
  ; Create IDLitTester.
  ; Nothing to do, for now.
  ; Register properties.
  ;
  ; * Only registered properties will show up in the property sheet.
  ; * <identifier> must match self.<identifier>.
   
  self->RegisterProperty, 'BOOLEAN', /BOOLEAN , $
    NAME = 'Boolean', DESCRIPTION = 'TRUE or FALSE'
   
  self->RegisterProperty, 'COLOR', /COLOR, $
    NAME = 'Color', DESCRIPTION = 'Color (RGB)'
   
  self->RegisterProperty, 'USERDEF', USERDEF = '', $
    NAME = 'User Defined', DESCRIPTION = 'User defined property'
   
  self->RegisterProperty, 'NUMBER1', /INTEGER , $
    NAME = 'Integer', DESCRIPTION = 'Integer in [-100, 100]', $
    valid_range = [-100, 100]
   
  self->RegisterProperty, 'NUMBER2', /FLOAT, $
    NAME = 'Floating Point', DESCRIPTION = 'Number trackbar', $
    valid_range = [-19.0D, 6.0D, 0.33333333333333D]
   
  self->RegisterProperty, 'NUMBER3', /FLOAT, $
    NAME = 'Floating Point', $
    DESCRIPTION = 'Double in [-1.0, 1.0]', $
    valid_range = [-1.0D, 1.0D]
   
  self->RegisterProperty, 'LINESTYLE', /LINESTYLE, $
    NAME = 'Line Style', DESCRIPTION = 'Line style'
   
  self->RegisterProperty, 'LINETHICKNESS', /THICKNESS , $
    NAME = 'Line Thickness', $
    DESCRIPTION = 'Line thickness (pixels)'
   
  self->RegisterProperty, 'STRING1', /STRING , $
    NAME = 'String', DESCRIPTION = 'Just some text'
   
  self->RegisterProperty, 'STRINGLIST', $
    NAME = 'String List', DESCRIPTION = 'Enumerated list', $
    enumlist = ['ant', 'bat', 'cat', 'dog', 'emu', $
      'fox', 'hog', 'owl', 'pig', 'rat']
   
  self->RegisterProperty, 'SYMBOL', /SYMBOL , $
    NAME = 'Symbol', DESCRIPTION = 'Symbol of some sort'
   
  ; Set any property values.
  self->SetProperty, _EXTRA = _extra
   
  RETURN, 1
   
END
 
;------------------------------------------------
; IDLitTester::Cleanup
PRO IDLitTester::Cleanup
 
  COMPILE_OPT IDL2
   
  self->IDLitComponent::Cleanup
   
END
 
;------------------------------------------------
; IDLitTester::GetProperty
; Implemention for IIDLProperty interface
 
PRO IDLitTester::GetProperty, $
  boolean = boolean, $
  color = color, $
  userdef = userdef, $
  font = font, $
  number1 = number1, $
  number2 = number2, $
  number3 = number3, $
  linestyle = linestyle, $
  linethickness = linethickness, $
  string1 = string1, $
  stringlist = stringlist, $
  symbol = symbol, $
  _REF_EXTRA = _extra
   
  COMPILE_OPT IDL2
   
  IF (ARG_PRESENT(boolean)) THEN boolean = self.boolean
  IF (ARG_PRESENT(color)) THEN color = self.color
  IF (ARG_PRESENT(userdef)) THEN userdef = self.userdef
  IF (ARG_PRESENT(font)) THEN font = self.font
  IF (ARG_PRESENT(number1)) THEN number1 = self.number1
  IF (ARG_PRESENT(number2)) THEN number2 = self.number2
  IF (ARG_PRESENT(number3)) THEN number3 = self.number3
  IF (ARG_PRESENT(linestyle)) THEN linestyle = self.linestyle
  IF (ARG_PRESENT(linethickness)) $
    THEN linethickness = self.linethickness
  IF (ARG_PRESENT(string1)) THEN string1 = self.string1
  IF (ARG_PRESENT(stringlist)) THEN stringlist = self.stringlist
  IF (ARG_PRESENT(symbol)) THEN symbol = self.symbol
   
  ; Superclass' properties:
  IF (N_ELEMENTS(_extra) gt 0) THEN $
    self->IDLitComponent::GetProperty, _EXTRA = _extra
     
END
;------------------------------------------------
; IDLitTester::SetProperty
; Implementation for IIDLProperty interface
 
PRO IDLitTester::SetProperty, $
  boolean = boolean, $
  color = color, $
  userdef = userdef, $
  font = font, $
  number1 = number1, $
  number2 = number2, $
  number3 = number3, $
  linestyle = linestyle, $
  linethickness = linethickness, $
  string1 = string1, $
  stringlist = stringlist, $
  symbol = symbol, $
  _REF_EXTRA = _extra
   
  COMPILE_OPT IDL2
   
  IF (N_ELEMENTS(boolean) ne 0) THEN self.boolean = boolean
  IF (N_ELEMENTS(color) NE 0) THEN self.color = color
  IF (N_ELEMENTS(userdef) NE 0) THEN self.userdef = userdef
  IF (N_ELEMENTS(font) NE 0) THEN self.font = font
  IF (N_ELEMENTS(number1) NE 0) THEN self.number1 = number1
  IF (N_ELEMENTS(number2) NE 0) THEN self.number2 = number2
  IF (N_ELEMENTS(number3) NE 0) THEN self.number3 = number3
  IF (N_ELEMENTS(linestyle) NE 0) THEN self.linestyle = linestyle
  IF (N_ELEMENTS(linethickness) NE 0) THEN $
    self.linethickness = linethickness
  IF (N_ELEMENTS(string1) NE 0) THEN self.string1 = string1
  IF (N_ELEMENTS(stringlist) NE 0) THEN self.stringlist = stringlist
  IF (N_ELEMENTS(symbol) NE 0) THEN self.symbol = symbol
   
  self->IDLitComponent::SetProperty, _EXTRA = _extra
   
END
 
;------------------------------------------------
; IDLitTester__Define
 
PRO IDLitTester__Define
 
  COMPILE_OPT IDL2, HIDDEN
   
  struct = {$
    IDLitTester, $
    INHERITS IDLitComponent, $
    boolean:0L, $
    color:[0B,0B,0B], $
    userdef:"", $
    number1:0L, $
    number2:0D, $
    number3:0D, $
    linestyle:0L, $
    linethickness:0L, $
    string1:"", $
    stringlist:0L, $
    symbol:0L $
  }
   
END
 
;================================================
; Methods for handling the user-defined data type.
 
;------------------------------------------------
; UserDefEvent
;
; This procedure is the event handler for the user-defined property's
; widget base.  The user has selected a button and here we update the
; component.
 
PRO UserDefEvent, e
 
  IF (TAG_NAMES(e, /STRUCTURE_NAME) eq 'WIDGET_BUTTON') THEN BEGIN
   
    WIDGET_CONTROL, e.top, GET_UVALUE = uvalue
    WIDGET_CONTROL, e.id, GET_UVALUE = numb_ness
     
    propsheet = uvalue.propsheet
    component = uvalue.component
    identifier = uvalue.identifier
     
    ; Set the human readable value.
     
    component->SetPropertyAttribute, identifier, userdef = numb_ness
     
    ; Set the real value of the component.
     
    component->SetPropertyByIdentifier, identifier, numb_ness
    WIDGET_CONTROL, propsheet, REFRESH_PROPERTY = identifier
    PRINT, 'Changed: ', uvalue.identifier, ': ', numb_ness
    WIDGET_CONTROL, e.top, /DESTROY
     
  ENDIF
   
END
;------------------------------------------------
; GetUserDefValue
;
; Creates widgets used to modify the user defined property's
; value. The value is actually set in UserDefEvent.
 
PRO GetUserDefValue, e
 
  base = WIDGET_BASE(/ROW, TITLE = 'Pick a Number', $
    /MODAL, GROUP_LEADER = e.top)
     
  one = WIDGET_BUTTON(base, VALUE = 'one', UVALUE = 'oneness')
  two = WIDGET_BUTTON(base, VALUE = 'two', UVALUE = 'twoness')
  six = WIDGET_BUTTON(base, VALUE = 'six', UVALUE = 'sixness')
  ten = WIDGET_BUTTON(base, VALUE = 'ten', UVALUE = 'tenness')
   
  ; We will need this info when we set the value.
   
  WIDGET_CONTROL, base, $
    SET_UVALUE = {propsheet:e.id, $
    component:e.component, $
    identifier:e.identifier}
     
  WIDGET_CONTROL, base, /REALIZE
   
  XMANAGER, 'UserDefEvent', base, EVENT_HANDLER = 'UserDefEvent'
 
END
 
;================================================
; Event handlers and main widget program.
 
;------------------------------------------------
; prop_event
;
; The property sheet generates an event whenever the user changes
; a value. The event holds the property's identifier and type, and
; an object reference to the component.
;
; Note: Calling "widget_control, e.id, get_value = objref" also
; retrieves an object reference to the component.
 
PRO prop_event, e
 
  IF (e.type EQ 0) THEN BEGIN ; Value changed
   
    ; Get the value of the property identified by e.identifier.
     
    IF (e.proptype NE 0) THEN BEGIN
     
      ; Get the value from the property sheet.
      value = widget_info(e.id, PROPERTY_VALUE = e.identifier)
       
      ; Set the component's property's value.
      e.component->SetPropertyByIdentifier, e.identifier, value
       
      ; Print the change in the component's property value.
      PRINT, 'Changed ', e.identifier, ': ', value
   
    ENDIF ELSE BEGIN
   
      ; Use alternative means to get the value.
      GetUserDefValue, e
   
    ENDELSE
   
  ENDIF ELSE BEGIN ; selection changed
   
    r = e.component->GetPropertyByIdentifier(e.identifier, value)
    PRINT, 'Selected ' + e.identifier + ': ', value
     
  ENDELSE
   
END
 
;------------------------------------------------
PRO refresh_event, e
 
  WIDGET_CONTROL, e.id, GET_UVALUE = uvalue
   
  uvalue.o->SetProperty, boolean = 0L
  uvalue.o->SetProperty, color = [255, 0, 46]
  uvalue.o->SetPropertyAttribute, 'userdef', USERDEF = "Yeehaw!"
  uvalue.o->SetProperty, number1 = 99L
  uvalue.o->SetProperty, number2 = -13.1
  uvalue.o->SetProperty, number3 = 6.5
  uvalue.o->SetProperty, linestyle = 3L
  uvalue.o->SetProperty, string1 = 'It worked!'
  uvalue.o->SetProperty, stringlist = 8L
  uvalue.o->SetProperty, symbol = 6L
  uvalue.o->SetPropertyAttribute, 'Number1', SENSITIVE = 1
  uvalue.o->SetPropertyAttribute, 'Number2', SENSITIVE = 1
     
  WIDGET_CONTROL, uvalue.prop, $
    REFRESH_PROPERTY = ['boolean', 'color', 'userdef', $
      'number1', 'number2', 'number3', 'linestyle', $
      'string1', 'stringlist', 'symbol']
       
END
 
;------------------------------------------------
 
PRO reload_event, e
 
  WIDGET_CONTROL, e.id, GET_UVALUE = uvalue
  load_values, uvalue.o
  WIDGET_CONTROL, uvalue.prop, SET_VALUE = uvalue.o
  update_state, e.top, 1
   
END
 
;------------------------------------------------
 
PRO hide_event, e
 
  WIDGET_CONTROL, e.id, GET_UVALUE = uvalue
  uvalue.o->SetPropertyAttribute, 'color', /HIDE
  WIDGET_CONTROL, uvalue.prop, REFRESH_PROPERTY = 'color'
   
END
 
;------------------------------------------------
 
PRO show_event, e
 
  WIDGET_CONTROL, e.id, GET_UVALUE = uvalue
  uvalue.o->SetPropertyAttribute, 'color', HIDE = 0
  WIDGET_CONTROL, uvalue.prop, REFRESH_PROPERTY = 'color'
   
END
 
;------------------------------------------------
 
PRO clear_event, e
 
  update_state, e.top, 0
  WIDGET_CONTROL, e.id, GET_UVALUE = uvalue
  WIDGET_CONTROL, uvalue.prop, SET_VALUE = OBJ_NEW()
   
END
 
;------------------------------------------------
; propsheet_demo_event
;
; Handles resize events for the property sheet demo program.
 
PRO propsheet_demo_event, e
 
  WIDGET_CONTROL, e.id, GET_UVALUE = base
   
  geo_tlb = WIDGET_INFO(e.id, /GEOMETRY)
   
  WIDGET_CONTROL, base.prop, $
    SCR_XSIZE = geo_tlb.xsize - (2*geo_tlb.xpad), $
    SCR_YSIZE = geo_tlb.ysize - (2*geo_tlb.ypad)
     
END
 
;------------------------------------------------
; sensitivity_event
;
; Procedure to test sensitizing and desensitizing.
 
PRO sensitivity_event, e
 
  WIDGET_CONTROL, e.id, GET_UVALUE = uvalue, GET_VALUE = value
   
  b = value ne 'Desensitize'
   
  uvalue.o->SetPropertyAttribute, 'Boolean', sensitive = b
  uvalue.o->SetPropertyAttribute, 'Color', sensitive = b
  uvalue.o->SetPropertyAttribute, 'UserDef', sensitive = b
  uvalue.o->SetPropertyAttribute, 'Number1', sensitive = b
  uvalue.o->SetPropertyAttribute, 'Number2', sensitive = b
  uvalue.o->SetPropertyAttribute, 'Number3', sensitive = b
  uvalue.o->SetPropertyAttribute, 'LineStyle', sensitive = b
  uvalue.o->SetPropertyAttribute, 'LineThickness', sensitive = b
  uvalue.o->SetPropertyAttribute, 'String1', sensitive = b
  uvalue.o->SetPropertyAttribute, 'StringList', sensitive = b
  uvalue.o->SetPropertyAttribute, 'Symbol', sensitive = b
   
  WIDGET_CONTROL, uvalue.prop, $
    REFRESH_PROPERTY = ['Boolean', 'Color', 'UserDef', $
      'Number1', 'Number2', 'Number3', 'LineStyle', $
      'LineThickness', 'String1', 'StringList', 'Symbol']
       
END
 
;------------------------------------------------
PRO load_values, o
 
  o->SetProperty, boolean = 1L             ; 0 or 1
  o->SetProperty, color = [200, 100, 50]   ; RGB
  o->SetPropertyAttribute, 'userdef', USERDEF = ""  ; to be set later
  o->SetProperty, number1 = 42L            ; integer
  o->SetProperty, number2 = 0.0            ; double
  o->SetProperty, number3 = 0.1            ; double
  o->SetProperty, linestyle = 4L           ; 5th item (zero based)
  o->SetProperty, linethickness = 4L       ; pixels
  o->SetProperty, string1 = "This is a string."
  o->SetProperty, stringlist = 3L          ; 4th item in list
  o->SetProperty, symbol = 4L              ; 5th symbol in list
   
END
 
;------------------------------------------------
 
PRO quit_event, e
 
  WIDGET_CONTROL, e.top, /DESTROY
   
END
 
;------------------------------------------------
; update_state
 
PRO update_state, top, sensitive
 
  WIDGET_CONTROL, top, GET_UVALUE = uvalue
   
  FOR i = 0, N_ELEMENTS(uvalue.b) - 1 do $
    WIDGET_CONTROL, uvalue.b[i], SENSITIVE = sensitive
     
END
 
;------------------------------------------------
; propsheet_demo
 
PRO propsheet_demo
 
  ; Create and initialize the component.
   
  o = OBJ_NEW('IDLitTester')
   
  load_values, o
   
  ; Create some widgets.
   
  base = WIDGET_BASE(/COLUMN, /TLB_SIZE_EVENT, $
    TITLE = 'Property Sheet Demo')
     
  prop = WIDGET_PROPERTYSHEET(base, VALUE = o, $
    YSIZE = 13, /FRAME, EVENT_PRO = 'prop_event')
   
  b1 = WIDGET_BUTTON(base, VALUE = 'Refresh', $
    UVALUE = {o:o, prop:prop}, $
    EVENT_PRO = 'refresh_event')
   
  b2 = WIDGET_BUTTON(base, VALUE = 'Reload', $
    UVALUE = {o:o, prop:prop}, $
    EVENT_PRO = 'reload_event')
   
  b3 = WIDGET_BUTTON(base, VALUE = 'Hide Color', $
    UVALUE = {o:o, prop:prop}, $
    EVENT_PRO = 'hide_event')
   
  b4 = WIDGET_BUTTON(base, VALUE = 'Show Color', $
    UVALUE = {o:o, prop:prop}, $
    EVENT_PRO = 'show_event')
   
  b5 = WIDGET_BUTTON(base, VALUE = 'Clear', $
    UVALUE = {o:o, prop:prop}, $
    EVENT_PRO = 'clear_event')
   
  b6 = WIDGET_BUTTON(base, VALUE = 'Desensitize', $
    UVALUE = {o:o, prop:prop}, $
    EVENT_PRO = 'sensitivity_event')
   
  b7 = WIDGET_BUTTON(base, VALUE = 'Sensitize', $
    UVALUE = {o:o, prop:prop}, $
    EVENT_PRO = 'sensitivity_event')
   
  b8 = WIDGET_BUTTON(base, VALUE = 'Quit', $
    EVENT_PRO = 'quit_event')
   
  ; Note buttons that can't be pushed after clearing.
   
  b = [b1, b3, b4, b5, b6, b7]
   
  ; Activate the widgets.
   
  WIDGET_CONTROL, base, SET_UVALUE = {prop:prop, b:b}, /REALIZE
   
  XMANAGER, 'propsheet_demo', base, /NO_BLOCK
   
END

The following figure displays the output of this example, as user-defined property sheet:

To demonstrate the controls available from the WIDGET_PROPERTYSHEET, do the following and note the Selected and Changed messages in the IDL Output Log:

  • Change the Boolean field to False.
  • Select a new Color from the color picker.
  • Select a new “numberness” value in the User Defined field.
  • Change the Integer field to a new value. Note that this field has been restricted to integers in the range -100 to 100.
  • Change the first Floating Point field to a new value by moving the slider.
  • Change the second Floating Point field to a new value by editing the text. Note that this field has been restricted to floating point numbers in the range -1.0 to 1.0.
  • Change the Line Style field to a new style.
  • Change the Line Thickness field to a new thickness.
  • Select a new symbol in the Symbol field.
  • Select a new string from the String List.

Click the eight buttons at the bottom of the property sheet to initiate the following events:

  • The Refresh button loads the data specified in refresh_event into the property sheet, using the REFRESH_PROPERTY keyword to WIDGET_CONTROL.
  • The Reload button reloads the data specified in LoadValues into the property sheet, using the SET_VALUE keyword to WIDGET_CONTROL.
  • The Hide Color button runs hide_event, which sets the HIDE attribute for the color property to one.
  • The Show Color button runs show_event, which sets the HIDE attribute for the color property to zero.
  • The Clear button runs clear_event, which creates a new set of empty objects, deactivating all but the Reload button.
  • The Desensitize button runs sensitivity_event, which deactivates the displayed fields.
  • The Sensitize button runs sensitivity_event, which reactivates the displayed fields.
  • The Quit button runs quit_event, which destroys the top-level base and ends the program.