Введение


После размещения первой статьи о своем приложении FAQ.Net (программа заметок под Windows), появились первые пользователи, для которых хочется дальше развивать свою программу для заметок.

Чтобы повысить интерес у пользователей, возникла идея повысить его привлекательность за счет настройки цвета и шрифта.

Требования к настройке внешнего вида приложения


  1. хранить и загружать свойства отдельных компонентов в XML-файле
  2. пользователь должен иметь простой инструмент по настройке внешнего вида
  3. список настраиваемых компонентов должен очень просто расширяться (одной строкой кода)
  4. при изменении значения свойства пользователь должен сразу видеть изменения на форме
  5. должна быть кнопка отмены, которая позволит вернуть все изменения назад

Результат работы


Окно настройки внешнего вида:



До и после настроек внешнего вида:



Для решения задачи необходимо


  1. использовать компонент PropertyGridEx, для отображения и изменения свойств компонентов
  2. создать класс CustomDesignControl для удобства хранения свойств в XML
  3. создать форму MainForm, на которой будем изменять внешний вид
  4. создать форму AppSettingsForm, на которой будем настраивать внешний вид MainForm
  5. создать класс SettingsXml для удобства работы с XML

Функция отмены изменений внешнего вида


Чтобы реализовать отмену изменений внешнего вида (пункт 5 из требований), решил найти универсальный способ клонирования свойств компонентов которые будут создаваться в памяти и заменять отображаемые компоненты.

Клонирование объектов:

public static class ControlExtensions
  {
    public static T Clone<T>(T controlToClone)
        where T : Control
    {
      PropertyInfo[] controlProperties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);

      T instance = Activator.CreateInstance<T>();

      RichTextBox rtb = new RichTextBox();
      foreach (PropertyInfo propInfo in controlProperties)
      {
        if (propInfo.Name == "Parent")
          continue;
        if (propInfo.CanWrite)
        {
          if (propInfo.Name != "WindowTarget")
          {
              propInfo.SetValue(instance, propInfo.GetValue(controlToClone, null), null);
          }
        }
      }
      return instance;
    }

Скопировал код, убрал лишние свойства, которые не нужно сохранять, оставив цвета и шрифт.
После проверки работы кода, сразу выяснились недостатки данного способа:

  1. используется приведение любого контрола к классу Control, а это означает, что половину свойств можно потерять (например, компонент DataGridView унаследован от класса Control, но имеет свой дополнительный набор свойств)
  2. при клонировании компонента некоторые свойства (например: цвет шрифта внутри ячейки DataGridView) не клонируются, а создают ссылки на клонируемый компонент (возможно, это потому, что используется свойство со своими внутренними свойствами и их нужно как-то по-другому обрабатывать).

Начал экспериментировать над вторым пунктом, чтобы добиться именно клонирования свойства.
Для этого создавал объект нужного типа оператором new.

DataGridView dgvClone = new DataGridView();

Безрезультатно, компонент не смог вернуть изменения обратно.

Попробовал тогда само свойство создать оператором new и задать ему значение методом SetValue:

propInfo.SetValue(_controls[i], propInfo.GetValue(_controlsBeforeChange[i], null), null);

И этот способ не дал результата. При изменении значения свойства в одном компоненте, оно автоматически обновляется и в клонируемом компоненте.

Другие способы я больше не искал и сделал возврат изменений обычным перезапуском приложения с возможностью сохранения рабочего документа или отмены перезапуска. Это позволило значительно съэкономить память приложения, так как не нужно создавать клоны объектов.

Пример приложения с настройкой внешнего вида


На всякий случай, подготовил готовый код, на котором желающие смогут проверить и возможно доработать функцию отмены изменений внешнего вида без перезагрузки приложения и рассказать нам о проблеме в комментариях.
Нужно добиться, чтобы при нажатии на кнопку «Автотест» на компоненте DataGridView был серый фон и шрифт ячеек был мелкий.



Скачать Windows приложение FAQ.Net (бесплатно):

(x64) yadi.sk/d/sq3g4NOD3Zt7ZY
(x86) yadi.sk/d/NKXeyUWH3Zt8PQ

Группа ВКонтакте
Исходный код FAQ.Net

Вывод


Надеюсь, что во многих WinForms приложениях, благодаря этой статье, у конечного пользователя появится возможность настраивать интерфейс на свой вкус и цвет.

Комментарии (13)


  1. xkondorx
    01.11.2019 08:16

    Тема раскрыта, но зачем?


  1. SeyranGV
    01.11.2019 08:45

    Как же видно старого делфиста по реализации GUI приложения.


    1. GennPen
      01.11.2019 09:02
      +1

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


      1. Zam_dev
        01.11.2019 09:50

        Насчет аляпавато — возможно, но при этом не умаляет практичность и интуитивность.


        1. SeyranGV
          01.11.2019 21:31
          +2

          вы зря ребят минусуете, в некоторых случаях пестро раскрашенные интерфейсы очень даже помогают, два простых примера:
          1. Просто кассир розничной точки
          2. Оператор на потоке, упаковка или сортировка, не важно

          В обоих случаях внимание людей сильно сосредоточено на основной функции, это торговля, либо поток продукции, а взаимодействие с интерфейсом программы уже второстепенная функция. В обоих случаях в интерфейсе программ много кнопок с надписями. Смотришь на интерфейс программы, все понятно, кнопки подписаны лаконично но понятно, но операторы постоянно ошибаются нажимая не туда и теряют много времени на возврат действий. В итоге помогла контрастная раскраска этих кнопок, то есть у каждой кнопки в интерфейсе свой пестрый цвет, и операторы перестали ошибаться.


  1. shmelev-1987 Автор
    01.11.2019 08:45

    Может подскажете ссылку на готовое решение? Я бы почитал


    1. Zam_dev
      01.11.2019 09:43

      Спасибо, давно хотел сделать такое на шарпе, лень было вникать.


  1. maxzh83
    01.11.2019 09:58
    +3

    Что-то результат «после» слабоват. Где кислотные цвета, где шрифт Comic Sans?


    1. shmelev-1987 Автор
      01.11.2019 10:09

      Возможно, цвета и шрифт выглядят кислотно и я не дизайнер, но фишка была не в этом, а в том, что пользователь сам может настроить любой цвет и шрифт. Я делал эти цвета на быструю руку, чтобы показать что все это работает.
      Зато теперь у пользователей есть реальная возможность сделать такие же цвета, как в Visual Studio, например. А изменение шрифта на более крупный, позволит настроить приложение слабовидящим.


      1. maxzh83
        01.11.2019 10:45
        +2

        Если серьезно, то тут главный вопрос: зачем? Есть операционная система, у нее есть свои правила построения интерфейса, люди пишут большие гайды на эту тему. Пользователи привыкают к этому поведению и внешнему виду, хотят, например, один раз настроить цвета и шрифты в настройках винды.
        Есть исключения из этого правила, всякие плееры, например, но они совсем игнорируют все что можно, используют картинки, крутилки и прочее. А бизнес приложения все же стараются использовать общепринятый в ОС вид.

        А изменение шрифта на более крупный, позволит настроить приложение слабовидящим.

        Слабовидящие выберут контрастную тему в Windows.


  1. a-tk
    01.11.2019 10:43

    Use WPFAvalonia, Luke!


    1. shmelev-1987 Автор
      01.11.2019 10:56

      Спасибо, но это XAML. Есть что-то подобное под WinForms?


      1. a-tk
        01.11.2019 13:29

        Речь не о XAML-е, а о куда более развитых способах стилизации интерфейса.