Индикатор прогресса ProgressBar. События ввода и изменения

30.03.2019

Изменение громкости, баланса, скорости и позиции воспроизведения

Для управления громкостью, балансом, скоростью и текущей позицией медиафайла MediaElement предлагает те же свойства, что и MediaPlayer. Ниже показано простое окно, расширяющее пример аудиопроигрывателя из предыдущей статьи, с дополнительными элементами для управления этими параметрами.

Ползунки громкости и баланса привязать проще всего. Поскольку Volume и Balance - свойства зависимости, их элементы управления можно подключить к MediaElement с помощью выражения двунаправленной привязки.

Хотя выражения двунаправленной привязки требуют некоторых дополнительных накладных расходов, они обеспечивают обратную связь: если свойства MediaElement будут изменены каким-то другим способом, эти ползунки останутся синхронизированными с текущими значениями свойств. Свойство SpeedRatio может быть подключено аналогичным образом:

Громкость: Баланс: Скорость:

Однако здесь есть несколько нюансов. Во-первых, SpeedRatio не задействовано в управляемом таймером аудио (где применяется MediaTimeline). Чтобы использовать его, придется установить свойство LoadedBehavior из SpeedRatio в Manual и принять управление воспроизведением на себя через соответствующие методы.

В случае использования MediaTimeline от действия SetStoryboardSpeedRatio получается тот же эффект, что и от установки свойства MediaElement.SpeedRatio. Во-вторых, SpeedRatio не является свойством зависимости, и WPF не принимает уведомлений о его изменении. Это значит, что если свойство SpeedRatio модифицируется в коде, то ползунок соответствующим образом не обновляется. (Одним из обходных путей может быть изменение самого ползунка в коде вместо прямой модификации MediaElement.)

Изменение скорости воспроизведения аудио может исказить звук и вызвать появление эффектов вроде эха. И последняя деталь - текущая позиция, которая представлена свойством Position.

Опять-таки, для установки свойства Position элемент MediaElement должен находиться в режиме Manual, что означает невозможность применения MediaTimeline. (При использовании TimeLine подумайте о применении действия BeginStoryboard вместе с Offset для установки требуемой позиции.)

Чтобы заставить это работать, не применяйте никаких привязок данных в ползунке:

Для установки позиции ползунка при открытии медиафайла можно использовать код, подобный следующему:

Private void media_MediaOpened(object sender, RoutedEventArgs e) { sliderPosition.Maximum = media.NaturalDuration.TimeSpan.TotalSeconds; }

Затем при перемещении ползунка можно перепрыгнуть в определенную позицию:

Private void sliderPosition_ValueChanged(object sender, RoutedEventArgs e) { // Приостановка воспроизведения перед переходом в другую позицию // исключит "заикания" при слишком быстрых движениях ползунка, media.Pause(); media.Position = TimeSpan.FromSeconds(sliderPosition.Value); media.Play(); }

Недостаток такого решения состоит в том, что ползунок не будет обновляться по мере воспроизведения. Если же это необходимо, придется прибегнуть к обходному маневру (вроде применения элемента DispatcherTimer, который будет выполнять периодическую проверку текущей позиции в процессе воспроизведения и соответственно обновлять положение ползунка). То же самое справедливо и при использовании MediaTimeline. По разным причинам привязаться напрямую к информации MediaElement.Clock невозможно. Вместо этого придется обрабатывать событие Storyboard.CurrentTimeInvalidated.

Синхронизация анимации с аудио

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

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

Один прием, позволяющий связать воспроизведение с другими действиями, состоит в применении анимации ключевого кадра . Эту анимацию можно поместить вместе с MediaTimeline в одну раскадровку. Подобным образом можно применить определенные смещения времени в анимации, которые будут соответствовать определенным моментам времени в аудиофайле. Фактически, есть возможность даже воспользоваться программой от независимых разработчиков, которая может аннотировать аудио и экспортировать список важных моментов времени.

Затем эта информация применяется для установки времени каждого ключевого кадра. Используя анимацию ключевого кадра, важно установить свойство Storyboard.SlipBehavior в Slip. Это укажет, что анимация ключевого кадра не должна обгонять MediaTimeline, если происходит задержка воспроизведения. Это важно потому, что MediaTimeline может тормозить из-за буферизации (когда зависит от потока с сервера) или же, что бывает чаще, по причине задержки при загрузке.

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

Есть два вида полос прокрутки:

  1. Стандартные - являются составной частью окна (включая диалоговое окно). Базовый класс CWnd .
  2. Независимые - существуют независимо от окон (включая диалоговые окна). Базовый класс CScrollBar .

Обработка сообщений полосы прокрутки:

В отличие от других элементов управления, полосы прокрутки не генерируют сообщение WM_COMMAND . Вместо этого могут посылаться следующие сообщения: WM_VSCROLL и WM_HSCROLL для вертикальной и горизонтальной полос соответственно. Эти сообщения обрабатываются функциями OnVScroll() и OnHScroll() , прототипы которых таковы:

Afx_msg void CWnd::OnVScroll(UINT SBCode, INT Pos, CScrollBar *SB);
afx_msg void CWnd::OnHScroll(UINT SBCode, INT Pos, CScrollBar *SB);

Параметр SBCode содержит код выполненного над полосой прокрутки действия. Наиболее распространенные его значения таковы:

  • SB_LINEUP - Изменение позиции ползунка на одну позицию вверх.
  • SB_LINEDOWN - Изменение позиции ползунка на одну позицию вниз.
  • SB_PAGEUP - Изменение позиции ползунка на одну страницу вверх.
  • SB_PAGEDOWN - Изменение позиции ползунка на одну страницу вниз.
  • SB_LINELEFT - Изменение позиции ползунка на одну позицию влево.
  • SB_LINERIGHT - Изменение позиции ползунка на одну позицию вправо.
  • SB_PAGELEFT - Изменение позиции ползунка на одну страницу влево.
  • SB_PAGERIGHT - Изменение позиции ползунка на одну страницу вправо.
  • SB_THUMBPOSITION - Генерируется при перемещении ползунка на новую позицию.
  • SB_THUMBTRACK - В дополнение к SB_THUMBPOSITION , это сообщение генерируется если при перемещении ползунка на новое место, удерживается кнопка мышки. Это позволяет отслеживать позицию ползунка прежде чем он будет отпущен.

Pos - Указывает на текущую позицию ползунка. SB - Если сообщение сгенерировано стандартной полосой прокрутки, то параметр SB буде равен NULL . Если же оно было сгенерированно независимой полосой прокрутки, то параметр SB будет содержать указатель на объект полосы.

Задание и получение диапазона полосы прокрутки:

(По умолчанию) Стандартная полоса прокрутки имеет диапазон от 0 до 100. Независимая, имеет диапазон от 0 до 0, что означает необходимость предварительного задания диапазона.SetScrollRange() - эта функция служит для задания диапазона, и имеет два варианта: один для стандартных полос, другой для независимых.

Прототип для стандартной полосы:

Void CWnd::SetScrollRange(int Which, int Min, int Max, BOOL Redraw = TRUE);

  • Which - Определяет, для какого типа полос прокрутки задается диапазон: SB_VERT или SB_HORZ .
  • Min - Нижняя граница диапазона.
  • Max - Верхняя граница диапазона.
  • Redraw - Если равен TRUE , то полоса прокрутки будет перерисована.

Прототип для независимой полосы:

Void CScrollBar::SetScrollRange(int Min, int Max, BOOL Redraw = TRUE;)

При необходимости получения диапазона полосы прокрутки используется функция GetScrollRange() . Эта функция так же существует в двух вариантах:

Void CWnd::GetScrollRange(int Which, LPINT lpMin, LPINT lpMax) const;
void CScrollBar::GetScrollRange(LPINT lpMin, LPINT lpMax) const;

В первом варианте значением параметра Which должно быть либо SB_VERT , либо SB_HORZ . В обоих случаях значения нижней и верхней границы диапазона возвращаются в переменных, на которые указывают параметры lpMin и lpMax , соответственно.

Задание и получение позиции ползунка:

Управление полосами прокрутки осуществляется программно. Это означает, что программа должна изменять положение ползунка при необходимости. С этой целью используется функция SetScrollPos() . Для стандартных полос прокрутки такая функция определена в классе CWnd , и её прототип таков:

Int CWnd::SetScrollPos(int Which, int Pos, BOOL Redraw = TRUE);

Для независимых полос прокрутки прототип следующий:

Int CScrollBar::SetScrollPos(int Pos, BOOL Redraw = TRUE);

Получить текущую позицию ползунка можно при помощи функции GetScrollPos()

Int CWnd::GetScrollPos(int Which) const;
int CScrollBar::GetScrollPos() const;

В обеих функциях возвращается значение текущей позиции ползунка.

Функции SetScrollInfo() и GetScrollInfo() - "Только для Windows-95 и выше"

В среде Windows-95 функции GetScrollInfo() и SetScrollInfo() , позволяют заменить такие функции, как GetScrollPos() и SetScrollPos() . Новые функции работают с 32-разрядными данными, тогда как старые - только с 16-разрядными.

Для стандартных полос прокрутки используются функции:

BOOL CWnd::SetScrollInfo(int Which, LPSCROLLINFO lpSI, BOOL Redraw = TRUE);
BOOL CWnd::GetScrollInfo(int Which, LPSCROLLINFO lpSI, UINT Mask = SIF_ALL);

Для независимых полос прокрутки используются функции:

BOOL CScrollBar::SetScrollInfo(LPSCROLLINFO lpSI, BOOL Redraw = TRUE);
BOOL CScrollBar::GetScrollInfo(LPSCROLLINFO lpSI, UINT Mask = SIF_ALL);

Все функции возвращают ненулевое значение в случае успешного выполнения и ноль в противном случае.

  • Which - Это SB_VERT или SB_HORZ
  • Redraw - Отвечает за перерисовку полосы прокрутки.
  • Mask - Указывает на то, какая именно информация должна быть получена.
  • lpSI - Отвечает за установку атрибутов. Во всех вариантах функций параметр lpSI указывает на структуру типа SCROLLINFO .

Структура SCROLLINFO:

Typedef struct tagSCROLLINFO
{
UINT cbSize;// размер структуры SCROLLINFO
UINT fMask;// сведения о полученных атрибутах
int nMin;// нижняя граница диапазона
int nMax;// верхняя граница диапазона
UINT nPage;// размер страницы
int Pos;// позиция ползунка
int nTrackPos;// текущая позиция ползунка при перемещении
} SCROLLINFO;

Здесь в поле sbSize содержится размер структуры. Значение (или значения) поля fMask определяет, какие из последующих полей структуры содержат требуемую информацию. Эти значения могут быть следующими:

  • SIF_ALL - То же, что SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS
  • SIF_DISABLENOSCROLL - Если диапазон полосы прокрутки равен нулю, то она станет недоступной, но не будет удалена
  • SIF_PAGE - Поле nPage содержит информацию
  • SIF_POS - Поле nPos содержит информацию
  • SIF_RANGE - Поля nMin и nMax содержат информацию
  • SIF_TRACKPOS - Поле nTrackPos содержит информацию

Можно объединять сразу несколько из перечисленных констант с помощью операции побитового ИЛИ. Те же самые значения можно указывать в параметре Mask функции GetScrollInfo() . В поле nPage содержится текущий размер страницы для пропорциональных полос прокрутки. Поле nPos определяет позицию ползунка. Поля nMin и nMax содержат нижнюю и верхнюю границу диапазона полосы прокрутки. В поле nTrackPos содержится позиция ползунка, но не статическая, а динамическая, т.е. меняющаяся в процессе его перемещения. Это значение не может быть установлено.

Последнее обновление: 31.10.2015

TrackBar

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

Некоторые важные свойства TrackBar:

    Orientation : задает ориентацию ползунка - расположение по горизонтали или по вертикали

    TickStyle : задает расположение делений на ползунке

    TickFrequency : задает частоту делений на ползунке

    Minimum : минимальное возможное значение на ползунке (по умолчанию 0)

    Maximum : максимальное возможное значение на ползунке (по умолчанию 10)

    Value : текущее значение ползунка. Должно находиться между Minimum и Maximum

Свойство TickStyle может принимать ряд значений:

    None: деления отсутствуют

    Both: деления расположены по обеим сторонам ползунка

    BottomRight: у вертикального ползунка деления находятся справа, а у горизонтального - снизу

    TopLeft: у вертикального ползунка деления находятся слева, а у горизонтального - сверху (применяется по умолчанию)

К наиболее важным событиям элемента следует отнести событие Scroll , которое позволяет обработать перемещение ползунка от одного деления к другому. Что может быть полезно, если нам надо, например, устанавливать соответствующую громкость звука в зависимости от значения ползунка, либо какике-нибудь другие настройки:

Public partial class Form1: Form { public Form1() { InitializeComponent(); // установка обработчика события Scroll trackBar1.Scroll+=trackBar1_Scroll; } private void trackBar1_Scroll(object sender, EventArgs e) { label1.Text = String.Format("Текущее значение: {0}", trackBar1.Value); } }

Timer

Timer является компонентом для запуска действий, повторяющихся через определенный промежуток времени. Хотя он не является визуальным элементом, но его аткже можно перетащить с Панели Инструментов на форму:

Наиболее важные свойства и методы таймера:

    Свойство Enabled : при значении true указывает, что таймер будет запускаться вместе с запуском формы

    Свойство Interval : указывает интервал в миллисекундах, через который будет срабатывать обработчик события Tick, которое есть у таймера

    Метод Start() : запускает таймер

    Метод Stop() : останавливает таймер

Для примера определим простую форму, на которую добавим кнопку и таймер. В файле кода формы определим следующий код:

Public partial class Form1: Form { int koef = 1; public Form1() { InitializeComponent(); this.Width = 400; button1.Width = 40; button1.Left = 40; button1.Text = ""; button1.BackColor = Color.Aqua; timer1.Interval = 500; // 500 миллисекунд timer1.Enabled = true; button1.Click += button1_Click; timer1.Tick += timer1_Tick; } // обработчик события Tick таймера void timer1_Tick(object sender, EventArgs e) { if (button1.Left == (this.Width-button1.Width-10)) { koef=-1; } else if (button1.Left == 0) { koef = 1; } button1.Left += 10 *koef; } // обработчик нажатия на кнопку void button1_Click(object sender, EventArgs e) { if(timer1.Enabled==true) { timer1.Stop(); } else { timer1.Start(); } } }

Здесь в конструкторе формы устанавливаются начальные значения для таймера, кнопки и формы.

Через каждый интервал таймера будет срабатывать обработчик timer1_Tick , в котором изменяется положение кнопки по горизонтали с помощью свойства button1.Left . А с помощью дополнительной переменной koef можно управлять направлением движения.

Кроме того, с помощью обраотчика нажатия кнопки button1_Click можно либо остановить таймер (и вместе с ним движение кнопки), либо опять его запустить.

Индикатор прогресса ProgressBar

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

Наиболее важые свойства ProgressBar:

    Minimum : минимальное возможное значение

    Maximum : максимальное возможное значение

    Value : текущее значение элемента

    Step : шаг, на который изменится значение Value при вызове метода PerformStep

Для имитации работы прогрессбара поместим на форму таймер и в коде формы определим следующий код:

Public partial class Form1: Form { public Form1() { InitializeComponent(); timer1.Interval = 500; // 500 миллисекунд timer1.Enabled = true; timer1.Tick += timer1_Tick; } // обработчик события Tick таймера void timer1_Tick(object sender, EventArgs e) { progressBar1.PerformStep(); } }