Tuesday, August 7, 2012

Raize Components Makes Id/Value Pair Logic A Breeze

I have known about Raize Components for a long time. The buzz I've always heard was something like this... "Oh yeah, Raize is good if you want a nice consistent look to all of your components within your program."

That's not really very exciting when you are thinking about spending $400.

Then I watched a video from Code Rage 5 where Ray demonstrates and talks about his components. Wow, did this change my mind. I purchased Raize Components 6.0 three days later. Here is the Code Rage 5 video I'm talking about UI Design with Raize Components.

There's also an XE2 Code Rage 6 video UI Design with Raize Components and RAD Studio XE2

You owe it to yourself to watch one or both of these videos.

Anyway, I have a passion for using Id/Value pairs in all of my lookup code. I find it much easier to pass around an integer Id instead of the actual string value. I guess this comes from years of creating databases with lookup tables based on nonsensical auto-increment Id's.  For example:

tblColors
IdValue
1Blue
2Green
3Red
4Yellow

So, I was very excited when I learned that the TRzComboBox component from Raize supports Id/Value Pairs.  You just right click on the control, choose Edit Items & Values and enter your Id value pairs.
 
When you want to retrieve the Value associated with the ItemIndex of the control you simply reference the TRzComboBox.Value property.

Okay that was the easy part. Now how do you go about doing the opposite, setting the control so the ItemIndex refletcs the Id value? That's a good question.

Suppose we have the value 7 which represents "Seven" stored in an interger variable and we want to set the control so that Seven is the highlighted item.


You simply iterate through the control testing each of the values to see if it matches. Upon a match you set the ItemIndex.

Raize makes this easy beacause it exposes both the Value property and the Values property. Value corresponds to the ItemIndex and Values is a TStringList of all the values.

Below is a simple little program to demonstrate how to do set the ItemIndex to the Value. It does require Raize Componets 6.0 from http://www.raize.com/

The key is a little helper procedure that accepts two parameters. The first is a TRzComboBox, and the second is the Id value. By passing the TRzComboBox by reference it allows any changes to the ItemIndex to happen directly to the control itself.

Procedure SetComboBoxValue(var cb:TRzComboBox; x:integer);
var i :integer;
begin
  for i := 0 to cb.Count - 1 do
    if x = StrToInt(cb.Values[i]) then
    begin
      cb.ItemIndex := i;
      Exit;
    end;
end;
As you can see, it simply iterates through the list of values looking for a match and when found it Exits.

Enjoy,
Semper Fi - Gunny Mike

 UPDATE 08/08/2012  9:25 PM EST

I just received an email from Ray Konopka regarding TRzComboBox.Value property. It turns out that the Value property is a read/write property. That means you can assign a value to the Value property and it will change the ItemIndex property automatically! How simple is that...

RzComboBox1.Value := IntToStr(7);

Thanks Ray and keep up the great work.




object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 203
  ClientWidth = 226
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object RzButton1: TRzButton
    Left = 64
    Top = 112
    Width = 110
    Caption = 'Value = 1'
    TabOrder = 0
    OnClick = RzButton1Click
  end
  object RzComboBox1: TRzComboBox
    Left = 64
    Top = 32
    Width = 110
    Height = 21
    TabOrder = 1
    Text = 'One'
    OnChange = RzComboBox1Change
    Items.Strings = (
      'One'
      'Two'
      'Three'
      'Four'
      'Five'
      'Six'
      'Seven'
      'One Hundred'
      'Nine Nine Nine')
    ItemIndex = 0
    Values.Strings = (
      '1'
      '2'
      '3'
      '4'
      '5'
      '6'
      '7'
      '100'
      '999')
  end
end

unit SetComboBox;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
  Forms, Dialogs, StdCtrls, RzCmboBx, RzButton;

type
  TForm1 = class(TForm)
    RzButton1: TRzButton;
    RzComboBox1: TRzComboBox;
    procedure RzButton1Click(Sender: TObject);
    procedure RzComboBox1Change(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  Procedure SetComboBoxValue(var cb:TRzComboBox; x:integer);
var
  Form1: TForm1;

implementation

{$R *.dfm}

Procedure SetComboBoxValue(var cb:TRzComboBox; x:integer);
var i :integer;
begin
  for i := 0 to cb.Count - 1 do
    if x = StrToInt(cb.Values[i]) then
    begin
      cb.ItemIndex := i;
      Exit;
    end;
end;

procedure TForm1.RzButton1Click(Sender: TObject);
var
  i,j : integer;
begin
  i := Random(RzComboBox1.Count);
  j := StrToInt(RzComboBox1.Values[i]);
  RzButton1.Caption := 'Value = ' + IntToStr(j);
  SetComboBoxValue(RzComboBox1,j);
end;

procedure TForm1.RzComboBox1Change(Sender: TObject);
begin
  RzButton1.Caption := 'Value = ' + RzComboBox1.Value;
end;

end.

8 comments:

  1. It's shorter :)

    cb.ItemIndex := cb.Values.IndexOf(IntToStr(x));

    but have to know, that if "x" isn't in Values, then cb.ItemIndex will be -1

    ReplyDelete
  2. XCluster is right and you could do that in a class helper for the raize component.

    Something like that:


    TRzComboBoxHelper = class helper for TRzComboBox
    public
    procedure SelectItem(Value: Integer);
    end;


    procedure TRzComboBoxHelper.SelectItem(Value: Integer);
    const
    IndexNotFound = -1;
    SItemNotFoundError = 'The item "%d" was not found in the list.';
    var
    FoundedIndex: Integer;
    begin
    FoundedIndex := Self.IndexOf(Value);
    if FoundedIndex = -1 then
    Self.ItemIndex := FoundedIndex
    else
    raise Exception.CreateFmt(SItemNotFoundError, [Value]);
    end;

    Hope it helps :)

    Regards,
    Diego Garcia

    ReplyDelete
    Replies
    1. if FoundedIndex > -1 then
      :)

      Delete
    2. Thanks for the correction xcluster !

      I've quick typed the code here :)

      Regards
      Diego Garcia

      Delete
    3. XCluster the right should be:

      if FoundedIndex <> IndexNotFound then
      Self.ItemIndex := FoundedIndex
      else
      raise Exception.CreateFmt(SItemNotFoundError, [Value]);

      Regards,
      Diego Garcia

      Delete
  3. And this feature does also work in the TRzDbCombobox, where you can have cryptic values in the database and nice verbose text displayed to the user. Very handy whan you want to have a DB combobox showing its items in the current UI language, while the database content keeps unchanged.

    ReplyDelete
  4. Very nice. As a nice side effect the raize components generally fix a lot of weird glitches that I otherwise experienced in the VCL. A simple example is that the Page Control in regular VCL, which is simply a wrapper for the MS Common Controls library does not override the default painting code for Tabs when they are positioned on the bottom. Since Windows itself does a bad job (paints the tabs the wrong way around), and since the VCL doesn't work around it, you have to add owner-draw code to ALL your page controls if you want them to look okay, on Windows Vista or Windows 7 with the tabs positioned at the bottom.

    With the Raize page controls (or the Jedi ones, or any other decent tab control), things like this are taken care of for you. And then you can get the funky page/tab shapes that the Raize tab control has. Very nice.

    Ray Konopka is a super nice guy and has been a member of the Delphi community since forever. Some of the newcomers might not remember that one of his contributions to the community was the first and only book for Delphi focused on component development. It was for a now-ancient delphi version, but remains the definitive work on the subject. Any aspiring delphi hacker must acquire a copy of his book.

    W

    ReplyDelete
  5. I just received an email from Ray Konopka regarding the Value property. As it turns out the Value property is a read/write property. So, you can assign a value to the Value property and the ItemIndex will automatically change. How simple is that.

    RzComboBox1.Value := IntToStr(7);

    Thanks Ray and keep up the great work.

    Gunny Mike

    ReplyDelete