BillWoodruff
Цитата:
Дэйв к. писал: "используя ENTER для перемещения из поля в поле, вы идете против стандартной функциональности приложений Windows."
Я уважительно не согласен с этим утверждением в том смысле, что считаю его слишком обобщенным, слишком абсолютным.
Существует множество сценариев, в которых приложение управляет фокусом взаимодействия пользователя от элемента управления к элементу управления в соответствии с некоторым набором правил или в последовательном порядке. Хороший пример - "волшебник".
Дизайнеры из окна формы позволяют любой элемент управления должен быть удален из
Остановка табуляции просто установив свойство. Для TextBox и RichTextBox оба предлагают контроль над тем, принимаются ли вкладки, а текстовое поле позволяет игнорировать ввод.
Эти варианты, я полагаю, существуют не просто так: чтобы позволить разработчикам иметь гибкость при проектировании поведения приложений.
Теперь позвольте мне показать вам довольно полный эскиз (это означает, что он работает в VS 2017) для WinForm UserControl, который управляет последовательным вводом данных пользователей через шесть текстовых полей. Этот UserControl имеет две кнопки: "btnSubmit" и "btnCancel"..
Все элементы управления в пользовательских элементов управления имеют свои свойства tabstop значение false. Текстовые поля имеют свойства AcceptsTab и AcceptsReturn, установленные в false.
Все ключ-запись на пользовательских элементов управления перехватывается за езду метода processcmdkey.
Валидаторы прикрепляются к первым трем текстовым полям: первое требует заполнения по крайней мере одной цифры; второе требует заполнения 2 цифр и 2 знаков препинания; третье требует ввода текста любого типа. В остальных трех текстовых полях определен валидатор по умолчанию, который принимает все, что угодно, или вообще никакой текст.
Когда пользователь достигает последнего текстового поля, если нет ошибки проверки, кнопка Отправить включена.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace MatrixTestForm
{
public partial class DataEntryUserControl : UserControl
{
public DataEntryUserControl()
{
InitializeComponent();
}
public Action<List<string>> SendData;
public Dictionary<TextBox,Func<TextBox, bool>> TxtBxesToValidator = new Dictionary<TextBox, Func<TextBox, Boolean>>();
private bool allValid = false;
private List<TextBox> textBoxes = new List<TextBox>();
private TextBox activeTextBox;
private int lastTBoxIndex;
private void DataEntryUserControl_Load(object sender, EventArgs e)
{
btnSubmit.Enabled = false;
textBoxes.AddRange(new List<TextBox>()
{
textBox1, textBox2, textBox3, textBox4, textBox5, textBox6
});
lastTBoxIndex = textBoxes.Count - 1; // adjust for #0 offset
foreach (var tbx in textBoxes)
{
tbx.Enter += TbxOnEnter;
tbx.Leave += TbxOnLeave;
}
activeTextBox = textBox1;
textBox1.Capture = true;
// no digits
toolTip1.SetToolTip(textBox1, "no digits");
TxtBxesToValidator[textBox1] = box =>
{
return box.Text != null && !box.Text.Any(char.IsDigit);
};
// must have 2 digit2 and 2 punctuation
toolTip1.SetToolTip(textBox2,"2 digits, 2 ounctuation");
TxtBxesToValidator[textBox2] = box =>
{
return box.Text != null
&& box.Text.Any(char.IsDigit)
&& box.Text.Where(char.IsDigit).Count() == 2
&& box.Text.Any(char.IsPunctuation)
&& box.Text.Where(char.IsPunctuation).Count() == 2;
};
// gotta have something
toolTip1.SetToolTip(textBox3, "enter something");
TxtBxesToValidator[textBox3] = box =>
{
return !String.IsNullOrWhiteSpace(box.Text);
};
}
private void TbxOnEnter(Object sender, EventArgs eventArgs)
{
activeTextBox = sender as TextBox;
activeTextBox.Capture = true;
}
private void TbxOnLeave(Object sender, EventArgs eventArgs)
{
activeTextBox.Capture = false;
activeTextBox = null;
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Enter && activeTextBox != null)
{
activeTextBox.Capture = false;
// validator
if (TxtBxesToValidator.ContainsKey(activeTextBox))
{
allValid = TxtBxesToValidator[activeTextBox](activeTextBox);
}
if (! allValid)
{
btnSubmit.Enabled = false;
MessageBox.Show("invalid");
return base.ProcessCmdKey(ref msg, keyData);
}
int ndx = textBoxes.IndexOf(activeTextBox);
if (ndx == lastTBoxIndex)
{
btnSubmit.Enabled = allValid;
}
else
{
textBoxes[ndx + 1].Focus();
textBoxes[ndx + 1].Capture = true;
}
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
private void btnCancel_Click(object sender, EventArgs e)
{
btnSubmit.Enabled = false;
// whatever : hide the UserControl ?
}
private void btnSubmit_Click(object sender, EventArgs e)
{
// send the valid data back to wherever ?
SendData?.Invoke(textBoxes.Select(tbx => tbx.Text).ToList());
// clean-up
// hide the UserControl ?
// clear the TextBoxes ?
// other ??
}
}
}
BillWoodruff
Наш опыт отличается, брат Дейв :) Возможно, Вам будет интересно отметить, что приведенный пример кода не ограничивает пользователя последовательным вводом: ничто не мешает пользователю использовать мышь для выбора любого из текстовых полей для ввода (конечно, это может быть реализовано).
Держу пари, у вас нет проблем с написанием "защитных предложений" в ваших обработчиках событий, чтобы избежать распространения ошибок, проверки на нули и т. д. Почему бы не применить тот же принцип в дизайне пользовательского интерфейса ?
Обратите внимание, что в этом примере я не использовал ErrorProvider; обычно я бы это сделал.