| File: System\Windows\Forms\Dialogs\TaskDialog\TaskDialogProgressBar.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace System.Windows.Forms; /// <summary> /// Represents a progress bar control of a task dialog. /// </summary> public sealed class TaskDialogProgressBar : TaskDialogControl { private TaskDialogProgressBarState _state; private TaskDialogProgressBarState _stateWhenBound; private int _minimum; private int _maximum = 100; private int _value; private int _marqueeSpeed; /// <summary> /// Initializes a new instance of the <see cref="TaskDialogProgressBar"/> class. /// </summary> public TaskDialogProgressBar() { } /// <summary> /// Initializes a new instance of the <see cref="TaskDialogProgressBar"/> class /// using the given <paramref name="state"/>. /// </summary> /// <param name="state">The state of the progress bar.</param> public TaskDialogProgressBar(TaskDialogProgressBarState state) : this() { // Use the setter which will validate the value. State = state; } /// <summary> /// Gets or sets the state of the progress bar. /// </summary> /// <value> /// The state of the progress bar. The default is <see cref="TaskDialogProgressBarState.Normal"/>, /// except if this instance is the default instance created by a <see cref="TaskDialogPage"/>, /// where the default value is <see cref="TaskDialogProgressBarState.None"/>. /// </value> /// <remarks> /// <para> /// This control will only be shown if this property is not /// <see cref="TaskDialogProgressBarState.None"/>. /// </para> /// <para> /// This property can be set while the dialog is shown. However, while the dialog is /// shown, it is not possible to change the state from /// <see cref="TaskDialogProgressBarState.None"/> to any other state, /// and vice versa. /// </para> /// </remarks> /// <exception cref="InvalidOperationException"> /// The property is set on a progress bar instance that is currently bound to a task dialog, but the value /// to be set is <see cref="TaskDialogProgressBarState.None"/>. /// - or - /// The property is set on a progress bar instance that is currently bound to a task dialog, but it's not visible as its initial /// <see cref="State"/> property value was <see cref="TaskDialogProgressBarState.None"/>. /// - or - /// The property is set on a progress bar instance that is currently bound to a task dialog, but the dialog /// has just started navigating to a different page. /// </exception> public TaskDialogProgressBarState State { get => _state; set { SourceGenerated.EnumValidator.Validate(value); DenyIfBoundAndNotCreated(); if (BoundPage is not null && value == TaskDialogProgressBarState.None) { throw new InvalidOperationException( SR.TaskDialogCannotRemoveProgressBarWhileDialogIsShown); } TaskDialogProgressBarState previousState = _state; _state = value; try { if (BoundPage?.WaitingForInitialization == false) { UpdateState(previousState); } } catch { // Revert to the previous state. This could happen if the dialog's // DenyIfDialogNotUpdatable() (called by one of the Set...() methods) // throws. _state = previousState; throw; } } } /// <summary> /// Gets or sets the minimum value of the range of the control. /// </summary> /// <value> /// The minimum value of the range. The default is <c>0</c>. /// </value> /// <remarks> /// <para> /// This value is only used if the progress bar is not a marquee progress bar (as defined /// by the <see cref="State"/> property). /// </para> /// <para> /// This property can be set while the dialog is shown. /// </para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException"> /// The value is less than 0 or greater than <see cref="ushort.MaxValue" />. /// </exception> /// <exception cref="InvalidOperationException"> /// The property is set on a progress bar instance that is currently bound to a task dialog, but it's not visible as its initial /// <see cref="State"/> property value was <see cref="TaskDialogProgressBarState.None"/>. /// - or - /// The property is set on a progress bar instance that is currently bound to a task dialog, but the dialog /// has just started navigating to a different page. /// </exception> public int Minimum { get => _minimum; set { ArgumentOutOfRangeException.ThrowIfNegative(value); ArgumentOutOfRangeException.ThrowIfGreaterThan(value, ushort.MaxValue); DenyIfBoundAndNotCreated(); // We only update the task dialog if the current state is a // non-marquee progress bar. if (BoundPage?.WaitingForInitialization == false && !ProgressBarStateIsMarquee(_state)) { BoundPage.BoundDialog!.SetProgressBarRange(value, _maximum); } _minimum = value; } } /// <summary> /// Gets or sets the maximum value of the range of the control. /// </summary> /// <value> /// The maximum value of the range. The default is <c>100</c>. /// </value> /// <remarks> /// <para> /// This value is only used if the progress bar is not a marquee progress bar (as defined /// by the <see cref="State"/> property). /// </para> /// <para> /// This property can be set while the dialog is shown. /// </para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException"> /// The value is less than 0 or greater than <see cref="ushort.MaxValue" />. /// </exception> /// <exception cref="InvalidOperationException"> /// The property is set on a progress bar instance that is currently bound to a task dialog, but it's not visible as its initial /// <see cref="State"/> property value was <see cref="TaskDialogProgressBarState.None"/>. /// - or - /// The property is set on a progress bar instance that is currently bound to a task dialog, but the dialog /// has just started navigating to a different page. /// </exception> public int Maximum { get => _maximum; set { ArgumentOutOfRangeException.ThrowIfNegative(value); ArgumentOutOfRangeException.ThrowIfGreaterThan(value, ushort.MaxValue); DenyIfBoundAndNotCreated(); // We only update the task dialog if the current state is a // non-marquee progress bar. if (BoundPage?.WaitingForInitialization == false && !ProgressBarStateIsMarquee(_state)) { BoundPage.BoundDialog!.SetProgressBarRange(_minimum, value); } _maximum = value; } } /// <summary> /// Gets or sets the current position of the progress bar. /// </summary> /// <value> /// The position within the range of the progress bar. The default is <c>0</c>. /// </value> /// <remarks> /// <para> /// This value is only used if the progress bar is not a marquee progress bar (as defined /// by the <see cref="State"/> property). /// </para> /// <para> /// This property can be set while the dialog is shown. /// </para> /// </remarks> /// <exception cref="ArgumentOutOfRangeException"> /// The value is less than 0 or greater than <see cref="ushort.MaxValue" />. /// </exception> /// <exception cref="InvalidOperationException"> /// The property is set on a progress bar instance that is currently bound to a task dialog, but it's not visible as its initial /// <see cref="State"/> property value was <see cref="TaskDialogProgressBarState.None"/>. /// - or - /// The property is set on a progress bar instance that is currently bound to a task dialog, but the dialog /// has just started navigating to a different page. /// </exception> public int Value { get => _value; set { ArgumentOutOfRangeException.ThrowIfNegative(value); ArgumentOutOfRangeException.ThrowIfGreaterThan(value, ushort.MaxValue); DenyIfBoundAndNotCreated(); // We only update the task dialog if the current state is a // non-marquee progress bar. if (BoundPage?.WaitingForInitialization == false && !ProgressBarStateIsMarquee(_state)) { BoundPage.BoundDialog!.SetProgressBarPosition(value); // We need to set the position a second time to work // reliably if the state is not 'Normal'. // See this comment in the TaskDialog implementation // of the Windows API Code Pack 1.1: // "Due to a bug that wasn't fixed in time for RTM of // Vista, second SendMessage is required if the state // is non-Normal." // Apparently, this bug is still present in Win10 V1909. if (_state != TaskDialogProgressBarState.Normal) { BoundPage.BoundDialog!.SetProgressBarPosition(value); } } _value = value; } } /// <summary> /// Gets or sets the speed of the marquee display of a progress bar. /// </summary> /// <value> /// The speed of the marquee display which is the time, in milliseconds, between marquee /// animation updates. If this value is <c>0</c>, the marquee animation is updated every /// 30 milliseconds. The default value is <c>0</c>. /// </value> /// <remarks> /// <para> /// This value is only used if the progress bar is a marquee progress bar (as defined /// by the <see cref="State"/> property). /// </para> /// <para> /// This property can be set while the dialog is shown. /// </para> /// </remarks> /// <exception cref="InvalidOperationException"> /// The property is set on a progress bar instance that is currently bound to a task dialog, but it's not visible as its initial /// <see cref="State"/> property value was <see cref="TaskDialogProgressBarState.None"/>. /// - or - /// The property is set on a progress bar instance that is currently bound to a task dialog, but the dialog /// has just started navigating to a different page. /// </exception> public int MarqueeSpeed { get => _marqueeSpeed; set { DenyIfBoundAndNotCreated(); int previousMarqueeSpeed = _marqueeSpeed; _marqueeSpeed = value; try { // We only update the task dialog if the current state is a // marquee progress bar. if (BoundPage?.WaitingForInitialization == false && ProgressBarStateIsMarquee(_state)) { // Update the state which will also update the marquee speed. UpdateState(_state); } } catch { _marqueeSpeed = previousMarqueeSpeed; throw; } } } internal override bool IsCreatable => base.IsCreatable && _state != TaskDialogProgressBarState.None; private static bool ProgressBarStateIsMarquee(TaskDialogProgressBarState state) => state is TaskDialogProgressBarState.Marquee or TaskDialogProgressBarState.MarqueePaused; private static uint GetNativeProgressBarState(TaskDialogProgressBarState state) => state switch { TaskDialogProgressBarState.Normal => PInvoke.PBST_NORMAL, TaskDialogProgressBarState.Paused => PInvoke.PBST_PAUSED, TaskDialogProgressBarState.Error => PInvoke.PBST_ERROR, _ => throw new ArgumentException(null, nameof(state)) }; private protected override TASKDIALOG_FLAGS BindCore() { TASKDIALOG_FLAGS flags = base.BindCore(); // When specifying the flags for the page creation, the state of the initial progress bar // shown in the dialog will either be equivalent to the 'MarqueePaused' or 'Normal' state. // Other states will need to wait for the initialization. bool initialStateIsMarquee = ProgressBarStateIsMarquee(_state); _stateWhenBound = initialStateIsMarquee ? TaskDialogProgressBarState.MarqueePaused : TaskDialogProgressBarState.Normal; flags |= initialStateIsMarquee ? TASKDIALOG_FLAGS.TDF_SHOW_MARQUEE_PROGRESS_BAR : TASKDIALOG_FLAGS.TDF_SHOW_PROGRESS_BAR; return flags; } private protected override void ApplyInitializationCore() { UpdateState(_stateWhenBound, true); } private void UpdateState(TaskDialogProgressBarState previousState, bool isInitialization = false) { Debug.Assert(BoundPage is not null); TaskDialog taskDialog = BoundPage.BoundDialog!; // Check if we need to switch between a marquee and a // non-marquee bar. bool newStateIsMarquee = ProgressBarStateIsMarquee(_state); bool switchMode = ProgressBarStateIsMarquee(previousState) != newStateIsMarquee; if (switchMode) { // When switching from non-marquee to marquee mode, we // first need to set the state to "Normal"; otherwise // the marquee will not show. if (newStateIsMarquee && previousState != TaskDialogProgressBarState.Normal) { taskDialog.SetProgressBarState(PInvoke.PBST_NORMAL); } taskDialog.SwitchProgressBarMode(newStateIsMarquee); } // Update the properties. if (newStateIsMarquee) { taskDialog.SetProgressBarMarquee( _state == TaskDialogProgressBarState.Marquee, _marqueeSpeed); } else { taskDialog.SetProgressBarState(GetNativeProgressBarState(_state)); if (isInitialization || switchMode) { // Also need to set the other properties after switching // the mode or when applying the initialization. taskDialog.SetProgressBarRange(_minimum, _maximum); Value = _value; } } } }