Как сделать автозаполнение в TextBox показывать предложения, когда пусто - PullRequest
3 голосов
/ 06 июля 2010

Я использую свойства автозаполнения для текстового поля (на самом деле это ToolStripTextBox). Это работает нормально, за исключением того, что не показывает, пока я не наберу в аренду один символ. Как сделать так, чтобы предложения отображались, даже если текстовое поле пусто?

Mode = Suggest
Source = CustomSource

Источник установлен программно и ограничен 10 предметами

В качестве альтернативы, если кто-то знает, как заставить предложения показывать программно на событии OnEnter, которое может быть решением

Ответы [ 4 ]

4 голосов
/ 13 июля 2012

Учтите, что это хак .Мне удалось решить эту проблему и отсутствие функциональности API, сделав тривиальную и неприятную вещь.Я покажу вам это с кодом:

    dim source as AutoCompleteStringCollection = new AutoCompleteStringColection()
    dim values() as String = new String() {" Monday", _
                                           " Tuesday", _
                                           " Wednesday", _
                                           " Thursday", _
                                           " Friday", _
                                           " Saturday", _
                                           " Sunday" }
    TextBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend
    TextBox1.AutoCompleteSource = AutoCompleteSource.CustomSource
    TextBox1.AutoCompleteCustomSource = source

То есть добавьте пробел к каждой строке в списке автозаполнения.Тогда это ваше знание об этом факте и используйте его для своей удобной цели.

Например, вы можете добавить пробел в TextBox при нажатии, фокусировке и т. Д. (Обратите внимание, что это можно сделать с любым символом. Идея состоит в том, чтобы знать, что каждая строка в списке автозаполнения начинаетсяс тем же символом)

Вы ДОЛЖНЫ знать об этом.На самом деле, подумайте над расширением формы TextBox и управляйте правильной обрезкой введенной строки.

Опять же, называть это рекомендованным или нет - это ваше решение.Этот ответ, как правило, решает проблему с желанием, чтобы TextBox выпал из списка предложений, не начав печатать с ограничениями API, также называемыми обходным путем или ugly-hack .

3 голосов
/ 07 июля 2010

Я не уверен, есть ли какое-либо решение для этого, тем более что окно предложения автоматически закроется, если вы удалите весь текст в TextBox.

Обходной путь может заключаться в перекрытии ComboBoxTextBox, и вы можете иметь это, чтобы показать все опции, но если один из них выбран или если что-то напечатано, вы автоматически переключаетесь на TextBox и показываете это вместо этого.
Хотя я бы предположил, что в этом сценарииЯ, вероятно, рассмотрю возможность полностью избавиться от TextBox и просто использовать ComboBox, поскольку это то, для чего оно здесь.

Чтобы показать выпадающее меню программно в ComboBox взгляде на SendMessageAPI и сообщение CB_SHOWDROPDOWN (и если я правильно помню, вы можете использовать один и тот же API / сообщение, но с другими параметрами, чтобы программно закрыть его, если это также необходимо).

2 голосов
/ 02 ноября 2018

Решение этой проблемы заключается в непосредственном переносе и вызове функций AutoSuggest WinShell. Затем вы можете включить эту функцию без особых головных болей (и даже получить несколько дополнительных бесплатно).

Ниже приведен рабочий пример, который позволяет отображать список AutoSuggest, даже если в поле ничего не напечатано (когда пользователь нажимает стрелки вверх / вниз)

Тот же код, что и в этом Гисте: https://gist.github.com/sverrirs/e6a64faaab341882c3e801792f5e87ae

public void EnableAutoSuggest(TextBox tb, string[] suggestions) {
    // Try to enable a more advanced settings for AutoComplete via the WinShell interface
    try {
        var source = new SourceCustomList() {StringList = suggestions.ToArray()};
        // For options descriptions see: 
        // https://docs.microsoft.com/en-us/windows/desktop/api/shldisp/ne-shldisp-_tagautocompleteoptions
        var options = AUTOCOMPLETEOPTIONS.ACO_UPDOWNKEYDROPSLIST | AUTOCOMPLETEOPTIONS.ACO_USETAB |
                      AUTOCOMPLETEOPTIONS.ACO_AUTOAPPEND | AUTOCOMPLETEOPTIONS.ACO_AUTOSUGGEST;
        AutoCompleteExt.Enable(tb.Handle, source, options);
    }
    catch (Exception) {
        // Incase of an error, let's fall back to the default
        var source = new AutoCompleteStringCollection();
        source.AddRange(suggestions);
        tb.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
        tb.AutoCompleteSource = AutoCompleteSource.CustomSource;
        tb.AutoCompleteCustomSource = source;
    }      
}

public void DisableAutoSuggest(TextBox tb) {
    tb.AutoCompleteCustomSource = null;
    AutoCompleteExt.Disable(tb.Handle);
}

И реализация AutoCompleteExt

using System;
using System.Runtime.InteropServices;

/// <summary>
/// From: https://www.codeproject.com/Articles/3792/C-does-Shell-Part-4
/// Note: The UCOMIEnumString interface is deprecated in .NET as of 2018!
/// </summary>
public class AutoCompleteExt {
    public static Guid CLSID_AutoComplete = new Guid("{00BB2763-6A77-11D0-A535-00C04FD7D062}");

    private static object GetAutoComplete()
    {
        return Activator.CreateInstance(Type.GetTypeFromCLSID(CLSID_AutoComplete));
    }

    public static void Enable(IntPtr controlHandle, SourceCustomList items, AUTOCOMPLETEOPTIONS options) {
        if (controlHandle == IntPtr.Zero)
            return;

        IAutoComplete2 iac = null;
        try {
            iac = (IAutoComplete2) GetAutoComplete();
            int ret = iac.Init(controlHandle, items, "", "");
            ret = iac.SetOptions(options);
            ret = iac.Enable(true);
        }
        finally {
            if (iac != null)
                Marshal.ReleaseComObject(iac);
        }
    }

    public static void Disable(IntPtr controlHandle) {
        if (controlHandle == IntPtr.Zero)
            return;

        IAutoComplete2 iac = null;
        try {
            iac = (IAutoComplete2) GetAutoComplete();
            iac.Enable(false);
        }
        finally {
            if (iac != null)
                Marshal.ReleaseComObject(iac);
        }
    }
}
/// <summary>
/// From https://www.pinvoke.net/default.aspx/Interfaces.IAutoComplete2
/// </summary>
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("EAC04BC0-3791-11D2-BB95-0060977B464C")]
public interface IAutoComplete2 {
    [PreserveSig]
    int Init(
        // Handle to the window for the system edit control that is to
        // have autocompletion enabled. 
        IntPtr hwndEdit,

        // Pointer to the IUnknown interface of the string list object that
        // is responsible for generating candidates for the completed 
        // string. The object must expose an IEnumString interface. 
        [MarshalAs(UnmanagedType.IUnknown)] object punkACL,

        // Pointer to an optional null-terminated Unicode string that gives
        // the registry path, including the value name, where the format 
        // string is stored as a REG_SZ value. The autocomplete object 
        // first looks for the path under HKEY_CURRENT_USER . If it fails,
        // it then tries HKEY_LOCAL_MACHINE . For a discussion of the 
        // format string, see the definition of pwszQuickComplete.
        [MarshalAs(UnmanagedType.LPWStr)] string pwszRegKeyPath,

        // Pointer to an optional string that specifies the format to be
        // used if the user enters some text and presses CTRL+ENTER. Set
        // this parameter to NULL to disable quick completion. Otherwise,
        // the autocomplete object treats pwszQuickComplete as a sprintf 
        // format string, and the text in the edit box as its associated 
        // argument, to produce a new string. For example, set 
        // pwszQuickComplete to "http://www. %s.com/". When a user enters
        // "MyURL" into the edit box and presses CTRL+ENTER, the text in 
        // the edit box is updated to "http://www.MyURL.com/". 
        [MarshalAs(UnmanagedType.LPWStr)] string pwszQuickComplete
    );

    // Enables or disables autocompletion.
    [PreserveSig]
    int Enable(bool value);

    // Sets the current autocomplete options.
    [PreserveSig]
    int SetOptions(AUTOCOMPLETEOPTIONS dwFlag);

    // Retrieves the current autocomplete options.
    [PreserveSig]
    int GetOptions(out AUTOCOMPLETEOPTIONS pdwFlag);
}

/// <summary>
///   Specifies values used by IAutoComplete2::GetOptions and 
///   "IAutoComplete2.SetOptions" for options surrounding autocomplete.
/// </summary>
/// <remarks>
///   [AUTOCOMPLETEOPTIONS Enumerated Type ()]
///   http://msdn.microsoft.com/en-us/library/bb762479.aspx
/// </remarks>
[Flags]
public enum AUTOCOMPLETEOPTIONS {
    /// <summary>Do not autocomplete.</summary>
    ACO_NONE = 0x0000,

    /// <summary>Enable the autosuggest drop-down list.</summary>
    ACO_AUTOSUGGEST = 0x0001,

    /// <summary>Enable autoappend.</summary>
    ACO_AUTOAPPEND = 0x0002,

    /// <summary>Add a search item to the list of 
    /// completed strings. When the user selects 
    /// this item, it launches a search engine.</summary>
    ACO_SEARCH = 0x0004,

    /// <summary>Do not match common prefixes, such as 
    /// "www." or "http://".</summary>
    ACO_FILTERPREFIXES = 0x0008,

    /// <summary>Use the TAB key to select an 
    /// item from the drop-down list.</summary>
    ACO_USETAB = 0x0010,

    /// <summary>Use the UP ARROW and DOWN ARROW keys to 
    /// display the autosuggest drop-down list.</summary>
    ACO_UPDOWNKEYDROPSLIST = 0x0020,

    /// <summary>Normal windows display text left-to-right 
    /// (LTR). Windows can be mirrored to display languages 
    /// such as Hebrew or Arabic that read right-to-left (RTL). 
    /// Typically, control text is displayed in the same 
    /// direction as the text in its parent window. If 
    /// ACO_RTLREADING is set, the text reads in the opposite 
    /// direction from the text in the parent window.</summary>
    ACO_RTLREADING = 0x0040,

    /// <summary>[Windows Vista and later]. If set, the 
    /// autocompleted suggestion is treated as a phrase 
    /// for search purposes. The suggestion, Microsoft 
    /// Office, would be treated as "Microsoft Office" 
    /// (where both Microsoft AND Office must appear in 
    /// the search results).</summary>
    ACO_WORD_FILTER = 0x0080,

    /// <summary>[Windows Vista and later]. Disable prefix 
    /// filtering when displaying the autosuggest dropdown. 
    /// Always display all suggestions.</summary>
    ACO_NOPREFIXFILTERING = 0x0100
}

/// <summary>
/// Implements the https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nn-objidl-ienumstring
/// interface for autoccomplete
/// </summary>
public class SourceCustomList : UCOMIEnumString {

    public string[] StringList;
    private int currentPosition = 0;

    public int Next(
            int celt,     // Number of elements being requested.
            string[] rgelt, // Array of size celt (or larger) of the 
                            // elements of interest. The type of this 
                            // parameter depends on the item being
                            // enumerated.  
            out int pceltFetched) // Pointer to the number of elements actually
                                    // supplied in rgelt. The Caller can pass 
                                    // in NULL if  celt is 1. 
    {
        pceltFetched = 0;
        while ((currentPosition <= StringList.Length-1) && (pceltFetched<celt))
        {
            rgelt[pceltFetched] = StringList[currentPosition];
            pceltFetched++;
            currentPosition++;            
        }

        if (pceltFetched == celt)
            return 0;    // S_OK;
        else
            return 1;    // S_FALSE;
    }

    /// <summary>
    /// This method skips the next specified number of elements in the enumeration sequence.
    /// </summary>
    /// <param name="celt"></param>
    /// <returns></returns>
    public int Skip(
        int celt)                    // Number of elements to be skipped. 
    {
        currentPosition += celt;
        if (currentPosition <= StringList.Length-1)
            return 0;
        else
            return 1;
    }

    // This method resets the enumeration sequence to the beginning.
    public Int32 Reset()
    {
        currentPosition = 0;
        return 0;
    }

    // This method creates another enumerator that contains the same enumeration 
    // state as the current one. Using this function, a client can record a 
    // particular point in the enumeration sequence and return to that point at a 
    // later time. The new enumerator supports the same interface as the original one.
    public void Clone(
            out UCOMIEnumString ppenum)         // Address of the IEnumString pointer  
                                                // variable that receives the interface 
                                                // pointer to the enumeration object. If 
                                                // the method  is unsuccessful, the value
                                                // of this output variable is undefined. 
    {
        SourceCustomList clone = new SourceCustomList {
            currentPosition = currentPosition,
            StringList = (String[]) StringList.Clone()
        };
        ppenum = clone;
    }        
}
1 голос
/ 16 июля 2010

В .net api такой возможности нет. Более того, в интерфейсе оболочки IAutoComplete , который используется внутри .net, такой возможности нет.

Таким образом, вы должны жить с этим или реализовать собственное поведение автозаполнения без использования по умолчанию. Вы можете использовать выпадающий список со списком, как предлагалось ранее, или даже создать свой собственный элемент автозаполнения (в моем приложении я выбрал последний вариант).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...