Найкращий дизайн для форм Windows, які матимуть спільний функціонал


20

Раніше я використовував успадкування, щоб дозволити розширення форм Windows у своїй програмі. Якщо всі мої форми мали б загальні елементи управління, твори мистецтва та функціональність, я створив б базову форму, що реалізує загальні елементи керування та функціональні можливості, а потім дозволю іншим елементам управління успадковувати цю базову форму. Однак у мене виникли кілька проблем із цим дизайном.

  1. Одночасно керування може бути в одному контейнері, тому будь-які статичні елементи керування будуть складними. Наприклад: Припустимо, у вас була базова форма під назвою BaseForm, яка містила TreeView, який ви створюєте захищеним та статичним, щоб усі інші (похідні) екземпляри цього класу могли змінювати та відображати той самий TreeView. Це не буде працювати для декількох класів, успадкованих від BaseForm, тому що TreeView може бути лише в одному контейнері одночасно. Ймовірно, це буде остання форма ініціалізована. Хоча кожен екземпляр міг редагувати елемент керування, він відображатиметься лише в одному в заданий час. Звичайно, є робочі місця, але всі вони некрасиві. (Це здається мені дуже поганим дизайном. Чому декілька контейнерів не можуть зберігати вказівники на один і той же об’єкт? Як би там не було.)

  2. Стан між формами, тобто стану кнопок, текст мітки тощо, я повинен використовувати глобальні змінні для та скидання станів на Load.

  3. Дизайнер Visual Studio не дуже добре підтримує це.

Чи є кращий, але все ще легко ретельний дизайн у використанні? Або успадкування форми все ще є найкращим підходом?

Оновлення Я перейшов від перегляду MVC до MVP до шаблона спостерігача до шаблону події. Ось що я зараз думаю, будь ласка, критикуйте:

Мій клас BaseForm міститиме лише елементи керування та події, пов'язані з цими елементами управління. Усі події, які потребують будь-якої логіки для їх оброблення, негайно перейдуть до класу BaseFormPresenter. Цей клас буде обробляти дані з інтерфейсу користувача, виконувати будь-які логічні операції, а потім оновлювати BaseFormModel. Модель буде виставляти події, які спричинятимуть зміни стану, класу Presenter, на який він підпишеться (або буде спостерігати). Коли Ведучий отримає сповіщення про подію, він виконає будь-яку логіку, і тоді Презентатор змінить Вигляд відповідно.

У пам’яті буде лише один клас кожного класу Model, але потенційно може бути багато примірників BaseForm і, отже, BaseFormPresenter. Це вирішило б мою проблему синхронізації кожного екземпляра BaseForm з тією ж моделлю даних.

Запитання:

На якому шарі слід зберігати такі речі, як остання натиснута кнопка, щоб я міг залишати її виділеною для користувача (як у меню CSS) між формами?

Будь ласка, критикуйте цей дизайн. Спасибі за вашу допомогу!


Я не бачу, чому ви змушені використовувати глобальні змінні, але якщо так, то так, безумовно, повинен бути кращий підхід. Може, фабрики створюють загальні компоненти в поєднанні зі складом замість спадкування?
stijn

Весь ваш дизайн недосконалий. Якщо ви вже розумієте, що ви хочете зробити, це не підтримує Visual Studio, який повинен вам щось сказати.
Рамхаунд

1
@Ramhound Його підтримує візуальна студія, просто не дуже добре. Ось як Microsoft каже вам це зробити. Мені просто здається, що це біль у А. msdn.microsoft.com/en-us/library/aa983613(v=vs.71).aspx Як би там не було, я маю на увазі все.
Джонатан Хенсон

@stijn Я вважаю, що я міг би мати метод у кожній базовій формі, який буде завантажувати контрольні стани в інший екземпляр. тобто LoadStatesToNewInstance (екземпляр BaseForm). Я можу назвати це будь-коли, коли хочу показати нову форму цього базового типу. form_I_am_about_to_hide.LoadStatesToNewInstance (це);
Джонатан Хенсон

Я не розробник .net, чи можете ви розширити пункт № 1? У мене може бути рішення
Імран Омар Бухш

Відповіді:


6
  1. Я не знаю, навіщо вам потрібні статичні елементи керування. Можливо, ти знаєш щось, чого я не знаю. Я використовував багато візуального успадкування, але ніколи не бачив необхідності статичних елементів керування. Якщо у вас є загальний елемент управління переглядом дерев, дозвольте кожному екземпляру форми мати свій власний екземпляр елемента керування та поділитися одним екземпляром даних, пов'язаних з переглядами дерев.

  2. Спільний стан контролю (на відміну від даних) між формами також є незвичною вимогою. Ви впевнені, що FormB справді повинен знати про стан кнопок на FormA? Розгляньте конструкції MVP або MVC. Розгляньте кожну форму як німий "погляд", який нічого не знає про інші погляди чи навіть про саму програму. Контролюйте кожен вид за допомогою розумного ведучого / контролера. Якщо це має сенс, один ведучий може контролювати кілька переглядів. Пов’язати об'єкт стану з кожним видом. Якщо у вас є деякий стан, який потрібно розділити між переглядами, дозвольте ведучому (-ям) опосередкувати це (і врахуйте прив'язку даних - див. Нижче).

  3. Погоджено, Visual Studio доставить вам головні болі. Розглядаючи спадщину форми або користувальницького контролю, вам потрібно ретельно зважити переваги проти потенційної (та ймовірної) вартості боротьби з фрустраційними химерностями та обмеженнями дизайнера форми. Я пропоную звести спадщину форми до мінімуму - використовуйте її лише тоді, коли окупність висока. Майте на увазі, що, як альтернатива підкласифікації, ви можете створити загальну "базову" форму і просто інстанціювати її один раз для кожної потенційної "дитини", а потім налаштувати її під час руху. Це має сенс, коли відмінності між кожною версією форми незначні порівняно із спільними аспектами. (IOW: складна основна форма, лише-трохи-складніші дитячі форми)

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

Я думаю, що найважливіша порада, яку я можу запропонувати, - якщо ви зараз не використовуєте певну форму схеми перегляду / контролера, я настійно рекомендую вам почати це робити. Це змушує вас навчитися та оцінити переваги розшарування та розділення шарів.

відповідь на ваше оновлення

На якому шарі слід зберігати такі речі, як остання натиснута кнопка, щоб я міг залишати її виділеною для користувача ...

Ви можете поділитися станом між переглядами так, як ви поділилися б станом між ведучим та його переглядом. Створіть спеціальний клас SharedViewState. Для простоти ви можете зробити його однотонним, або ви можете виділити його в основний ведучий і передати звідти всі перегляди (через їх презентаторів). Коли стан пов'язаний з елементами управління, використовуйте прив'язку даних, де це можливо. Більшість Controlвластивостей можуть бути пов'язані з даними. Наприклад, властивість BackColor кнопки може бути пов'язана з властивістю класу SharedViewState. Якщо ви зробите це прив'язка до всіх форм, які мають однакові кнопки, ви можете виділити Button1 на всіх формах, просто встановивши SharedViewState.Button1BackColor = someColor.

Якщо ви не знайомі з прив'язкою даних WinForms, натисніть MSDN і прочитайте читання. Це не складно. Дізнайтеся INotifyPropertyChangedі ви вже на півдорозі.

Ось типова реалізація класу viewstate з властивістю Button1BackColor як приклад:

public class SharedViewState : INotifyPropertyChanged
{
    // boilerplate INotifyPropertyChanged stuff
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    // example of a property for data-binding
    private Color button1BackColor;
    public Color Button1BackColor
    {
        get { return button1BackColor; }
        set
        {
            if (value != button1BackColor)
            {
                button1BackColor = value;
                NotifyPropertyChanged("Button1BackColor");
            }
        }
    }
}

Дякую за вашу продуману відповідь. Чи знаєте ви добру посилання на модель перегляду / контролера?
Джонатан Хенсон

1
Я пам’ятаю, думав, що це було досить непогано: codebetter.com/jeremymiller/2007/05/22/…
Ігбі Ларгеман

будь ласка, дивіться моє оновлення.
Джонатан Хенсон

@JonathanHenson: відповідь оновлена.
Ігбі Ларгеман

5

Я підтримую нову програму WinForms / WPF на основі шаблону MVVM та елементів керування оновленнями . Він розпочався як WinForms, потім я створив версію WPF через маркетингову важливість інтерфейсу, який виглядає добре. Я подумав, що було б цікавим дизайнерським обмеженням підтримувати додаток, який підтримує дві абсолютно різні технології інтерфейсу з одним і тим самим заднім кодом (viewmodels та моделі), і я мушу сказати, що я цілком задоволений таким підходом.

У своєму додатку, коли мені потрібно ділитися функціональністю між частинами інтерфейсу, я використовую власні елементи керування або елементи управління (класи, отримані з WPF UserControl або WinForms UserControl). У версії WinForms є UserControl всередині іншого UserControl всередині похідного класу TabPage, який, нарешті, знаходиться всередині елемента управління елементами в основній формі. Версія WPF насправді має UserControls, які вкладені на один рівень глибше. Використовуючи власні елементи керування, ви можете легко скласти новий інтерфейс користувача з UserControls, який ви створили раніше.

Оскільки я використовую шаблон MVVM, я вкладаю якомога більше логіки програми у ViewModel (включаючи модель презентації / навігації ) або модель (залежно від того, чи пов'язаний код з користувальницьким інтерфейсом чи ні), але оскільки той самий ViewModel використовується як в представленнях WinForms, так і в WPF, ViewModel не може містити код, призначений для WinForms або WPF або безпосередньо взаємодіючи з інтерфейсом користувача; такий код ОБОВ'ЯЗКОВО перейде до перегляду.

Також у шаблоні MVVM об’єкти інтерфейсу повинні уникати взаємодії один з одним! Натомість вони взаємодіють із моделями перегляду. Наприклад, TextBox не запитає сусідній ListBox, який пункт обрано; замість цього поле списку збереже посилання на вибраний на даний момент елемент десь у шарі перегляду, і TextBox запитає шар перегляду, щоб дізнатися, що обрано зараз. Це вирішує вашу проблему щодо пошуку того, яка кнопка була натиснута в одній формі з іншої форми. Ви просто поділите об'єкт навігаційної моделі (який є частиною шару перегляду модуля програми) між двома формами і помістіть у цей об'єкт властивість, яка представляє, яку кнопку було натиснуто.

Сам WinForms не дуже добре підтримує шаблон MVVM, але Update Controls забезпечує власний унікальний підхід MVVM як бібліотеку, що сидить на вершині WinForms.

На мою думку, такий підхід до розробки програм працює дуже добре, і я планую використовувати його в майбутніх проектах. Причини, по яких це працює так добре, є (1), що Update Controls управляє залежностями автоматично, і (2), що зазвичай дуже зрозуміло, як слід структурувати свій код: весь код, який взаємодіє з об'єктами інтерфейсу, належить до представлення, весь код, який є Користувальницький інтерфейс, але НЕ HAVE взаємодіяти з об'єктами інтерфейсу, належить до ViewModel. Досить часто ви розділите код на дві частини, одну частину для перегляду та іншу частину для ViewModel. У мене виникли труднощі з розробкою контекстних меню в моїй системі, але врешті-решт я придумав і дизайн для цього.

Я також захоплююсь питаннями Control Controls у своєму блозі . Цей підхід вимагає багато звикання, але в масштабних додатках (наприклад, якщо ваші списки списків містять тисячі елементів) ви можете зіткнутися з проблемами продуктивності через поточні обмеження автоматичного управління залежностями.


3

Я збираюся відповісти на це, навіть якщо ви вже прийняли відповідь.

Як вказували інші, я не розумію, чому вам довелося використовувати щось статичне; це звучить, як ви робили щось дуже неправильно.

У всякому разі, у мене була така ж проблема, як і у вас: у моєму додатку WinForms у мене є кілька форм, які поділяють деяку функціональність та деякі елементи управління. Крім того, всі мої форми вже походять від базової форми (назвемо її "MyForm"), яка додає функціональний рівень на рівні (незалежно від застосування.) Дизайнер форм Visual Studio нібито підтримує редагування форм, успадкованих з інших форм, але в На практиці це працює лише до тих пір, поки ваші форми не будуть робити нічого, крім "Привіт, світ! - Добре - Скасувати".

Що я в кінцевому підсумку робив так: я зберігаю свій загальний базовий клас "MyForm", який є досить складним, і я продовжую отримувати з нього всі форми моєї заявки. Однак у цих формах я абсолютно нічого не роблю, тому редактор форм VS не має проблем із їх редагуванням. Ці форми складаються виключно з коду, який створює Дизайнер форм для них. Потім у мене є окрема паралельна ієрархія об'єктів, які я називаю "Сурогати", які містять усі функціональні можливості програми, такі як ініціалізація елементів керування, обробка подій, породжених формою та елементами управління тощо. Є один-на-один відповідність між сурогатними класами та діалогами в моїй заявці: є базовий клас сурогатів, який відповідає "MyForm", потім виводиться інший клас сурогатів, який відповідає "MyApplicationForm",

Кожен сурогат приймає за параметр часу побудови певний тип форми, і він приєднує себе до нього, реєструючи для своїх подій. Він також делегує на базу, аж до "MySurrogate", який приймає "MyForm". Цей сурогат реєструє подію форми "Позбуто", щоб, коли форма знищується, сурогат посилається на себе переборний, щоб він і всі його нащадки могли проводити очищення. (Скасування реєстрації подій тощо)

Поки що це добре працює.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.