【问题标题】:A combo-box providing a resizable file name completion list and drop-down list with a history一个组合框,提供可调整大小的文件名完成列表和带有历史记录的下拉列表
【发布时间】:2013-07-11 19:44:01
【问题描述】:

我需要创建一个组合框

  • 提供可调整大小的文件名完成列表和
  • 保留以前输入的历史记录并在下拉列表中显示它们

类似于 Windows 中的“运行”对话框。

可调整大小的完成列表:

下拉列表:

在 WinForms、WPF 或任何开源库中是否有任何合适的控件?还是我需要使用低级控件手动实现?

提前谢谢你!

【问题讨论】:

  • 完成列表仅在 Windows 8 中可调整大小。

标签: wpf winforms drop-down-menu autocomplete combobox


【解决方案1】:

WPF 的解决方案

Part 1

原则上,您可以使用样式和模板来实现您的问题。在ComboBox 中,结果在Popup 中给出,但默认不支持更改大小。如果您使用事件DragDelta,添加调整大小并不困难。示例:

private void MyThumb_DragDelta(object sender, DragDeltaEventArgs e)
{           
    double yadjust = MyPopup.Height + e.VerticalChange;
    double xadjust = MyPopup.Width + e.HorizontalChange;

    if ((xadjust >= 0) && (yadjust >= 0))
    {
        MyPopup.Width = xadjust;
        MyPopup.Height = yadjust;
    }
} 

事件最好设置在Thumb控件上(他也有事件DragStartedDragCompleted)。

一切都很好,但我们确实需要在ComboBox 内做。一种方法是使用StyleTemplate。首先,在ComboBox 样式中添加Thumb,使其出现在展开的列表中:

...

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="ComboBox">
            <Grid Name="MainGrid">
                <ToggleButton Name="ToggleButton" Template="{StaticResource ComboBoxToggleButton}" Grid.Column="2" Focusable="False" IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press" />

                <ContentPresenter Name="ContentSite" IsHitTestVisible="False" Content="{TemplateBinding SelectionBoxItem}" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Margin="3,3,23,3" VerticalAlignment="Center" HorizontalAlignment="Left" />

                <TextBox x:Name="PART_EditableTextBox" Style="{x:Null}" Template="{StaticResource ComboBoxTextBox}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="3,3,23,3" Focusable="True" Background="{TemplateBinding Background}" Visibility="Hidden" IsReadOnly="{TemplateBinding IsReadOnly}" />

                <!-- Expanded list store here -->
                <Popup Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Slide">
                    <Grid Name="DropDown" Width="100" Height="100" SnapsToDevicePixels="True">
                    <Border x:Name="DropDownBorder" Background="White" BorderThickness="1" BorderBrush="Gray" />

                    <ScrollViewer Margin="2" SnapsToDevicePixels="True">
                        <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
                    </ScrollViewer>

                    <!-- Our Thumb -->
                    <Thumb x:Name="ResizeGripThumb" Style="{StaticResource ResizeGripStyle}" HorizontalAlignment="Right" Margin="0,0,2,2" Background="Transparent" VerticalAlignment="Bottom" Width="12" Height="12" />
                </Grid>
            </Popup>
        </Grid>
...             

为了正常显示Thumb,使用Path添加样式:

<!-- ResizeGrip Style -->
<Style x:Key="ResizeGripStyle" TargetType="{x:Type Thumb}">
    <Setter Property="SnapsToDevicePixels" Value="True" />
    <Setter Property="Cursor" Value="SizeNWSE" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Thumb}">
                <Grid>
                    <Path Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" Stretch="Fill" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Fill="Gray" Data="M8,0L10,0 10,2 8,2z M4,4L6,4 6,6 4,6z M8,4L10,4 10,6 8,6z M0,8L2,8 2,10 0,10z M4,8L6,8 6,10 4,10z M8,8L10,8 10,10 8,10z "/>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

现在,在这个阶段,我们在扩展列表中显示了ResizeGrip。但是默认的ScrollBar,然后用他的存在来关闭它,所以它也定义了ScrollBar的样式。它将更改边距VerticalThumb,因此:

...

<!-- VerticalThumb for ScollBar -->
<Style x:Key="VerticalThumb" TargetType="{x:Type Thumb}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Thumb}">
                <Rectangle Fill="Gray" Margin="-1,-1,-3,16" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>        

现在,主要组件正常显示。在 XAML 中声明 ComboBox

<ComboBox Name="ResizeComboBox" Style="{StaticResource MyComboBox}" IsEditable="True" IsTextSearchEnabled="True" FontSize="14" SelectedIndex="0" Width="100" Height="30">
    <ComboBoxItem>1</ComboBoxItem>
    <ComboBoxItem>2</ComboBoxItem>
    <ComboBoxItem>3</ComboBoxItem>
    <ComboBoxItem>4</ComboBoxItem>
    <ComboBoxItem>5</ComboBoxItem>
    <ComboBoxItem>6</ComboBoxItem>
    <ComboBoxItem>7</ComboBoxItem>
    <ComboBoxItem>8</ComboBoxItem>
</ComboBox>

仍然需要设置一个处理程序来调整Popup 的大小。我将使用函数FindChild&lt;T&gt; 在模板中使其成为搜索控件。为了安全起见,我会在ContentRenderedWindow 事件中执行此操作,以了解所有已加载的元素:

private void Window_ContentRendered(object sender, EventArgs e)
{
    // Find MainGrid in our ComboBox template
    Grid MyMainGrid = FindChild<Grid>(ResizeComboBox, "MainGrid");

    // Find Popup in Grid
    Popup MyPopup = MyMainGrid.FindName("Popup") as Popup;

    // Find Thumb in Popup
    Thumb MyThumb = MyPopup.FindName("ResizeGripThumb") as Thumb;

    // Set the handler
    MyThumb.DragDelta += new DragDeltaEventHandler(MyThumb_DragDelta);
}

FindChild&lt;&gt;列表:

    public static T FindChild<T>(DependencyObject parent, string childName) where T : DependencyObject
    {
        if (parent == null)
        {
            return null;
        }

        T foundChild = null;

        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);

        for (int i = 0; i < childrenCount; i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            T childType = child as T;

            if (childType == null)
            {
                foundChild = FindChild<T>(child, childName);

                if (foundChild != null) break;
            }
            else
                if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = child as FrameworkElement;

                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        foundChild = (T)child;
                        break;
                    }
                    else
                    {
                        foundChild = FindChild<T>(child, childName);

                        if (foundChild != null)
                        {
                            break;
                        }
                    }
                }
                else
                {
                    foundChild = (T)child;
                    break;
                }
        }

        return foundChild;
    }

处理程序列表MyThumb_DragDelta:

private void MyThumb_DragDelta(object sender, DragDeltaEventArgs e)
{
    Thumb MyThumb = sender as Thumb;
    Grid MyGrid = MyThumb.Parent as Grid;

    // Set the new Width and Height fo Grid, Popup they will inherit
    double yAdjust = MyGrid.Height + e.VerticalChange;
    double xAdjust = MyGrid.Width + e.HorizontalChange;

    // Set new Height and Width
    if ((xAdjust >= 0) && (yAdjust >= 0))
    {
        MyGrid.Width = xAdjust;
        MyGrid.Height = yAdjust;
    }
}       

原来如此:

Some notes: 要设置模板值,它们应该有一个默认值,否则该值将是NaN,我们不能设置它们。我们在这里设置了这些参数:

<Grid Name="DropDown" Width="100" Height="100" SnapsToDevicePixels="True">      

模板和代码的完整列表可以在here 找到,因为它们的数量很大。样式不能轻易改变,因为它们是匆忙制作的,所以应该为自己做。

Part 2

至于存储输入的数据,这取决于您的目标。我认为你可以这样做:

  1. 创建一个列表(可能是ObservableCollection)来存储项目。
  2. 成功输入元素后,例如 - which he was found 在某些来源中,将其保存到您的列表中。
  3. 将此列表绑定到ComboBox

只需设置属性IsEditable= "True"IsTextSearchEnabled= "True" 即可在下拉列表中显示输入字符(如我的示例所示)。

因此,您将拥有一个列表,其中添加了元素,可以向用户显示。

【讨论】:

    【解决方案2】:

    对于 WindowsForms,您需要指定组合属性 AutoCompleteSource = AutoCompleteSource.FileSystem 和(可选)AutoCompleteMode = AutoCompleteMode.Suggest。它“提供了一个可调整大小的文件名完成列表”。

    我不知道“保留以前输入的历史记录并在下拉列表中显示它们”的嵌入式解决方案。

    【讨论】:

      【解决方案3】:

      这是一段可重用的代码,可以帮助您入门,这是您如何将其与具有标准文本框的标准 Windows 窗体应用程序一起使用:

      public partial class Form1 : Form
      {
          private AutoCompletion _ac;
      
          public Form1()
          {
              InitializeComponent();
      
              // add the autocompletion tool to the 'textBox1' text box 
              _ac = new AutoCompletion(textBox1);
              _ac.TextChanged += AutoCompletionTextChanged;
          }
      
          private void AutoCompletionTextChanged(object sender, EventArgs e)
          {
              if (ShowSomething())
              {
                  // clear the items and add 50 as an example
                  _ac.Items.Clear();
                  for (int i = 0; i < 50; i++)
                  {
                      AutoCompletion.AutoCompletionItem item = new AutoCompletion.AutoCompletionItem();
                      item.Text = "Item " + i;
                      _ac.Items.Add(item);
                  }
                  _ac.SelectedItem = _ac.Items[0]; // pre-select first one as an example
                  _ac.Show(); // show the autocompletion window
                  return;
              }
          }
      
          private bool ShowSomething()
          {
              return true; // TODO: implement this
          }
      
          protected override void Dispose(bool disposing)
          {
              if (disposing && (components != null))
              {
                  components.Dispose();
              }
      
              base.Dispose(disposing);
      
              // don't forget to dispose the tool
              if (_ac != null)
              {
                  _ac.Dispose();
              }
          }
      }
      

      这里是代码:

      public sealed class AutoCompletion: IDisposable
      {
          private AutoCompleteForm _form;
          private AutoCompletionItem _selectedItem;
          private readonly List<AutoCompletionItem> _items = new List<AutoCompletionItem>();
      
          public event EventHandler TextChanged;
      
          public AutoCompletion(TextBoxBase textBox)
          {
              if (textBox == null)
                  throw new ArgumentNullException("textBox");
      
              _form = new AutoCompleteForm(this);
              _form.SetOwner(textBox);
              Margin = new Padding(2, 0, 30, 0);
              ImageMargin = 2;
              BorderStyle = FormBorderStyle.SizableToolWindow;
              MinimumLines = 1;
              MaximumLines = 100;
              IsEnabled = true;
          }
      
          public bool IsEnabled
          {
              get
              {
                  return _form.IsEnabled;
              }
              set
              {
                  _form.IsEnabled = value;
              }
          }
      
          public bool UserDismissed
          {
              get
              {
                  return _form.UserDismissed;
              }
              set
              {
                  _form.UserDismissed = value;
              }
          }
      
          public AutoCompletionItem SelectedItem
          {
              get
              {
                  return _selectedItem;
              }
              set
              {
                  if (_selectedItem == value)
                      return;
      
                  _selectedItem = value;
              }
          }
      
          public Font Font
          {
              get
              {
                  return _form.ListBoxFont;
              }
              set
              {
                  _form.ListBoxFont = value;
              }
          }
      
          public int MinimumLines
          {
              get
              {
                  return _form.MinimumLines;
              }
              set
              {
                  _form.MinimumLines = value;
              }
          }
      
          public int MaximumLines
          {
              get
              {
                  return _form.MaximumLines;
              }
              set
              {
                  _form.MaximumLines = value;
              }
          }
      
          public int ImageMargin
          {
              get
              {
                  return _form.ImageMargin;
              }
              set
              {
                  _form.ImageMargin = value;
              }
          }
      
          public Padding Margin
          {
              get
              {
                  return _form.ListBoxMargin;
              }
              set
              {
                  _form.ListBoxMargin = value;
              }
          }
      
          public FormBorderStyle BorderStyle
          {
              get
              {
                  return _form.FormBorderStyle;
              }
              set
              {
                  _form.FormBorderStyle = value;
              }
          }
      
          public ImageList Images
          {
              get
              {
                  return _form.Images;
              }
              set
              {
                  _form.Images = value;
              }
          }
      
          public IList<AutoCompletionItem> Items
          {
              get
              {
                  return _items;
              }
          }
      
          public void Hide()
          {
              _form.HideList();
          }
      
          public void Show()
          {
              _form.ShowList();
          }
      
          public void Dispose()
          {
              if (_form != null)
              {
                  _form.Dispose();
                  _form = null;
              }
          }
      
          private void OnTextChanged(object sender, EventArgs e)
          {
              EventHandler handler = TextChanged;
              if (handler != null)
              {
                  handler(sender, e);
              }
          }
      
          public class AutoCompletionItem
          {
              public AutoCompletionItem()
                  : this(null)
              {
              }
      
              public AutoCompletionItem(string text)
                  : this(text, -1)
              {
              }
      
              public AutoCompletionItem(string text, int imageIndex)
                  :this(text, null, imageIndex)
              {
              }
      
              public AutoCompletionItem(string text, string toolTip, int imageIndex)
                  :this(text, toolTip, null, imageIndex)
              {
              }
      
              public AutoCompletionItem(string text, string toolTip, string toolTipTitle, int imageIndex)
              {
                  if (text == null)
                  {
                      text = string.Empty;
                  }
                  Text = text;
                  ToolTip = toolTip;
                  ImageIndex = imageIndex;
                  ToolTipTitle = toolTipTitle;
              }
      
              public string Text { get; set; }
              public string ToolTip { get; set; }
              public string ToolTipTitle { get; set; }
              public int ImageIndex { get; set; }
          }
      
          private class AutoCompleteForm : Form
          {
              private readonly ImageListBox _listBox;
              private TextBoxBase _textBox;
              private bool _isEnabled;
              private readonly AutoCompletion _autoCompletion;
      
              [DllImport("user32.dll")]
              private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
      
              [DllImport("user32.dll")]
              private static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
      
              [DllImport("user32.dll")]
              private static extern int MsgWaitForMultipleObjectsEx(int nCount, IntPtr pHandles, int dwMilliseconds, int dwWakeMask, int dwFlags);
      
              [StructLayout(LayoutKind.Sequential)]
              private struct RECT
              {
                  public int Left;
                  public int Top;
                  public int Right;
                  public int Bottom;
              }
      
              private const int GWL_HWNDPARENT = -8;
              private const int WM_KEYDOWN = 0x0100;
              private const int WM_KEYUP = 0x0101;
              private const int WM_SIZING = 0x0214;
              private const int WM_NCHITTEST = 0x0084;
              private const int HTNOWHERE = 0;
              private const int HTLEFT = 10;
              private const int HTTOP = 12;
              private const int HTTOPLEFT = 13;
              private const int HTTOPRIGHT = 14;
              private const int HTBOTTOMLEFT = 16;
              private const int MWMO_INPUTAVAILABLE = 0x0004;
      
              private class ImageListBox : ListBox
              {
                  private ImageList _images;
                  private int _imageMargin;
                  private readonly AutoCompleteForm _form;
                  public readonly ToolTip _toolTip;
                  private Point _lastToolTipPoint;
      
                  public ImageListBox(AutoCompleteForm form)
                  {
                      _form = form;
                      BorderStyle = BorderStyle.None;
                      SelectionMode = SelectionMode.One;
                      DisplayMember = "Text";
                      Dock = DockStyle.Fill;
                      DrawMode = DrawMode.OwnerDrawFixed;
                      _toolTip = new ToolTip();
                  }
      
                  protected override void OnSelectedIndexChanged(EventArgs e)
                  {
                      base.OnSelectedIndexChanged(e);
                      if (SelectedIndices.Count == 0)
                          return;
      
                      Rectangle rect = GetItemRectangle(SelectedIndices[0]);
                      AutoCompletionItem item = (AutoCompletionItem)Items[SelectedIndices[0]];
                      _toolTip.Show(item.ToolTip, this, Width + 2 * _form.BorderSize.Width, rect.Top);
                      if (!string.IsNullOrEmpty(item.ToolTipTitle))
                      {
                          _toolTip.ToolTipTitle = item.ToolTipTitle;
                      }
                      _toolTip.ShowAlways = true;
                  }
      
                  protected override void OnDoubleClick(EventArgs e)
                  {
                      _form.Commit(null);
                  }
      
                  protected override void OnMouseMove(MouseEventArgs e)
                  {
                      base.OnMouseMove(e);
                      int index = IndexFromPoint(e.Location);
                      if ((index >= 0) && (index < Items.Count))
                      {
                          AutoCompletionItem item = (AutoCompletionItem)Items[index];
                          if (!string.IsNullOrEmpty(item.ToolTip))
                          {
                              // avoid flickering
                              if ((_toolTip.GetToolTip(this) != item.ToolTip) && (_lastToolTipPoint != e.Location))
                              {
                                  _toolTip.SetToolTip(this, item.ToolTip);
                                  if (!string.IsNullOrEmpty(item.ToolTipTitle))
                                  {
                                      _toolTip.ToolTipTitle = item.ToolTipTitle;
                                  }
                                  _lastToolTipPoint = e.Location;
                              }
                          }
                      }
                  }
      
                  protected override void WndProc(ref Message m)
                  {
                      // we need this to track the TAB character
                      if ((m.Msg == WM_KEYUP) && (m.WParam.ToInt32() == 9))
                      {
                          _form.OnTabPressed();
                          m.Result = new IntPtr(1); // handled
                          return;
                      }
                      base.WndProc(ref m);
                  }
      
                  public int ImageMargin
                  {
                      get
                      {
                          return _imageMargin;
                      }
                      set
                      {
                          if (_imageMargin == value)
                              return;
      
                          _imageMargin = value;
                          Invalidate();
                      }
                  }
      
                  public ImageList Images
                  {
                      get
                      {
                          return _images;
                      }
                      set
                      {
                          if (_images == value)
                              return;
      
                          _images = value;
                          if (_images != null)
                          {
                              ItemHeight = _images.ImageSize.Height + Margin.Vertical;
                          }
                          Invalidate();
                      }
                  }
      
                  protected override void OnDrawItem(DrawItemEventArgs e)
                  {
                      if (e.Index < 0)
                          return;
      
                      AutoCompletionItem item = (AutoCompletionItem)Items[e.Index];
                      if (_images == null)
                      {
                          e.DrawBackground();
                          e.DrawFocusRectangle();
                          using (Brush foreBrush = new SolidBrush(e.ForeColor))
                          {
                              e.Graphics.DrawString(item.Text, e.Font, foreBrush, e.Bounds);
                          }
                          return;
                      }
      
                      Rectangle bounds = e.Bounds;
      
                      bounds.X += Margin.Left;
                      if (item.ImageIndex >= 0)
                      {
                          _images.Draw(e.Graphics, bounds.Left, bounds.Top, item.ImageIndex);
                          bounds.X += _images.ImageSize.Width + _imageMargin;
                      }
      
                      using (Brush backBrush = new SolidBrush(e.BackColor))
                      {
                          e.Graphics.FillRectangle(backBrush, bounds);
                      }
      
                      if (((e.State & DrawItemState.Focus) == DrawItemState.Focus) && ((e.State & DrawItemState.NoFocusRect) != DrawItemState.NoFocusRect))
                      {
                          ControlPaint.DrawFocusRectangle(e.Graphics, bounds, ForeColor, BackColor);
                      }
      
                      bounds.Y += Margin.Top;
                      using (Brush foreBrush = new SolidBrush(e.ForeColor))
                      {
                          e.Graphics.DrawString(item.Text, e.Font, foreBrush, bounds);
                      }
                  }
              }
      
              public AutoCompleteForm(AutoCompletion autoCompletion)
              {
                  _autoCompletion = autoCompletion;
                  ShowInTaskbar = false;
                  ControlBox = false;
                  MinimizeBox = false;
                  MaximizeBox = false;
                  Text = string.Empty;
                  AutoScaleMode = AutoScaleMode.None;
                  _listBox = new ImageListBox(this);
                  _listBox.KeyDown += OnListBoxKeyDown;
                  Controls.Add(_listBox);
                  DockPadding.All = 0;
              }
      
              public bool UserDismissed { get; set; }
              public int MaximumLines { get; set; }
              public int MinimumLines { get; set; }
      
              public bool IsEnabled
              {
                  get
                  {
                      return _isEnabled;
                  }
                  set
                  {
                      if (_isEnabled != value)
                      {
                          _isEnabled = value;
                          if (!_isEnabled)
                          {
                              HideList();
                          }
                      }
                  }
              }
      
              public Font ListBoxFont
              {
                  get
                  {
                      return _listBox.Font;
                  }
                  set
                  {
                      _listBox.Font = value;
                  }
              }
      
              public Padding ListBoxMargin
              {
                  get
                  {
                      return _listBox.Margin;
                  }
                  set
                  {
                      _listBox.Margin = value;
                  }
              }
      
              public int ImageMargin
              {
                  get
                  {
                      return _listBox.ImageMargin;
                  }
                  set
                  {
                      _listBox.ImageMargin = value;
                  }
              }
      
              public ImageList Images
              {
                  get
                  {
                      return _listBox.Images;
                  }
                  set
                  {
                      _listBox.Images = value;
                  }
              }
      
              private Size BorderSize
              {
                  get
                  {
                      Size size = Size - ClientSize;
                      return new Size(size.Width / 2, size.Height / 2);
                  }
              }
      
              private void OnTabPressed()
              {
                  Commit(null);
              }
      
              private static bool PassThru(KeyEventArgs e)
              {
                  if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down || e.KeyCode == Keys.PageUp || e.KeyCode == Keys.PageDown)
                      return false;
      
                  return true;
              }
      
              private void OnListBoxKeyDown(object sender, KeyEventArgs e)
              {
                  switch (e.KeyCode)
                  {
                      case Keys.Escape:
                          UserDismissed = true;
                          HideList();
                          return;
      
                      case Keys.Return:
                          Commit(null);
                          return;
      
                      case Keys.OemPeriod:
                      case Keys.Decimal:
                          UserDismissed = false;
                          if (_listBox.SelectedItem == null)
                          {
                              // repost
                              PostMessage(_autoCompletion._form._textBox.Handle, WM_KEYDOWN, new IntPtr((int)e.KeyData), IntPtr.Zero);
                              return;
                          }
                          Commit(".");
                          return;
      
                      default:
                          if (e.KeyCode == Keys.Back)
                          {
                              CaptureOriginalText();
                          }
      
                          if (PassThru(e))
                          {
                              PostMessage(_autoCompletion._form._textBox.Handle, WM_KEYDOWN, new IntPtr((int)e.KeyData), IntPtr.Zero);
                          }
                          return;
                  }
              }
      
              private static Point GetSelectionPoint(TextBoxBase textBox)
              {
                  using (Graphics graphics = Graphics.FromHwnd(textBox.Handle))
                  {
                      string text;
                      if (textBox.Text.Length == 0)
                      {
                          // use dummy text
                          text = "I";
                      }
                      else
                      {
                          text = textBox.Text.Substring(0, textBox.SelectionStart);
                      }
                      SizeF size = graphics.MeasureString(text, textBox.Font);
                      if (size.Width > textBox.Width)
                      {
                          size.Width = textBox.Width;
                      }
                      return textBox.Parent.PointToScreen(new Point((int)(size.Width + textBox.Location.X), (int)(size.Height + textBox.Location.Y)));
                  }
              }
      
              private bool _raiseTextChanged = true;
              private void Commit(string extra)
              {
                  UserDismissed = false;
                  if (_listBox.SelectedItem == null)
                      return;
      
                  _raiseTextChanged = false;
      
                  string newSelection = ((AutoCompletionItem)_listBox.SelectedItem).Text + extra;
      
                  string textBefore;
                  if (_originalSelectionStart > 0)
                  {
                      textBefore = _originalText.Substring(0, _originalSelectionStart);
                      int pos = textBefore.LastIndexOf('.');
                      if (pos >= 0)
                      {
                          textBefore = textBefore.Substring(0, pos);
                      }
                      else
                      {
                          textBefore = string.Empty;
                      }
                  }
                  else
                  {
                      textBefore = string.Empty;
                  }
      
                  if ((!textBefore.EndsWith(".")) && (!newSelection.EndsWith(".")) && (textBefore.Length > 0))
                  {
                      textBefore += ".";
                  }
      
                  _textBox.Text = textBefore + newSelection;// +textAfter;
                  _textBox.SelectionLength = 0;
                  _textBox.SelectionStart = _textBox.Text.Length;
      
                  _raiseTextChanged = true;
                  CaptureOriginalText();
                  HideList();
              }
      
              public void SetOwner(TextBoxBase textBox)
              {
                  if (textBox == null)
                      throw new ArgumentNullException("textBox");
      
                  _textBox = textBox;
                  _textBox.KeyDown += OnTextBoxKeyDown;
                  _textBox.TextChanged += OnTextBoxTextChanged;
              }
      
              private void OnTextBoxTextChanged(object sender, EventArgs e)
              {
                  if (!IsEnabled)
                      return;
      
                  if (!_raiseTextChanged)
                      return;
      
                  _autoCompletion.OnTextChanged(_textBox, e);
              }
      
              private void CaptureOriginalText()
              {
                  _originalText = _textBox.Text;
                  _originalSelectionStart = _textBox.SelectionStart;
                  _originalSelectionLength = _textBox.SelectionLength;
              }
      
              private void OnTextBoxKeyDown(object sender, KeyEventArgs e)
              {
                  if (!IsEnabled)
                      return;
      
                  if (Visible)
                      return;
      
                  CaptureOriginalText();
              }
      
              public void HideList()
              {
                  Visible = false;
              }
      
              public void ShowList()
              {
                  if (_autoCompletion.Items.Count == 0)
                      return;
      
                  if ((_textBox.Text.EndsWith(".")) && (!_originalText.EndsWith(".")))
                  {
                      _originalText += ".";
                      _originalSelectionStart++;
                  }
      
                  Visible = false;
                  _listBox.Items.Clear();
                  float maxWidth = 0;
                  int height = _autoCompletion.Items.Count * _listBox.ItemHeight;
                  using (Graphics graphics = Graphics.FromHwnd(_listBox.Handle))
                  {
                      foreach (AutoCompletionItem item in _autoCompletion._items)
                      {
                          int index = _listBox.Items.Add(item);
                          if (item == _autoCompletion.SelectedItem)
                          {
                              _listBox.SelectedIndex = index;
                          }
      
                          if (item.Text != null)
                          {
                              SizeF size = graphics.MeasureString(item.Text, _listBox.Font);
                              if (size.Width > maxWidth)
                              {
                                  maxWidth = size.Width;  
                              }
                          }
                      }
                  }
      
                  SetWindowLong(Handle, GWL_HWNDPARENT, _textBox.Handle);
      
                  Point point = GetSelectionPoint(_textBox);
                  Size borderSize = BorderSize;
                  if (Images != null)
                  {
                      point.X -= Images.ImageSize.Width;
                  }
                  point.X -= borderSize.Width + ListBoxMargin.Left + ImageMargin;
                  point.Y += 4; // TODO: can we be smarter?
      
                  Location = point;
                  int width = (int)maxWidth + ListBoxMargin.Right;
      
                  int minLines = MinimumLines;
                  if (minLines < 1)
                  {
                      minLines = 1;
                  }
      
                  int maxLines = MaximumLines;
                  if (maxLines < minLines)
                  {
                      maxLines = minLines;
                  }
      
                  height = Math.Min(height, maxLines * _listBox.ItemHeight);
                  ClientSize = new Size(width, height);
                  MinimumSize = new Size(width, minLines * _listBox.ItemHeight) + borderSize;
                  MaximumSize = new Size(width * 2, maxLines * _listBox.ItemHeight);
      
                  Visible = true;
                  _listBox.Focus();
                  DoModalLoop();
              }
      
              private string _originalText;
              private int _originalSelectionStart;
              private int _originalSelectionLength;
      
              private void DoModalLoop()
              {
                  while (Visible)
                  {
                      typeof(Application).InvokeMember("DoEventsModal", BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, null);
                      MsgWaitForMultipleObjectsEx(0, IntPtr.Zero, 250, 0xff, MWMO_INPUTAVAILABLE);
                  }
              }
      
              protected override void OnDeactivate(EventArgs e)
              {
                  base.OnDeactivate(e);
                  _listBox._toolTip.Hide(_listBox);
                  HideList();
              }
      
              protected override void WndProc(ref Message m)
              {
                  // prevent resize handle on top & left of window
                  if (m.Msg == WM_NCHITTEST)
                  {
                      base.WndProc(ref m);
                      int ht = m.Result.ToInt32();
      
                      // if user hit left or top, pretend he didn't
                      if ((ht == HTLEFT) || (ht == HTBOTTOMLEFT) || (ht == HTTOP) || (ht == HTTOPLEFT) || (ht == HTTOPRIGHT))
                      {
                          m.Result = new IntPtr(HTNOWHERE);
                      }
                      return;
                  }
      
                  // ensure integral height and maximum size
                  if (m.Msg == WM_SIZING)
                  {
                      RECT rect = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
                      int h = rect.Bottom - rect.Top;
                      int newh = h;
                      if ((h % _listBox.ItemHeight) != 0)
                      {
                          newh = ((h + _listBox.ItemHeight) / _listBox.ItemHeight) * _listBox.ItemHeight;
                      }
      
                      if (newh > (_listBox.ItemHeight * _listBox.Items.Count))
                      {
                          newh = _listBox.ItemHeight * (_listBox.Items.Count + 1);
                      }
      
                      rect.Bottom = rect.Top + newh;
                      Marshal.StructureToPtr(rect, m.LParam, false);
                      m.Result = new IntPtr(1); // handled
                      return;
                  }
      
                  base.WndProc(ref m);
              }
          }
      }
      

      注意:这是 100% 免费组件实用程序的摘录:CodeFluent Runtime Client

      【讨论】:

        猜你喜欢
        • 2015-11-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多