Laden...

Anhängen eines Ereignishandler an die vertikale Scrollleiste einer Listbox

Erstellt von Anchiko vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.460 Views
A
Anchiko Themenstarter:in
12 Beiträge seit 2020
vor 3 Jahren
Anhängen eines Ereignishandler an die vertikale Scrollleiste einer Listbox

Hallo,

ich versuche auf die (automatische) vertikalen Scrollleiste einer Listbox zuzugreifen. Genauer gesagt würde ich gerne den Ereignissen MouseEnter bzw. MosueLeave einen eigenen Ereignishandler anhängen.

Nach allem, was ich bisher gelesen habe, gibt es aber wohl keine Möglichkeit dies zu tun.

Hintergrund meines Problems ist, dass ich eine Textbox mit einem Form verknüpft habe, das nur aus einer Listbox besteht (kein Rahmen etc.) in der Textvorschläge erscheinen sollen in Abhängkeit vom bisher eingebenen Text. Das klappt auch alles soweit sehr gut.

Damit die Liste (die Form) im Vordergrund angezeigt wird, habe ich die Eigenschaft der Form mit der Liste auf TopMost = true gesetzt.

Damit die Liste dann (unter anderem) verschwindet, wenn das übergeordnete Fenster nicht mehr das aktive Fenster ist (und umgekehrt), habe ich dem übergeordneten Fenster einen ErgeinisHandler für Deactivate (bzw. Activated) angehängt. Klappt alles soweit.

Wenn ich dann in die Liste klicke verschwand bisher die Liste, was mir dann auch klar war wegen des oben genannten Ereignishandler für Deactivate für das übergeordnete Fenster.

Das konnte ich abfangen mit Ereignishandler für MouseEnter und MouseLeave für die Listbox. Nur dies hat leider keine Wirkung auf die vertikale Scrollleiste.

Gibt es vielleicht doch eine Möglichkeit der Scrollleiste eigene Ereignishandler anzuhängen?

Vielen Dank im Voraus!
Anchiko

4.931 Beiträge seit 2008
vor 3 Jahren

Du weißt, daß die Autovervollständigung bei einer TextBox schon eingebaut ist: TextBox.AutoCompleteMode?

Oder möchtest du eine individuelle Suche (nicht nur Textanfang), dann schau mal in Customize TextBox autocomplete und AutoComplete TextBox with SubString search, similar to SQL Like or Contains. Man benötigt also keine eigene Form für die ListBox (Stichwort: BringToFront).

A
Anchiko Themenstarter:in
12 Beiträge seit 2020
vor 3 Jahren

Hallo Th69,

vielen dank für Deine Antwort. Die Funktion AutoCompleteMode habe ich in der Tat noch nicht gekannt. Ich habe mir Deine Links angesehen und bin schnell an dem Punkt angekommen, warum ich den Standard nicht nutzen möchte.

Aus folgenden Gründen, habe ich eine indivuelle Liste erstellt;

  • Ich möchte die Liste selber Zeichnen (DrawMode = OwnerDraw).
  • Ich möchte die Liste selber positionieren. Dabei soll es auch ein Wechsel zwsichen linkem und rechtem "Anschlag" der Liste unter- oder oberhalb der Textbox geben je nach dem wo sich das Fenster gerade befindet.
  • Die Liste soll über das Formular hinaus angezeigt werden können.
  • Die Liste enthält von mir definierte Klassen, keine reinen String-Objekte.

Ich hatte es schon damit probiert, nur eine Listbox im jeweiligen Formular zu erzeugen (Stichwort BringToFront). Damit habe ich fast alles lösen können, nur nicht den Punkt, dass die Listbox über die Begrenzungen des Fensters hinaus angezeigt wird, wie es z.B. bei einer ComboBox der Fall ist. Deswegen habe ich ein Fenster mit der Liste erstellt und in meiner von TextBox abgeleiteten Namen-Textbox die Steuerung implemetiert (z.B. Reaktion auf die Pfeil-Tasten). Klappt alles so, wie ich es möchte. Nur eben nicht des Scrollen mit der Maus mit der vertikalen Scrollleiste.

Viele Grüße
Anchiko

4.931 Beiträge seit 2008
vor 3 Jahren

Hast du den Fokus denn auf die ListBox gesetzt? Dann sollte eigentlich das Scrollen per mittlerer Maustaste automatisch funktionieren?

A
Anchiko Themenstarter:in
12 Beiträge seit 2020
vor 3 Jahren

Die Liste erhält eigentlich nie den Fokus. Das Scrollen mit der mittleren Maustaste(Mausrad) funktioniert ohne Probleme (zumindest an meinem Rechner).

Beim Einfach- und beim Doppelklick mit der Maus wird der Fokus von der Liste sofort wieder an die Textbox zurückgegeben.

Dadurch wird beim übergeordneten Fenster ständig das Deactivate und Activated-Ereignis ausgelöst. Da ich beim Deactivate-Ereignis das Fenster mit der Liste schließe, habe ich über die Ereignisse MouseEnter und MouseLeave ein Kennzeichen gesetzt, damit das Listenfenster nicht geschlossen wird, wenn die Maus über ihr ist.

Diese Mouse-Ereignisse kennt (natürlich) auch die Scrollbar, was man ja daran erkennt, dass die Buttons der Scrollleiste ihre Farbe ändern, wenn man die Maus über sie bewegt.

Ich habe auch überlegt das WM_VSCROLL-Ereignis abzufangen und zu prüfen, ob der lParam-Wert der Message, das Handle der Scrollbar übergibt, um dann über das Handle auf das Objekt zugreifen zu können. Aber ich habe nichts gefunden, wie man über das Handle eines Control-Objekt auf dieses zugreifen kann und ich glaube, das soll auch so sein. Außerdem habe ich festgestellt, dass das Deactivate-Ereignis vor dem WM_VSCROLL-Ereignis gesendet wird, so dass mir das auch nichts bringen würde.

4.931 Beiträge seit 2008
vor 3 Jahren

Du meinst das Klicken auf die vertikale Scrollbar, weil diese dann das MouseClick-Ereignis auslöst, welche dann das Formular schließt? Warum nutzt du nicht das ListBox-SelectedItem-Ereignis, um den Eintrag zu wählen und das Formular mit dieser ListBox zu schließen.

Falls ich dich immer noch falsch verstanden habe, dann solltest du mal besser Code zeigen (bzw. ein Beispielprojekt anhängen).

Edit: Ich schau dann morgen früh hier noch mal rein...

A
Anchiko Themenstarter:in
12 Beiträge seit 2020
vor 3 Jahren

OK, entschuldige, ich stecke zu tief in der Materie.

Mein Problem ist: wenn ich auf die Scrollleiste klicke verschwindet die Liste.

Das liegt an folgendem Ereignishandler für Deactivate:


public class NamenTextbox : TextBox
.
.
.
protected virtual void HauptfensterDeaktivieren(object sender, EventArgs e)
{
    if (DeaktivierendurchAuswahlfenster) return;
    AuswahlZurücksetzen(sender, e);
    if (Vorschlag)
    {
        if (SelectionLength > 0) TextSetzen(Text.Remove(SelectionStart));
        TextBoxSelPosLeave = Text.Length;
        Vorschlag = false;
    }
    AuswahlWiederAktivieren = true;
}

protected virtual void HauptfensterAktiviert(object sender, EventArgs e)
{
    if (AuswahlWiederAktivieren)
    {
        AuswahlPositionStart = TextBoxSelPosLeave;
        TBTextChanged(sender, e);
        AuswahlWiederAktivieren = false;
    }
}

protected virtual void NTBParentWechsel(object sender, EventArgs e)
{
    if (TopLevelControl != null && !TopLevelControlGesetzt)
    {
        TopLevelControl.LocationChanged += new EventHandler(AuswahlAnpassen);
        if (TopLevelControl is Form)
        {
            ((Form)TopLevelControl).Activated += new EventHandler(HauptfensterAktiviert);
            ((Form)TopLevelControl).Deactivate += new EventHandler(HauptfensterDeaktivieren);
        }
        TopLevelControl.Disposed += new EventHandler(AuswahlZurücksetzen);
        TopLevelControlGesetzt = true;
   }
   else
   {
       Control parent = Parent;
       while (parent != null)
       {
           parent.ParentChanged += new EventHandler(NTBParentWechsel);
           parent = parent.Parent;
       }
   }
}

protected virtual void AuswahlZurücksetzen(object sender, EventArgs e)
{
    Auswahl?.Dispose();
    Auswahl = null;
    Auswahlliste = null;
}

public virtual void NTBListboxMouseEnter(object sender, EventArgs e)
{
    DeaktivierendurchAuswahlfenster = true;
}

public virtual void NTBListboxMouseLeave(object sender, EventArgs e)
{
    DeaktivierendurchAuswahlfenster = false;
}

public virtual void NTBListboxClick(object sender, EventArgs e)
{
    Focus(); // Fokus auf die Textbox
    if (Auswahlliste == null || Auswahlliste.SelectedItem == null || SelectionStart + SelectionLength != TextLength) return;
    Namenvorschlagen();
}

public virtual void NTBListboxDoubleClick(object sender, EventArgs e)
{
    if (Auswahlliste!= null && Auswahlliste.SelectedItem != null)
    {
        Namen name = (Namen)Auswahlliste.SelectedItem;
        if (name != null) TextSetzen(name.Text);
        SelectionLength = 0;
        SelectionStart = TextLength;
        TextBoxSelPosLeave = TextLength;
        VorschlagBeenden();
        Focus(); // Fokus auf die Textbox
    }
}

Meine Liste ist eine Form mit einer ListBox (siehe Bild1 im Anhang).

Da aber eben meiner Liste eine Form zugrundeliegt, wird jedes Mal, wenn mit der Maus in die Liste geklickt wird, diese Form aktiviert und das TopLevelControl erhält das Deactivate-Ereignis, was wiederum die Liste bzw. die Form schließt.

Deshalb habe ich die oben angegebenen Ereignishandler für MouseEnter und MouseLeave der Auswahlliste hinzugefügt.

Es funktioniert:

  • das Scrollen in der Liste mit den Pfeil-Tasten hoch und runter, wobei diese Tasten gedrückt werden, wenn die Textbox den Fokus hat (habe ich über das KeyDown-Ereignis gesteuert)
  • das Scrollen mit der mittleren Maus-Taste/Mausrad (wahrscheinlich, weil die Liste hierbei nicht den Fokus und somit das TopLevelControl kein Deactivate-Ereignis erhält)
  • das Klicken in die Liste

Wenn ich aber auf die Scrollleiste klicke, verschwindet die Liste. Das "Warum" ist mir klar (Deactivate-Ereignis des TopLevelControl, siehe oben).

Und ich denke, dass ich das vermeiden könnte, wenn ich der Scrollleiste Mouse- und Click-Ereignishandler anfügen könnte. Dafür brauche ich aber Zugriff auf die Scrollleiste.

4.931 Beiträge seit 2008
vor 3 Jahren

Evtl. hilft dir Getting Scroll Events for a Listbox.

Ein anderer Vorschlag ist es, das Fenster nicht-fokussierbar zu machen, so daß es auch nicht das Hauptfenster deaktivieren kann: s. Form ohne Focus öffnen (bes. meinen Beitrag 😉.

A
Anchiko Themenstarter:in
12 Beiträge seit 2020
vor 3 Jahren

Hi,

vielen Dank für Deine Hinweise.

Der Tip mit WS_EX_NOACTIVATE funktioniert bei mir nicht. Das übergeordnete Fenster bekommt nach wie vor die Deactivate-Ereignisse.

Hier der Code des Auswahlfensters:


public partial class AuswahlFenster : Form
{
    private const int WS_EX_NOACTIVATE = 0x8000000;

    public NamenTextbox Box { get; set; }

    public AuswahlFenster()
    {
        InitializeComponent();
    }

    protected override CreateParams CreateParams
    {
        get
        {
            CreateParams baseParams = base.CreateParams;
            baseParams.ExStyle |= WS_EX_NOACTIVATE;
            return baseParams;
        }
    }

    private void AuswahlFenster_Shown(object sender, EventArgs e)
    {
        if (Box != null) Box.Focus();
    }

    private void Liste_SizeChanged(object sender, EventArgs e)
    {
        Size = new Size(AuswahlListe.Width, AuswahlListe.Height);
    }

    public ListBox Liste()
    {
        return AuswahlListe;
    }
}

Bei InitializeComponent() werden folgende Einstellungen für das Fenster vorgenommen:


this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(200, 116);
this.ControlBox = false;
this.Controls.Add(this.AuswahlListe);
this.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AuswahlFenster";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "AuswahlFenster";
this.TopMost = true;
this.Shown += new System.EventHandler(this.AuswahlFenster_Shown);

Das Abfangen der Scrollereignisse führt auch nicht zum Ziel, weil das Deactivate-Ereignis vor den Scroll-Ereignissen gesendet wird, so dass die Liste schon nicht mehr existiert, um ein Scrollereignis bekommen zu können. Ich drehe mich im Kreis.

Aber vielen Dank für Deine Hilfe!
Anchiko

4.931 Beiträge seit 2008
vor 3 Jahren

Tja, schade.

Aber ich habe noch etwas weitergesucht und Autocomplete Menu gefunden (man muß aber bei CodeProject angemeldet sein, um die Anhänge herunterladen zu können).
Ich habe mir die Demo heruntergeladen und ausprobiert (die ListBox ragt über das Fenster hinaus und auch die Scrollbar ist per Maus aktivierbar 😉.

Edit: Im Anhang ein Screenshot als Beweis.

A
Anchiko Themenstarter:in
12 Beiträge seit 2020
vor 3 Jahren

Wow. Hut ab. Und er verwendet ein ToolStripDropDown...

Herzlichen Dank!