0

我正在制作棋盘,我想在完成后看到进展。棋盘不是经典的,它包含数百万个领域,创造的过程本身需要时间。有时,创建过程最多需要 2 分钟。我想直观地看到该过程本身何时结束。它不一定是进度条,它可以是任何不会减慢进程本身的控件。

当我使用Progress.Dispatcher.Invoke (()...时,我实际上减慢了创建过程,它需要的时间是平时的 5 倍。当我使用BackgroundWorkerand ReportProgress... 时,我也会减慢创建过程,它需要比平时多 5 到 8 倍。

我只想通过使用不会减慢进程的任何控件或类来向用户显示进度。有什么想法?

Rectangle[,] square = new Rectangle[x, x];
    for (int row = 0; row < x; row++)
        for (int col = 0; col < x; col++)
        {
            square[row, col] = new Rectangle()
            {
                Height = squareSize,
                Width = squareSize
            };
            Grid.SetColumn(square[row, col], col);
            Grid.SetRow(square[row, col], row);
            if ((row + col) % 2 == 0)
            {
                square[row, col].Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(233, 223, 191));
            }
            else
            {
                square[row, col].Fill = new SolidColorBrush(System.Windows.Media.Color.FromRgb(112, 42, 44));
            }
            LayoutRoot.Children.Add(square[row, col]);

            // Watch process of creation in real time
            if (cbCreationProcess.IsChecked == true)
                Progress.Dispatcher.Invoke(() => Progress.Value = x, DispatcherPriority.Background);
        }
4

2 回答 2

1

这个解决方案过去对我有用。

ProgressWindowControl.xaml

<Window
    x:Class="YourNamespace.ProgressWindowControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    WindowStartupLocation="CenterScreen" ShowInTaskbar="False" ResizeMode="NoResize"
    SizeToContent="WidthAndHeight" WindowStyle="None"
    mc:Ignorable="d">

    <Window.Style>
        <Style TargetType="Window">
            <Setter Property="AllowsTransparency" Value="True"/>
            <Setter Property="Background" Value="#00FFFFFF"/>
        </Style>
    </Window.Style>

    <Grid>
        <Grid Width="450" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0">
            <Grid x:Name="Back">
                <Border Background="Black" CornerRadius="3" Opacity="0.15"/>
                <Border CornerRadius="2" Margin="1" Background="White"/>
            </Grid>
            <Grid x:Name="Content_Area" Margin="12">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <TextBlock x:Name="Info" TextWrapping="Wrap" 
                           Text="{Binding Path=State,RelativeSource={RelativeSource AncestorType={x:Type Window}}}" 
                           Grid.Row="0" Margin="12,12,12,0" Foreground="#FF2D2D2D"/>
                <ProgressBar Height="12"
                             Grid.Row="1"
                             Margin="12"
                             IsIndeterminate="{Binding Path=IsIndeterminate,RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                             Value="{Binding Path=Progress,RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                             Maximum="{Binding Path=MaxProgress,RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

                <Button x:Name="uxCancelBtn" Grid.Row="2" Height="22" Width="85" HorizontalAlignment="Right" Margin="0 0 12 0"
                        Click="CancelButton_Click" IsEnabled="False" Content="{x:Static resx:Strings.Cancel}">               
                </Button>
            </Grid>
        </Grid>
    </Grid>
</Window>

ProgressWindowControl.cs

public sealed partial class ProgressWindowControl : Window
{
    public static readonly DependencyProperty ProgressProperty =
     DependencyProperty.Register("Progress", typeof(double), typeof(ProgressWindowControl), new PropertyMetadata(0d));

    public static readonly DependencyProperty MaxProgressProperty =
     DependencyProperty.Register("MaxProgress", typeof(double), typeof(ProgressWindowControl), new PropertyMetadata(100d));

    public static readonly DependencyProperty IsIndeterminateProperty =
        DependencyProperty.Register("IsIndeterminate", typeof(bool), typeof(ProgressWindowControl), new PropertyMetadata(true));

    public static readonly DependencyProperty StateProperty =
    DependencyProperty.Register("State", typeof(string), typeof(ProgressWindowControl), new PropertyMetadata(string.Empty));

    public static readonly DependencyProperty IsCancelAllowedProperty =
     DependencyProperty.Register("IsCancelAllowed", typeof(bool), typeof(ProgressWindowControl), new PropertyMetadata(false));

    private ProgressWindowControl()
    {
        InitializeComponent();
    }

    public double Progress
    {
        get
        {
            return (double)GetValue(ProgressProperty);
        }
        set
        {
            SetValue(ProgressProperty, value);
        }
    }

    public double MaxProgress
    {
        get
        {
            return (double)GetValue(MaxProgressProperty);
        }
        set
        {
            SetValue(MaxProgressProperty, value);
        }
    }

    public bool IsIndeterminate
    {
        get
        {
            return (bool)GetValue(IsIndeterminateProperty);
        }
        set
        {
            SetValue(IsIndeterminateProperty, value);
        }
    }

    public string State
    {
        get
        {
            return (string)GetValue(StateProperty);
        }
        set
        {
            SetValue(StateProperty, value);
        }
    }

    public Action OnProgressWindowCancel { get; set; }

    private void CancelButton_Click(object sender, RoutedEventArgs e)
    {
        if (OnProgressWindowCancel != null)
        {
            uxCancelBtn.IsEnabled = false;
            uxCancelBtn.Content = Strings.Cancelling;
            OnProgressWindowCancel();
        }
    }

    [DllImport("user32.dll")]
    private static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

    private const int GWL_HWNDPARENT = -8;

    private static ProgressWindowControl _progressWindowControl;
    private static bool _isVisible;
    private static Window _owner;
    private static ResizeMode? _ownerResizeMode;
    private static bool _ownerIsHitTestVisible;
    private static bool _ownerFocusable;

    public static void ShowProgressWindow(Window owner = null)
    {
        if (!_isVisible)
        {
            IntPtr ownerHandle = IntPtr.Zero;
            if (owner != null)
            {
                _owner = owner;
                ownerHandle = GetHandler(_owner);
                //Block owner window input while the progress bar is opened
                _ownerResizeMode = _owner.ResizeMode;
                _ownerIsHitTestVisible = _owner.IsHitTestVisible;
                _ownerFocusable = _owner.Focusable;
                _owner.ResizeMode = ResizeMode.NoResize;
                _owner.IsHitTestVisible = false;
                _owner.Focusable = false;
                _owner.PreviewKeyDown += Owner_PreviewKeyDown;
                _owner.PreviewMouseDown += Owner_PreviewMouseDown;
                _owner.Closing += Owner_Closing;
            }
            //Run window in its own thread
            Thread thread = new Thread(new ThreadStart(() =>
            {
                SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher));
                _progressWindowControl = new ProgressWindowControl();
                // Shutdown the dispatcher when the window closes
                _progressWindowControl.Closed += (s, e) =>
                    Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);

                // When the progress window has loaded, if an owner has been specified, attach it to the window, otherwise set Topmost = true
                ProgressWindowControl._progressWindowControl.Loaded += (s, e) =>
                {
                    if (owner != null)
                    {
                        IntPtr ownedWindowHandle = GetHandler(_progressWindowControl);
                        SetOwnerWindowMultithread(ownedWindowHandle, ownerHandle);
                    }
                    else
                    {
                        _progressWindowControl.Topmost = true;
                    }
                };
                _progressWindowControl.Show();
                _isVisible = true;
                System.Windows.Threading.Dispatcher.Run();
            }));
            thread.SetApartmentState(ApartmentState.STA);
            thread.IsBackground = true;
            thread.Start();
        }
    }

    private static void Owner_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        e.Cancel = true;
    }

    private static void Owner_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        e.Handled = true;
    }

    private static void Owner_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    {
        e.Handled = true;
    }

    private static void SetOwnerWindowMultithread(IntPtr windowHandleOwned, IntPtr intPtrOwner)
    {
        if (windowHandleOwned != IntPtr.Zero && intPtrOwner != IntPtr.Zero)
        {
            SetWindowLong(windowHandleOwned, GWL_HWNDPARENT, intPtrOwner.ToInt32());
        }
    }

    private static IntPtr GetHandler(Window window)
    {
        var interop = new WindowInteropHelper(window);
        return interop.Handle;
    }

    public static void CloseProgressWindow()
    {
        if (_progressWindowControl != null && _isVisible)
        {
            if (_progressWindowControl.Dispatcher.CheckAccess())
            {
                _progressWindowControl.Close();
            }
            else
            {
                _progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
                    new ThreadStart(_progressWindowControl.Close));
            }
            if (_owner != null)
            {
                //Unblock owner input
                _owner.ResizeMode = _ownerResizeMode ?? ResizeMode.CanResize;
                _owner.IsHitTestVisible = _ownerIsHitTestVisible;
                _owner.Focusable = _ownerFocusable;
                _owner.PreviewKeyDown -= Owner_PreviewKeyDown;
                _owner.PreviewMouseDown -= Owner_PreviewMouseDown;
                _owner.Closing -= Owner_Closing;
            }
            //Reset fields
            _ownerResizeMode = null;
            _ownerIsHitTestVisible = false;
            _ownerFocusable = false;
            _progressWindowControl = null;
            _owner = null;
            _isVisible = false;
        }
    }

    public static void SetProgress(double progress, double maxProgress)
    {
        if (_progressWindowControl != null)
        {
            if (_progressWindowControl.Dispatcher.CheckAccess())
            {
                _progressWindowControl.IsIndeterminate = false;
                _progressWindowControl.Progress = progress;
                _progressWindowControl.MaxProgress = maxProgress;
            }
            else
            {
                _progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
                    new ThreadStart(() =>
                    {
                        _progressWindowControl.IsIndeterminate = false;
                        _progressWindowControl.Progress = progress;
                        _progressWindowControl.MaxProgress = maxProgress;
                    }));
            }
        }
    }

    public static void SetIsIndeterminate(bool isIndeterminate)
    {
        if (_progressWindowControl != null)
        {
            if (_progressWindowControl.Dispatcher.CheckAccess())
            {
                _progressWindowControl.IsIndeterminate = isIndeterminate;
            }
            else
            {
                _progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
                    new ThreadStart(() =>
                    {
                        _progressWindowControl.IsIndeterminate = isIndeterminate;
                    }));
            }
        }
    }

    public static void SetState(string state)
    {
        if (_progressWindowControl != null)
        {
            if (_progressWindowControl.Dispatcher.CheckAccess())
            {
                _progressWindowControl.State = state;
            }
            else
            {
                _progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
                    new ThreadStart(() =>
                    {
                        _progressWindowControl.State = state;
                    }));
            }
        }
    }

    public static void SetIsCancelAllowed(bool isCancelAllowed, Action progressWindowCancel)
    {
        if (_progressWindowControl != null)
        {
            if (_progressWindowControl.Dispatcher.CheckAccess())
            {
                _progressWindowControl.OnProgressWindowCancel = progressWindowCancel;
                _progressWindowControl.uxCancelBtn.IsEnabled = isCancelAllowed;
                _progressWindowControl.uxCancelBtn.Content = Strings.Cancel;
            }
            else
            {
                _progressWindowControl.Dispatcher.Invoke(DispatcherPriority.Normal,
                    new ThreadStart(() =>
                    {
                        _progressWindowControl.OnProgressWindowCancel = progressWindowCancel;
                        _progressWindowControl.uxCancelBtn.IsEnabled = isCancelAllowed;
                        _progressWindowControl.uxCancelBtn.Content = Strings.Cancel;
                    }));
            }
        }
    }
}

打开窗口的辅助类:

public static class ProgressWindowHelper
{
    public static void Show(Window owner = null)
    {
        ProgressWindowControl.ShowProgressWindow(owner);
    }

    public static void Close()
    {
        ProgressWindowControl.CloseProgressWindow();
    }

    public static void SetProgress(double progress, double maxProgress)
    {
        ProgressWindowControl.SetProgress(progress, maxProgress);
    }

    public static void SetIsIndeterminate(bool isIndeterminate)
    {
        ProgressWindowControl.SetIsIndeterminate(isIndeterminate);
    }

    public static void SetState(string state)
    {
        ProgressWindowControl.SetState(state);
    }

    public static void SetIsCancelAllowed(bool isCancelAllowed, Action progressWindowCancel)
    {
        ProgressWindowControl.SetIsCancelAllowed(isCancelAllowed, progressWindowCancel);
    }
}

一项服务,以便您可以使用依赖注入(我没有包含接口,只需根据需要创建一个):

 public class ProgressWindowService : IProgressWindowService
{
    public void Show(Window owner = null)
    {
        ProgressWindowHelper.Show(owner);
    }

    public void Close()
    {
        ProgressWindowHelper.Close();
    }

    public void SetProgress(double progress, double maxProgress)
    {
        ProgressWindowHelper.SetProgress(progress, maxProgress);
    }

    public void SetIsIndeterminate(bool isIndeterminate)
    {
        ProgressWindowHelper.SetIsIndeterminate(isIndeterminate);
    }

    public void SetState(string state)
    {
        ProgressWindowHelper.SetState(state);
    }

    public void SetIsCancelAllowed(bool isCancelAllowed, Action progressWindowCancel)
    {
        ProgressWindowHelper.SetIsCancelAllowed(isCancelAllowed, progressWindowCancel);
    }
}

最后在您的 ViewModel 中(假设您已经注入了服务):

ProgressBarService.SetProgress(current, total);

ProgressBarService.SetState(state);

ProgressBarService.Show();

您也可以将一个窗口传递给 Show 方法,然后该窗口将附加到该窗口上,并且在显示进度窗口时输入将被阻止,如果没有提供任何窗口,则进度条显示在任何其他窗口的顶部(TopMost =真):

ProgressBarService.Show(YourWindow);

您还可以使用信使来触发进度窗口。

编辑

删除了 DevExpress 依赖项。

于 2017-09-11T14:30:12.263 回答
0

显然,您很清楚,任何添加的功能都会减慢它的速度。

如果有人告诉我使用上面的代码,我会做的可能是创建一个计时器并查看您拥有当前rowcol增量的任何位置,这取决于您对计算所做的事情。
您所要做的就是处理 Timer.Elapsed 事件(无论哪种计时器)并以百分比或其他形式计算/报告当前进度。
否则,您可以编写一个单独的线程并在一个循环中运行它,该循环会休眠N毫秒,直到绘制完成 - 基本上与使用计时器的想法相结合。通常,这是我们在播放 mp3 或在单独线程上操作的某些此类对象上报告进度的方法。

您在这里有很大的创意空间,并从这种场景中汲取一些智慧来优化结果,但在考虑这些之前......请
记住,WPF 在如何处理绘图方面是一头野兽,因为它严重依赖视频硬件、内存等...我倾向于将 WPF 视为 OpenGL 或 DirectX 的 html。我希望您的 GPU 与 heat-sync 有良好的连接并且不会变得太热和/或您没有在带有嵌入式 GPU 的笔记本电脑上工作。我在这里很激进,但这只是因为多年来我在写/编译/运行/重复周期中编写软件时已经烧毁了大量硬件。如果我玩得更安全一点,我会从大量硬件中获得更多生命。更不用说近年来我们的硬件越来越难了。

为了在这里尽可能有创意

  • 既然我们知道有多少个正方形并且可以确定生成的网格的尺寸,您是否想过将自定义图案渲染为背景并明确设置网格的大小?完成后,您可以在进一步填充所需或旋转一些有趣的过渡等方面发挥创造力
  • learning/usingSystem.Threading.Thread(ThreadStart)将使您能够将 process-priority 设置为比标准默认值更高的值——而不是 usingBackgroundWorker或其他类似的值......但我还没有完全围绕这样的实现。
  • 在 UI 之外构建一些对象,例如 a并在行、列或每N个增量List<>的末尾呈现
  • 我们知道我们x*x最终有盒子,所以如果我们明确说明尺寸,这里就有很大的创造力空间。我有一段时间没有修改 WPF,但这将是一个很好的案例,可以学习和寻找不同的方法来布置这样的东西(例如:CustomLayoutPanel tut)。如果你坚持网格的概念,你能做的只有这么多——我会本能地使用绘图方法来做到这一点,并且可能会想出一种方法来只绘制可见的东西根据需要屏幕...或
  • 通常@dymanoid 的回答是把你引向正确的方向,但它打破了所提出问题的逻辑,因此超出了问题的范围......但我们想在这里启发一下,通常是的,应该考虑 WPF 中的任何渲染过程内容使用现有的优化策略,例如 MVVM 和样式/模板/过渡。
于 2017-09-11T14:38:05.127 回答