Autor Beitrag
eXo
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Mo 19.01.04 17:42 
ich versuche eine neue Komponente zu erzeugen, ein TPageControl mit 3 Pages (TTabSheets) und verschiedenen Controls darauf. Mein Problem ist nun dass ich eine Exception bekomme nachdem ich die eigentliche Haupt-Form (die mit der Komponente drauf) starte:
'Exception EClass not found TTabSheet'

Sobald ich ein weiteres PageControl auf die Haupt-Form ziehe (mit mindestens 1 TabSheet) klappt die Sache.

Hier der Source-Code (vereinfacht) meiner Komponente:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
// Sourcecode begins here

unit NewPageControl;

interface

uses
 Windows, Messages, SysUtils, Classes, Controls, ComCtrls;

type
 TNewPageControl = class(TPageControl)
 private
 FTabSheet:TTabSheet;
 protected
   procedure CreateWnd; override;
 public
   constructor Create(AOwner: TComponent); override;
   destructor  Destroy; override;
 published
 end;

procedure Register;

implementation

procedure Register;
begin
 RegisterComponents('NewPageControl', [TNewPageControl]);
end;

constructor TNewPageControl.Create(AOwner: TComponent);
begin
 inherited Create(AOwner);
 FTabSheet := TTabSheet.Create(Self);
end;

destructor TNewPageControl.Destroy;
begin
 inherited Destroy;
end;

procedure TNewPageControl.CreateWnd;
begin
 inherited CreateWnd;
 FTabSheet.Caption := 'NewTabSheet1';
 FTabSheet.PageControl := Self;
end;

end.

// Sourcecode ends here


regards,
K.Boldt

Moderiert von user profile iconMotzi: Delphi-Tags hinzugefügt
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Mo 19.01.04 18:43 
Wieso überschreibst du denn die Methode CreateWnd? Es reicht schon, wenn du die Eigenschaften direkt nach dem Erzeugen zuweißt...
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
unit NewPageControl; 

interface 

uses 
 Windows, Messages, SysUtils, Classes, Controls, ComCtrls; 

type 
 TNewPageControl = class(TPageControl) 
 private 
 FTabSheet:TTabSheet; 
 protected 
 public 
   constructor Create(AOwner: TComponent); override
   destructor  Destroy; override
 published 
 end

procedure Register

implementation 

procedure Register
begin 
 RegisterComponents('NewPageControl', [TNewPageControl]); 
end

constructor TNewPageControl.Create(AOwner: TComponent); 
begin 
 inherited Create(AOwner); 
 FTabSheet := TTabSheet.Create(Self); 
 FTabSheet.Caption := 'NewTabSheet1'
 FTabSheet.PageControl := Self; 
end

destructor TNewPageControl.Destroy; 
begin 
 inherited Destroy; 
end

end.

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
eXo Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Mo 19.01.04 19:29 
Titel: geht nicht?
ganau das war mein erster Ansatz.

so bekomme ich nur beim draufziehen (auf meine form) der komponente die exception:

Element '' hat kein übergeordnetes Fenster.

wenn ich das TabSheet in einer Init-Routine (procedure innerhalb der komponente) erzeuge und diese vom main-from aufrufe klappts wunderbar. Das ist aber auch keine loesung fuer mein problem ich moechte die 3 tabsheets schon beim editieren sichtbar haben und nicht erst zur runtime.

gruesse,
Karsten
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Mo 19.01.04 19:42 
Du musst auch noch den Parent des TabSheets setzen...

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
eXo Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Mo 19.01.04 20:09 
Titel: hab ich
hab ich

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
// Schnipsel beginnt hier

constructor TNewPageControl.Create(AOwner: TComponent);  
begin  
 inherited Create(AOwner);  
 FTabSheet := TTabSheet.Create(Self);  
 FTabSheet.Parent := Self;
 FTabSheet.Caption := 'NewTabSheet1';
 FTabSheet.PageControl := Self;
end;

// Schnipsel endet hier


Moderiert von user profile iconPeter Lustig: Delphi-Tags hinzugefügt
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 20.01.04 10:14 
Ich fürchte das Problem liegt daran, dass das TPagenControl selbst zu diesem Zeitpunkt noch keinen Parent hat... du kannst aber die Methode SetParent überschreiben und die entsprechenden Eigenschaften, die einen Parent erfordern, erst dort setzen...

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
eXo Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Di 20.01.04 10:58 
Titel: ????
vielleicht koennte sich doch mal jemand die muehe machen und die sache in seinem delphi ausprobieren.

nachdem ich also folgendes versucht habe:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
unit NewPageControl;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, ComCtrls;

type
  TNewPageControl = class(TPageControl)
  private
   FTabSheet:TTabSheet;
   FTabSheetCreated: Boolean;
  protected
    procedure SetParent(Parent: TWinControl); override;
    procedure CreateCustomTabs;
  public
    constructor Create(AOwner: TComponent); override;
    destructor  Destroy; override;
  published
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('NewPageControl', [TNewPageControl]);
end;

procedure TNewPageControl.SetParent(Parent: TWinControl);
begin
  Inherited;
  if csLoading in ComponentState then
    FTabSheetCreated := True;
  if (Parent <> niland not FTabSheetCreated then
    CreateCustomTabs;
end;

procedure TNewPageControl.CreateCustomTabs;
begin
  FTabSheetCreated := True;

  FTabSheet := TTabSheet.Create(Self);
  FTabSheet.Caption := 'TabSheet1';
  FTabSheet.PageControl := Self;
end;

constructor TNewPageControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;

destructor TNewPageControl.Destroy;
begin
  inherited Destroy;
end;
end.

habe ich wieder das problem wie anfangs beschrieben "EClassNotFound TTabSheet" beim Start des main-progs (welches die Komponente enthält)

sobald ich ein weiteres PageControl (inklusive min. 1 TabSheet) auf meinem main-form platziere klappts (wie oben beschrieben)!

PLEASE HELP!

Moderiert von user profile iconTino: Delphi-Tags hinzugefügt.
eXo Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Di 20.01.04 11:05 
Titel: Ok Lösung!
Ok habs nun selbst rausgefunden (war langsam komplett verblendet in der Sache) :
ausblenden Delphi-Quelltext
1:
2:
initialization
 RegisterClass(TTabSheet);

und es klappt

Danke Motzi fuer diverse Denkanstösse!

hier nun der Final-Code (fuer die FAQs?!):

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
unit NewPageControl;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, ComCtrls;

type
  TNewPageControl = class(TPageControl)
  private
   FTabSheet:TTabSheet;
   FTabSheetCreated: Boolean;
  protected
    procedure SetParent(Parent: TWinControl); override;
    procedure CreateCustomTabs;
  public
    constructor Create(AOwner: TComponent); override;
    destructor  Destroy; override;
  published
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('NewPageControl', [TNewPageControl]);
end;

procedure TNewPageControl.SetParent(Parent: TWinControl);
begin
  Inherited;
  if csLoading in ComponentState then
    FTabSheetCreated := True;
  if (Parent <> niland not FTabSheetCreated then
    CreateCustomTabs;
end;

procedure TNewPageControl.CreateCustomTabs;
begin
  FTabSheetCreated := True;

  FTabSheet := TTabSheet.Create(Self);
  FTabSheet.Caption := 'TabSheet1';
  FTabSheet.PageControl := Self;
end;

constructor TNewPageControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;

destructor TNewPageControl.Destroy;
begin
  inherited Destroy;
end;

initialization
 RegisterClass(TTabSheet);

end.


Moderiert von user profile iconTino: Delphi-Tags hinzugefügt.
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 20.01.04 12:03 
Titel: Re: Ok Lösung!
eXo hat folgendes geschrieben:
Ok habs nun selbst rausgefunden (war langsam komplett verblendet in der Sache) :
ausblenden Delphi-Quelltext
1:
2:
initialization
 RegisterClass(TTabSheet);

und es klappt

Es reicht bereits RegisterClass() in der normalen Register-Procedure aufzurufen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
procedure Register;
begin
  RegisterComponents('NewPageControl', [TNewPageControl]);
  RegisterClass(TTabSheet);
end;


Zitat:
Danke Motzi fuer diverse Denkanstösse!

Keine Ursache..! Jederzeit wieder...! ;)

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
eXo Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Di 20.01.04 12:29 
Titel: no mam!
nein geht definitiv nicht dann (gleiches problem wie oben beschrieben) findet er die Klasse nicht.

habe jetzt aber noch ein anderes problem festgestellt folgender code:

ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
// Code starts here

unit NewPageControl;

interface

uses
  Windows, Messages, SysUtils, Classes, Controls, ComCtrls, StdCtrls;

type
  TNewPageControl = class(TPageControl)
  private
   FTabSheet:TTabSheet;
   FTabSheet2:TTabSheet;
   FButton : TButton;
   FButton2 : TButton;
   FTabSheetCreated: Boolean;
   FControlsCreated: Boolean;
  protected
    procedure SetParent(Parent: TWinControl); override;
    procedure CreateCustomTabs;
    procedure CreateControls;
  public
    constructor Create(AOwner: TComponent); override;
    destructor  Destroy; override;
  published
  end;

procedure Register;

implementation

procedure Register;
begin
  RegisterComponents('NewPageControl', [TNewPageControl]);
end;

procedure TNewPageControl.SetParent(Parent: TWinControl); 
begin 
  Inherited
  if csLoading in ComponentState then 
    FTabSheetCreated := True; 
  if (Parent <> niland not FTabSheetCreated then 
    CreateCustomTabs; 
end

procedure TNewPageControl.CreateCustomTabs;
begin
  FTabSheetCreated := True;
  FTabSheet := TTabSheet.Create(Self);
  FTabSheet.Caption := 'TabSheet1';
  FTabSheet.PageControl := Self;
  FTabSheet.Visible := true;
  Self.ActivePage := FTabSheet;

  FButton := TButton.Create(Self);
  FButton.Parent := FTabSheet;
  FButton.Visible := true;
  FButton.Width := 200;
  FButton.Caption := 'Hallo';

  FTabSheet2 := TTabSheet.Create(Self);
  FTabSheet2.Caption := 'TabSheet1';
  FTabSheet2.PageControl := Self;
  FTabSheet2.Visible := true;
  Self.ActivePage := FTabSheet2;

  FButton2 := TButton.Create(Self);
  FButton2.Parent := FTabSheet2;
  FButton2.Visible := true;
  FButton2.Width := 200;
  FButton2.Caption := 'Hallo2';
end;

procedure TNewPageControl.CreateControls;
begin
  FControlsCreated := True;
  FButton := TButton.Create(Self);
  FButton.Parent := FTabSheet;
  FButton.Visible := true;
  FButton.Width := 200;
  FButton.Caption := 'Hallo';
  FTabSheet.Visible := true;
  Self.ActivePage := FTabSheet;
end;

constructor TNewPageControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;

destructor TNewPageControl.Destroy;
begin
  inherited Destroy;
end;

initialization
 RegisterClass(TTabSheet);

end.

// Code ends here


FButton und FButton2 erscheinen nur im IDE!


Folgende Änderung zum Vergleich:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
// Schnipsel starts here 

procedure TNewPageControl.SetParent(Parent: TWinControl); 
begin 
  Inherited
//  if csLoading in ComponentState then 
//    FTabSheetCreated := True; 
  if (Parent <> niland not FTabSheetCreated then 
    CreateCustomTabs; 
end;

// Schnipsel ends here


ergibt:
im IDE -> alles normal
zur Laufzeit -> FTabSheet + FTabSheet2 jeweils 2x vorhanden jedoch nur 1x je mit Button

Moderiert von user profile iconMotzi: Delphi-Tags hinzugefügt
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 20.01.04 12:43 
Vorweg erstmal - benutz doch in Zukunft bitte die Delphi-Tags um deinen Code zu formatieren. Einfach am Anfang [ delphi ] bzw am Ende [ /delphi ] (natürlich ohne Leerzeichen dazwischen).

Zitat:
nein geht definitiv nicht dann (gleiches problem wie oben beschrieben) findet er die Klasse nicht.

Komisch.. bei mir hat das immer funktioniert.. :?

Probiers mal so:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
constructor TNewPageControl.Create(AOwner: TComponent); 
begin 
  inherited Create(AOwner); 
  FTabSheetCreated := False;
end

procedure TNewPageControl.SetParent(Parent: TWinControl);  
begin  
  Inherited;  
  if (Parent <> niland not FTabSheetCreated then  
  begin
    FTabSheetCreated := True;
    CreateCustomTabs; 
  end
end;

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
eXo Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Di 20.01.04 12:57 
Titel: geht auch nicht
vorweg: das mit den delphi-tags geht klar, habe direkt nach meinem letzten post gemerkt dass er das wohl nicht von alleine macht ;-) (wie auch) war gerade stoebern wie das geht als dein post kam

jetzt zum pagecontrol-prob: klappt auch nicht habe wenn ich deinem letzten vorschlag folge ebenso 4 tabsheets (2 mit 2 ohne buttons) zur runtime.

regards,
karsten
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Di 20.01.04 13:09 
Hm.. kann das momentan leider nicht testen weil ich kein Delphi zur Verfügung hab... werd das dann zu Hause machen, wenn ich Zeit hab....

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
eXo Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Di 20.01.04 14:19 
Titel: danke
ok thanks bin für jede mithilfe dankbar, zudem scheint das ja auch ein interessantes problem zu sein welches sich nur in verbindung mit pagecontrol+tabsheets stellt (tedit+tbutton geht z.b. ohne probleme)
Motzi
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2931

XP Prof, Vista Business
D6, D2k5-D2k7 je Prof
BeitragVerfasst: Mi 21.01.04 12:13 
Ok... ich habs! 8)

Das Problem ist, dass die IDE die TabSheets in der DFM-Datei gespeichert hat und sie daher beim Laden der Komponente aus dem Stream auch erzeugt hat. Du erzeugst sie aber auch nochmal, womit du dann insgesamt 4 TabSheets hast. Da die Buttons aber nicht in der DFM-Datei gespeichert werden, haben die zwei von der IDE gespeicherten Tabs aber keine Buttons...

Das ganze lässt sich mit einem kleinen "Hack" umgehen, indem wir eine neue Klasse ebenfalls mit dem Namen "TTabSheet" einführen, abgeleitet vom Original-TTabSheet. Damit unser neues PageControl unser TTabSheet nimmt muss es natürlich vor TNewPageControl deklariert werden. In unserem neuen TTabSheet überschreiben wir dann die Methode WriteState, die von der IDE aufgerufen wird und dafür verantwortlich ist, dass sich die Komponente in den Stream reinschreibt. Da wir aber genau das nicht wollen lassen wir die Implementierung der Methode also einfach leer - wir degradieren sie zu einer Dummy-Methode ;)

Der ganze Source schaut bei mir jetzt so aus und funktioniert auch einwandfrei:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
unit NewPageControl;

interface 

uses 
  Windows, Messages, SysUtils, Classes, Controls, ComCtrls, StdCtrls; 

type
  TTabSheet = class(ComCtrls.TTabSheet)
    public
      procedure WriteState(Writer: TWriter); override;
  end;

  TNewPageControl = class(TPageControl) 
    private
      FTabSheet:TTabSheet;
      FTabSheet2:TTabSheet;
      FButton : TButton;
      FButton2 : TButton;
      FTabSheetCreated: Boolean;
    protected
      procedure SetParent(Parent: TWinControl); override;
      procedure CreateCustomTabs;
    public
      constructor Create(AOwner: TComponent); override;
      destructor  Destroy; override;
    published 
  end

procedure Register

implementation 

procedure Register
begin 
  RegisterComponents('NewPageControl', [TNewPageControl]);
end;

{ TTabSheet }

procedure TTabSheet.WriteState(Writer: TWriter);
begin
  // do nothing - don't save to Stream !!
end;

{ TNewPageControl }

constructor TNewPageControl.Create(AOwner: TComponent);
begin
  FTabSheetCreated := False;
  inherited Create(AOwner);
end;

destructor TNewPageControl.Destroy;
begin
  inherited Destroy;
end;

procedure TNewPageControl.SetParent(Parent: TWinControl);
begin  
  inherited;
  if (Parent <> niland not FTabSheetCreated then
  begin
    FTabSheetCreated := True;
    CreateCustomTabs;
  end;
end;  

procedure TNewPageControl.CreateCustomTabs; 
begin
  FTabSheetCreated := True;
  FTabSheet := TTabSheet.Create(Self);
  FTabSheet.Caption := 'TabSheet1';
  FTabSheet.PageControl := Self;
  FTabSheet.Visible := true;
  Self.ActivePage := FTabSheet;

  FButton := TButton.Create(Self);
  FButton.Parent := FTabSheet;
  FButton.Visible := true;
  FButton.Width := 200;
  FButton.Caption := 'Hallo1';

  FTabSheet2 := TTabSheet.Create(Self);
  FTabSheet2.Caption := 'TabSheet2';
  FTabSheet2.PageControl := Self; 
  FTabSheet2.Visible := true; 
  Self.ActivePage := FTabSheet2;

  FButton2 := TButton.Create(Self); 
  FButton2.Parent := FTabSheet2; 
  FButton2.Visible := true; 
  FButton2.Width := 200
  FButton2.Caption := 'Hallo2';   
end;

end.

PS: der RegisterClass-Aufruf musste natürlich auch raus, da wir ja jetzt unsere eigene TTabSheet-Klasse in unsrer Unit haben...

Gruß, Motzi

_________________
gringo pussy cats - eef i see you i will pull your tail out by eets roots!
eXo Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Do 22.01.04 11:15 
Titel: prima -> vielen Dank!
Habe mir diesmal etwas mehr Zeit gelassen um moegliche weitere Probleme mit den auf den TabSheets platzierten Controls auszuschliessen (soll ja im am Ende nicht nur aus Buttons bestehen)

Mit der Reihenvolge der Erstellung muss man noch etwas auspassen (sonst zeichnet er den Inhalt des falschen Tabsheets auf dem PageContrrol (immer den letzt-erstellten)) aber im Prinzip funktioniert jetzt alles bestens.

Vielen Dank!

Gruesse,
Karsten