Чи існує якась систематична стратегія проектування та впровадження графічних інтерфейсів?


18

Я використовую Visual Studio для створення програми GUI в C #. Панель інструментів служить чудовою палітрою компонентів, що дозволяє мені легко перетягувати кнопки та інші елементи (для наочності я кажу кнопку, коли я маю на увазі "управління") на свою форму, що робить статичні форми досить простими. Однак у мене виникають дві проблеми:

  • Створення кнопок в першу чергу - це велика робота. Якщо у мене є форма, яка не є статичною (тобто кнопки або інші елементи керування створюються під час виконання відповідно до того, що робить користувач), я взагалі не можу використовувати палітру. Натомість я повинен створити кожну кнопку вручну, викликаючи конструктор будь-яким методом, який я використовую, а потім ініціалізувати вручну, вказавши висоту кнопки, ширину, положення, мітку, обробник подій тощо. Це надзвичайно виснажливо, тому що я повинен здогадуватися про всі ці косметичні параметри, не маючи змоги побачити, як виглядатиме форма, а також створює багато рядків повторюваного коду для кожної кнопки.
  • Змусити кнопки щось робити - це теж велика робота. Справа з подіями в повнофункціональній програмі - це величезний біль. Єдиний спосіб, коли я знаю, як це зробити, це вибрати кнопку, перейти до вкладки подій у її властивостях, натиснути OnClickподію так, щоб вона генерувала подію в Formкоді s, а потім заповнила тіло події. Оскільки я хочу розділити логіку та презентацію, всі мої обробники подій в кінцевому підсумку є однолінійними дзвінками до відповідної функції ділової логіки. Але використовуючи це для багатьох кнопок (наприклад, уявіть, що кількість кнопок, присутніх у програмі, як MS Word), забруднює код мого Formдесятками методів обробника подій на платформі, і це важко підтримувати.

Через це будь-яка програма GUI, складніша за Hello World, для мене насправді дуже недоцільна. Щоб було зрозуміло, я не маю жодних проблем зі складністю програм, які я пишу, які мають мінімальний інтерфейс користувача - я відчуваю, що я в змозі використовувати OOP з достатньою мірою компетенції, щоб акуратно структурувати мій бізнес-логічний код. Але при розробці GUI я застряг. Це здається набридливим, що я відчуваю, що я вигадую колесо, а там десь книга, що пояснює, як правильно робити графічний інтерфейс, який я не читав.

Я щось пропускаю? Або всі розробники C # просто приймають нескінченні списки повторюваних обробників подій та код створення кнопок?


Як (сподіваюся, корисна) підказка, я сподіваюся, що хороша відповідь буде говорити про:

  • Використовуючи методи OOP (наприклад, заводський зразок) для спрощення повторного створення кнопок
  • Об'єднання багатьох обробників подій в єдиний метод, який перевіряє, Senderщоб визначити, яка кнопка викликала її, і поводиться відповідно
  • XAML та використання WPF замість Windows Forms

Звичайно, вам не потрібно нічого згадувати. Це просто найкраща здогадка про те, яку відповідь я шукаю.


Як я бачу, так, Фабрика була б гарною схемою, але я бачу набагато більше модель "Перегляд моделі-контролера" з командами, підключеними до елементів управління, а не безпосередньо подій. WPF зазвичай MVVM, але MVC цілком можливий. Наразі у нас є величезне програмне забезпечення у WPF, що використовує MVC на 90%. Усі команди передаються контролеру, і він обробляє все, що завгодно
Франк,

Чи не можете ви створити динамічну форму за допомогою всіх можливих кнопок за допомогою редактора GUI, а просто зробити речі, які вам не потрібні динамічно непомітними? Здається, що набагато менше роботи.
Ганс-Пітер Штерр

@hstoerr Але було б важко змусити макет працювати. Дуже багато кнопок перекриваються.
Супербест

Відповіді:


22

За ці роки було розроблено кілька методологій для вирішення цих згаданих вами питань, які, погоджуюсь, є двома основними проблемами, з якими в останні роки доводилося вирішувати рамки інтерфейсу користувача. Виходячи з фону WPF, до них підходять наступним чином:

Декларативний дизайн, а не імперативний

Коли ви описуєте кропітко писати код, щоб створити елементи керування та встановити їх властивості, ви описуєте імперативну модель дизайну інтерфейсу. Навіть з дизайнером WinForms ви просто використовуєте обгортку над цією моделлю - відкрийте файл Form1.Designer.cs і побачите весь код, що сидить там.

З WPF та XAML - і подібними моделями в інших структурах, звичайно, від HTML і далі - ви очікуєте, що ви опишете свій макет, і дозвольте рамці зробити важкий процес його реалізації. Ви використовуєте розумніші елементи керування, такі як Панелі (Grid, WrapPanel тощо), щоб описати взаємозв'язок між елементами інтерфейсу, але ви очікуєте, що ви не розміщуєте їх вручну. Гнучкі поняття, такі як повторювані елементи керування - наприклад, повторювач ASP.NET або WPF's ItemsControl - допомагають створювати динамічний масштабування інтерфейсу користувача, не записуючи повторюваний код, дозволяючи динамічно зростаючим об'єктам даних динамічно представлятись як елементи управління.

DataTemplates WPF дозволяють вам визначити - ще раз, декларативно - маленькі самородки корисності інтерфейсу користувача та порівняти їх із вашими даними; наприклад, список об’єктів даних Клієнта може бути прив’язаний до ItemControl та інший шаблон даних, який викликається залежно від того, чи він звичайний працівник (використовуйте стандартний шаблон рядка рядка з іменем та адресою) чи основний клієнт, тоді як інший шаблон , відображаються зображення та легко доступні кнопки. Знову ж таки, не записуючи код у конкретне вікно, просто розумніші елементи керування, які знають про їх контекст даних, і таким чином дозволяють вам просто прив’язати їх до своїх даних і дозволити їм виконувати відповідні операції.

Прив’язка даних та розділення команд

Торкаючись прив'язки даних, прив'язка даних WPF (і, знову ж таки, в інших структурах, як, наприклад, AngularJS) дозволяє вам заявити про свої наміри, пов’язавши елемент управління (скажімо, текстове поле) із суттю даних (скажімо, ім'я клієнта) та дозволити каркас обробляють сантехнікою. Логіка обробляється аналогічно. Замість того, щоб вручну з'єднати обробники подій, що стоять за кодом, для бізнес-логіки на основі контролера, ви використовуєте механізм прив'язки даних, щоб зв’язати поведінку контролера (скажімо, властивості кнопки Command) з об'єктом Command, який представляє саморіз діяльності.

Це дозволяє ділити цю команду між вікнами без переписування обробників подій кожного разу.

Вищий рівень абстракції

Обидва ці рішення ваших двох проблем являють собою перехід на більш високий рівень абстракції, ніж орієнтована на події парадигма Windows Forms, яку ви справедливо вважаєте стомлюючою.

Ідея полягає в тому, що ви не хочете визначати в коді кожну властивість, яку має елемент управління, і кожну поведінку, починаючи з натискання кнопки і далі. Ви хочете, щоб ваші елементи керування та основні рамки зробили більше роботи для вас і дозволили розглянути більш абстрактні поняття Прив'язка даних (яка існує в WinForms, але ніде не є такою корисною, як у WPF) та шаблон команд для визначення зв’язків між інтерфейсом користувача і поведінка, яка не потребує спуску до металу.

Модель MVVM - це підхід Microsoft до цієї парадигми. Я пропоную прочитати про це.

Це приблизний приклад того, як виглядатиме ця модель та як це заощадить ваш час та рядки коду. Це не компілюється, але це псевдо-WPF. :)

Десь у ресурсах програми ви визначаєте Шаблони даних:

<DataTemplate x:DataType="Customer">
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

<DataTemplate x:DataType="PrimeCustomer">
   <Image Source="GoldStar.png"/>
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

Тепер ви пов'язуєте свій головний екран із ViewModel, класом, який відкриває ваші дані. Скажімо, це сукупність клієнтів (просто a List<Customer>) та об'єкта Command (знову ж таки, проста загальнодоступна властивість типу ICommand). Це посилання дозволяє прив’язувати:

public class CustomersnViewModel
{
     public List<Customer> Customers {get;}
     public ICommand RefreshCustomerListCommand {get;}  
}

та інтерфейс користувача:

<ListBox ItemsSource="{Binding Customers}"/>
<Button Command="{Binding RefreshCustomerListCommand}">Refresh</Button>

І це все. Синтаксис ListBox захопить список клієнтів з ViewModel і спробує повернути їх до інтерфейсу користувача. Через два шаблони DataTemplates, які ми визначили раніше, він імпортує відповідний шаблон (на основі DataType, припускаючи, що PrimeCustomer успадковує від Замовника) і поставить його як вміст ListBox. Ні циклічного, ні динамічного генерування елементів керування за допомогою коду.

Аналогічно, кнопка має попередній синтаксис, який пов'язує її поведінку з реалізацією ICommand, яка, імовірно, знає оновити Customersвластивість - спонукаючи рамки, що зв'язують дані, оновити інтерфейс знову, автоматично.

Тут я, звичайно, взяв кілька ярликів, але в цьому суть.


5

По-перше, я рекомендую цю статтю серії: http://codebetter.com/jeremymiller/2007/07/26/the-build-your-own-cab-series-table-of-contents/ - вона не стосується детальних проблем з кодом кнопки, але він дає систематичну стратегію розробки та впровадження власної програми додатків, особливо для програм GUI, показуючи, як застосувати MVC, MVP, MVVM до типового додатка Forms.

Вирішення вашого питання щодо "поводження з подіями": якщо у вас багато форм з кнопками, що працюють подібним чином, можливо, ви самі окупитесь, щоб виконати призначення обробника подій у вашій "програмі". Замість того, щоб призначати події клацання за допомогою дизайнера GUI, створіть умову іменування, як обробник "click" повинен бути названий для кнопки конкретного імені. Наприклад - для кнопки MyNiftyButton_ClickHandlerдля кнопки MyNiftyButton. Тоді не дуже складно написати процедуру багаторазового використання (використовуючи відображення), яка переходить на всі кнопки форми, перевірте, чи форма містить відповідний обробник кліків і призначити обробника події автоматично. Дивіться тут приклад.

І якщо ви просто хочете використовувати лише один обробник подій для купки кнопок, це також буде можливим. Оскільки кожен обробник події передає відправника, а відправник - це завжди натиснута кнопка, ви можете відправити бізнес-код, використовуючи властивість "Ім'я" кожної кнопки.

Для динамічного створення кнопок (або інших елементів управління): ви можете створити кнопки чи інші елементи керування за допомогою конструктора у невикористаній формі лише для того, щоб створити шаблон . Потім ви використовуєте відображення (див. Тут приклад ) для клонування керування шаблоном та зміни лише властивостей, таких як розташування чи розмір. Це, мабуть, буде набагато простіше, ніж ініціалізувати два-три десятки властивостей вручну в коді.

Я писав це вище, маючи на увазі Winforms (як і ви, я думаю), але загальні принципи повинні застосовуватися до інших середовищ GUI, таких як WPF з подібними можливостями.


Дійсно це можна зробити у WPF, і це навіть набагато простіше. Ви просто заповнюєте список команд (попередньо закодованих конкретних функцій) і встановлюєте джерело у своєму вікні. Ви створюєте приємний простий візуальний шаблон, і все, що вам потрібно подбати, - це заповнення колекції потрібними командами, а велика прив'язка WPF піклується про решту для вас візуально.
Франк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.