Entwickler-Ecke

WinForms - Nervendes (nicht)Focus-Verhalten von Form und Button


Sneedlewoods - Do 11.04.13 02:02
Titel: Nervendes (nicht)Focus-Verhalten von Form und Button
Hallo,

1. ich habe eine Form1 erstellt
2. ich habe KeyPreview auf true gestellt
3. ich setze einen Button auf die Form

nun ist das Problem:
1. dass der Button immer den Focus hat
2. CanSelect der Form schreibgeschützt ist und auf false steht
3. CanFocus/CanSelect vom Button schreibgeschützt ist und true ist

Ich möchte gerne, dass der Button gar nicht erst den Fokus erhält, oder dass ich in der Lage bin, der Form den Focus zu geben. Denn sonst müsste ich wohl alle Button-aktivierenden Tasten beim Button abfangen? Der Button soll gar nicht erst über die Tastatur aktiviert werden, was ich wohl noch hinkriegen würde. Lieber wäre mir jedoch die Kontrolle über den Focus.

Ich habe nun schon etwas länger gesucht, und wundere mich, dass es dafür nicht eine einfache Lösung gibt.

Grüße Sneedle


Ralf Jansen - Do 11.04.13 10:42

Button ableiten, CanFocus oder CanSelect (je nachdem was du genau vor hast) überschreiben und false zurückgeben lassen.

Für einen sinnvollen Ratschlag sollten wir aber erstmal wissen warum du das willst was du da willst.


Gausi - Do 11.04.13 11:03

Als Quick&Dirty-Lösung würde mir einfallen, den Button durch ein Panel zu ersetzen. Sieht dann zwar evtl. etwas anders aus und entspricht nicht dem normalen Windows-Verhalten, aber ich denke mal, dass du das sowieso umgehen willst.

Ich denk da so an einen Klick-Wettbewerb, den man nicht durch drücken der Enter-Taste umgehen soll. Liege ich da in etwa richtig? ;-)


Sneedlewoods - Do 11.04.13 13:23

Danke für die Tips.

@Ralf das mit dem Button ableiten hab ich auch überlegt und versucht. Mein Problem dabei ist, dass er CanFocus nicht erkennt:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
public class myButton : Button
{
    public override bool CanFocus ...  //CanFocus wird nicht erkannt, falsche Syntax?

    public myButton()
    {
       this.CanFocus = false;  //CanFocus erkannt aber schreibgeschützt
    }
}


Ansonsten muss man eben nicht wissen, warum ich das will. Das Problem ist da und sucht nach Lösungen, egal warum ich das will.

@Gausi
Ein anderes Control zu nehmen habe ich auch schon überlegt. Würde ich im Notfall vielleicht machen, aber schön find ich das nicht. Ich verstehe nicht, wieso es überhaupt so schwierig sein muss -.-
Nein das ist kein Klick-Wettbewerb ;) Aber das Problem ist das Gleiche. Ich fange Tastatureingaben auf Form1-Ebene ab. Wenn jedoch der Button den Focus hat, wird er aktiviert sobald man Leertaste oder Enter oder sowas drückt, was schlicht und einfach nicht immer sinnvoll ist.


Ralf Jansen - Do 11.04.13 14:39

CanFocus und CanSelect sind nicht virtual :( Tja da war mein Vorschlag dann nicht wirklich hilfreich.

Vielleicht hilft es wenn du im Constructor des abgeleiteten Buttons den Style passend setzt.


C#-Quelltext
1:
this.SetStyle(ControlStyles.Selectable, false);                    


Sneedlewoods - Fr 12.04.13 01:21

Hallo also beim Button funktioniert das so nicht. Eventuell müsste ich das Panel ableiten und SetStyles ändern, aber dass würde bedeuten dass ich alle Labels, Buttons etc. selber in das Panel setzen muss, oder kann ich den Designer irgendwo dafür einspannen? Theoretisch müsste das ja gehen, ist das gleiche Objekt.

Mir gefällt allerdings gerade die Idee besser, die Tastatureingaben für den Button zu sperren. Dann muss ich die ganzen Elemente nicht erneut auf die Form setzen. Aber wie ich es mir dachte funktioniert leider auch nicht -.-


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
Button.KeyPress += new KeyPressEventHandler(this.ButtonNoKey);
Button.KeyUp += new KeyEventHandler(this.ButtonNoKey);

private void ButtonNoKey(object sender, KeyPressEventArgs e)
{
    e.Handled=true;
}
private void ButtonNoKey(object sender, KeyEventArgs e)
{
    e.Handled=true;
}


Das hat leider nicht zum Erfolg geführt. Geht das irgendwie anders?


Sneedlewoods - Fr 12.04.13 01:31

OMG, auf meiner endlosen Suche nach einer Lösung, habe ich in einem anderen Forum einen beiläufigen Satz gefunden, der den Focus eines normalen Buttons verhindert!


C#-Quelltext
1:
TabStop = false;                    


Ich probier mir hier nen Wolf :D Leute es geht meistens einfach! :p


Ralf Jansen - Fr 12.04.13 11:00

Das ist das Problem mit den Fragen stellen aber nicht die Details mitgeben wollen ;)

Du hast gefragt wie man das focusieren verhindert zumindest ist das was hier angekommen ist. Deine Lösung ist jetzt aber den Button aus der Tabulatorreihenfolge zu nehmen. Wenn das reicht ok aber den Button per Keyboard focusieren und ausführen geht so natürlich immer noch. Man kann ja zum Beispiel per Cursortasten zum Button navigieren.


Sneedlewoods - Sa 13.04.13 12:37

Wir können ja jetzt noch lange darüber diskutieren, was aber zu nichts führen wird.
1. Letztendlich habe ich das Problem oben beschrieben, dass der Button immer den Focus hat. Dieses Problem wurde nun umgangen.
2. Die Cursor-Tasten führen nicht zu einer Aktivierung/Fokussierung!
3. In meinem 2. Post @Gausi sollte der unspektakuläre Hintergrund ersichtlich sein.
4. Mir ist bewusst dass es immer die Vertreter gibt, möglichst viel wissen zu müssen, weil sie dann eventuell auf bestimmte Ideen kommen. Diese Lösung kann man durchaus auch ohne viel Hintergrundwissen finden. (Zusätzlich siehe Punkt 3)
5. Ich hatte auch gesagt, die Tastatur für den Button komplett abzuschalten. Folglich brauche ich kein TabStop.

So hoffe, dass wir die Diskussion nun beenden können.


Ralf Jansen - Sa 13.04.13 13:05

OK dann keine Diskussion.

Dann nur eine Antwort zu dokumentarischen Zwecken für andere Leser.
TabStop verhindert nur das focussieren eines Controls über die Tabfolge es verhindert nicht das focussieren über andere Wege. In diesem speziellen Fall hier wo noch andere Dinge passieren mag das helfen im allgemeinen Fall wird das setzen von TabStop allein nicht helfen.


IhopeonlyReader - Sa 13.04.13 14:06

user profile iconSneedlewoods hat folgendes geschrieben Zum zitierten Posting springen:

2. ich habe KeyPreview auf true gestellt

hi, geht es dir darum, dass du die "tasten" nicht mehr angefangen kriegst, weil der Focus auf dem button ist?
wenn ja dann fang die "Tasten" doch mit

Delphi-Quelltext
1:
2:
3:
//procedure in einem timer
GetAsyncKeystate( Taste ))
//Taste könnte ord('A') sein falls du herausfinden willst, ob A gedrückt wurde etc..

Hier eine Liste der Taste:
http://www.delphipraxis.net/79622-%5Bvk_-codes%5Dtastendruck-simulieren.html


Sneedlewoods - Sa 13.04.13 21:00

Hi Ihope,

doch Tasten bekomme ich immernoch abgefangen. KeyPreview bewirkt ja eben, dass die Tasten zuerst zu den Form-Events geleitet wird und danach erst zu den Controls. Allerdings wird der Button eben immer ausgelöst wenn ich Return, Leertaste, etc. drücke, was nicht erwünscht ist, weil er den Focus hat, was ebenfalls nicht erwünscht ist.

Aber wie schon erwähnt, funktioniert es in diesem Falle mit TabStop=false. Ich habe nicht viel rumprobiert, aber bisher konnte keine Taste den Focus auf den Button legen.

Also alles i.O. ;)


Sneedlewoods - Sa 13.04.13 21:21

Also Ralf hatte Recht. Sobald ich mit der Maus den Button anklicke, hat er den Focus wieder. Mit den Tasten habe ich nachwievor keine Probleme.
Also bleibt die Suche nach einer Sicheren Methode offen.


Th69 - So 14.04.13 12:17

Hallo,

hast du denn die von Ralf Jansen vorgeschlagene Möglichkeit

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
public class NonFocusButton : Button
{
    public NonFocusButton()
    {
        SetStyle(ControlStyles.Selectable, false);
    }
}

mal ausprobiert?

Anstatt abzuleiten, kannst du auch per Reflection diese Methode auf das Control (Button) anwenden:

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
void SetControlNonSelectable(Control control)
{
    Type type = control.GetType();
    BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
    MethodInfo method = type.GetMethod( "SetStyle", flags );
    if(method != null)
    {
        object[] param = { ControlStyles.Selectable, false };
        method.Invoke(control, param);
    }
}


IhopeonlyReader - So 14.04.13 13:44

Um es zu lösen:
- Tabstop des Buttons auf FALSE setzen
UND die OnClick Procedure sollte so aussehen (am Beispiel von Button1)

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TForm1.Button1Click(Sender: TObject);
begin
Button1.Visible := False;
Button1.Visible := True;
end;

allgemein also:

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TForm1.ClickButton(Sender: TObject);
begin
(Sender as TButton).Visible := False;
(Sender as TButton).Visible := True;
end;


Edit: Es ist zwar in Delphi geschrieben, aber ich denke du verstehst damit was gemeint ist
-> Ein unsichtbares Object kann kein Focus haben ! somit verschwindet dieser bei Visisble := false;
damit der button aber weiterhin angezeigt wird wieder Visible := True;
... manche Compiler erkenen die "Sinnlosigkeit" und compilieren das deshalb gar nicht mit.. deshalb (falls das der Fall sein sollte) einfach mal ein delay(10) dazwischensetzen


Ralf Jansen - So 14.04.13 14:14

Im NonFocusButton Code von TH69 sicherheitshalber noch im Konstruktor den Basisklassen Konstruktor aufrufen.


IhopeonlyReader - So 14.04.13 14:21

@Ralf Jansen:
Danke dass du TH69-Variante noch verbesserst :D Aber ich denke meine "Art" ist leichter umzusetzen/zu verstehen oder?


Ralf Jansen - So 14.04.13 15:12

@IhopeonlyReader: Ich vermute mal das das hier, in C#/Winforms mit vermutlich nur diesem Buttom auf der Form, nicht funktioniert.
Verstehe auch nicht wie das in Delphi helfen sollte? Wenn der Button den Focus bekommt wird kein Click ausgeführt. Warum sollte das rumschrauben an der Visibility in Click dann irgendwas ändern. Wenn du dafür sorgen willst das das der Button nach dem Click den Focus verliert, warum auch immer, sollte das einfacher gehen.


IhopeonlyReader - So 14.04.13 15:21

weil er nur durch die Maus den Focus bekommen kann..

aber du hast recht, man kann natürlcih auch mit der maus den Focus setzten OHNE das onClick ergeignis auszuführen.
deshalb verbessert:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure TForm1.IrgendeinButtonMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if (Button=mbLeft) then (Sender as TButton).OnClick(Sender); //Diese Zeile ist nur Notwendig, falls der Button eine OnClick-Event besitzt
Button1.Visible := False;
Button1.Visible := True;
end;


Edit: In Delphi funktioniert dieser Code um einem Button den "Focus" wegzunehmen, in C# nicht wie ich gerade festgestellt habe...


Th69 - So 14.04.13 15:43

Hallo Ralf,

der Default-Basisklassenkonstruktor wird in .NET automatisch beim Ableiten aufgerufen.
Nur wenn man einen Basisklassenkonstruktor mit Parametern aufrufen möchte/muß, dann muß man diesen explizit aufrufen.


DarkStaRX - So 28.04.13 00:21

user profile iconSneedlewoods hat folgendes geschrieben Zum zitierten Posting springen:
Hallo also beim Button funktioniert das so nicht. Eventuell müsste ich das Panel ableiten und SetStyles ändern, aber dass würde bedeuten dass ich alle Labels, Buttons etc. selber in das Panel setzen muss, oder kann ich den Designer irgendwo dafür einspannen? Theoretisch müsste das ja gehen, ist das gleiche Objekt.

Mir gefällt allerdings gerade die Idee besser, die Tastatureingaben für den Button zu sperren. Dann muss ich die ganzen Elemente nicht erneut auf die Form setzen. Aber wie ich es mir dachte funktioniert leider auch nicht -.-


C#-Quelltext
1:
Button.KeyPress += new KeyPressEventHandler(                    
this.ButtonNoKey

C#-Quelltext
1:
2:
);
Button.KeyUp += new KeyEventHandler(
this.ButtonNoKey

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
);

private void ButtonNoKey(object sender, KeyPressEventArgs e)
{
    e.Handled=true;
}
private void ButtonNoKey(object sender, KeyEventArgs e)
{
    e.Handled=true;
}


Das hat leider nicht zum Erfolg geführt. Geht das irgendwie anders?


Beide führen zur Methode ButtonNoKey (Grün markiert)
Vielleicht liegt es daran