WPF-进度条的应用例子
以独立窗体形式打开,并以单独的线程的简单封装
1、XML代码如下
<Window x:Class="PrograssBarDLL.View.PrograssBarPrimary" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:PrograssBarDLL.View" mc:Ignorable="d" Title="ProgressBarPrimary" Height="100" Width="900" WindowStartupLocation="CenterScreen" Background="Transparent" Foreground="Transparent" WindowStyle="None" AllowsTransparency="True" Topmost="True"> <Window.Resources> <ResourceDictionary> <!--进度条--> <SolidColorBrush x:Key="ProgressBar.Progress" Color="#FF2564F9"/> <SolidColorBrush x:Key="ProgressBar.Background" Color="#FFB9B9B9"/> <SolidColorBrush x:Key="ProgressBar.Border" Color="#FFDEDEDE"/> <Style x:Key="BaseProgressBar" TargetType="{x:Type ProgressBar}"> <Setter Property="Foreground" Value="{StaticResource ProgressBar.Progress}"/> <Setter Property="Background" Value="{StaticResource ProgressBar.Background}"/> <Setter Property="BorderBrush" Value="{StaticResource ProgressBar.Border}"/> <Setter Property="BorderThickness" Value="0"/> <Setter Property="Border.CornerRadius" Value="13"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ProgressBar}"> <Grid x:Name="TemplateRoot"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Determinate"/> <VisualState x:Name="Indeterminate"> <Storyboard RepeatBehavior="Forever"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="Animation"> <EasingDoubleKeyFrame KeyTime="0" Value="0.25"/> <EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.25"/> <EasingDoubleKeyFrame KeyTime="0:0:2" Value="0.25"/> </DoubleAnimationUsingKeyFrames> <PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="Animation"> <EasingPointKeyFrame KeyTime="0" Value="-0.5,0.5"/> <EasingPointKeyFrame KeyTime="0:0:1" Value="0.5,0.5"/> <EasingPointKeyFrame KeyTime="0:0:2" Value="1.5,0.5"/> </PointAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" CornerRadius="{TemplateBinding Border.CornerRadius}"/> <Border x:Name="PART_Track" MinWidth="20" CornerRadius="{TemplateBinding Border.CornerRadius}"/> <Grid x:Name="PART_Indicator" ClipToBounds="true" HorizontalAlignment="Left"> <Border x:Name="Indicator" MinWidth="20" Background="{TemplateBinding Foreground}" CornerRadius="{TemplateBinding Border.CornerRadius}"/> <Border x:Name="Animation" MinWidth="20" Background="{TemplateBinding Foreground}" CornerRadius="{TemplateBinding Border.CornerRadius}" RenderTransformOrigin="0.5,0.5"> <Border.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup> </Border.RenderTransform> <TextBlock HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="#FFFFFF" FontSize="13" Margin="0,0,10,0"> <Run Text="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Tag}"/> <Run Text="%"/> </TextBlock> </Border> <!--发光板--> <Border ClipToBounds="True" > <Border Width="{Binding PART_Track.Width}" Name="border1" Margin="-100,0"> <Border.Background> <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5" Opacity="0.4"> <GradientStop Color="{TemplateBinding Foreground}" Offset="0"/> <GradientStop Color="#FFD8E4FB" Offset="0"/> <GradientStop Color="{TemplateBinding Foreground}" Offset="1"/> </LinearGradientBrush> </Border.Background> </Border> </Border> </Grid> </Grid> <ControlTemplate.Triggers> <Trigger Property="Orientation" Value="Vertical"> <Setter Property="LayoutTransform" TargetName="TemplateRoot"> <Setter.Value> <RotateTransform Angle="-90"/> </Setter.Value> </Setter> </Trigger> <Trigger Property="IsIndeterminate" Value="true"> <Setter Property="Visibility" TargetName="Indicator" Value="Collapsed"/> </Trigger> <EventTrigger RoutedEvent="UserControl.Loaded"> <BeginStoryboard> <Storyboard> <DoubleAnimation Duration="0:0:2" To="1" Storyboard.TargetName="border1" Storyboard.TargetProperty="Background.GradientStops[1].Offset" RepeatBehavior="Forever" AutoReverse="True"/> </Storyboard> </BeginStoryboard> </EventTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary> </Window.Resources> <Border CornerRadius="20" Background="Transparent" > <ProgressBar Name="baseProgressBar" Height="40" Maximum="100" Value="0" Style="{DynamicResource BaseProgressBar}" Background="Transparent" Tag="0" /> </Border> </Window>
2、后台代码---最大进度值为传入值,如果最大进度值为10,则会出现进度条单步百分之比显示为10%
/// <summary> /// ProgressBarPrimary.xaml 的交互逻辑 /// </summary> public partial class PrograssBarPrimary : Window { public PrograssBarPrimary(double maxMinum) { InitializeComponent(); this.baseProgressBar.Maximum = maxMinum; } /// <summary> /// 设置进度值 /// </summary> /// <param name="i"></param> public void SetBarValue(double i) { Action<DependencyProperty, object> update = this.baseProgressBar.SetValue; Dispatcher.Invoke(update, DispatcherPriority.Normal, new object[] { System.Windows.Controls.Primitives.RangeBase.ValueProperty, i }); Dispatcher.Invoke(update, DispatcherPriority.Normal, new object[] { System.Windows.Controls.Primitives.RangeBase.TagProperty, GetTagValue(i) }); DoEvents(); } /// <summary> /// 转换为百分之显示 /// </summary> /// <param name="i"></param> /// <returns></returns> double GetTagValue(double i) { return i == this.baseProgressBar.Maximum ? 100 : Math.Round(100 / this.baseProgressBar.Maximum * i); } /// <summary> /// 刷新界面的方法---非常重要,否则界面可能刷新不及时 /// </summary> void DoEvents() { var frame = new DispatcherFrame(); Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrames), frame); try { Dispatcher.PushFrame(frame); } catch (InvalidOperationException) { } } private object ExitFrames(object frame) { ((DispatcherFrame)frame).Continue = false; return null; } }
3、针对以上情况进行优化,设定最大进度值恒为100,
/// <summary> /// ProgressBarPrimary.xaml 的交互逻辑 /// </summary> public partial class PrograssBarPrimary : Window { public PrograssBarPrimary(double maxMinum) { InitializeComponent(); this.baseProgressBar.Maximum = 100;// maxMinum; stepLong = 100 / maxMinum; stepValue = maxMinum <= 10 ? 50 / stepLong : 1 / stepLong; if (maxMinum < 100) this.millisecondsTimeout = Convert.ToInt32(Math.Ceiling(maxMinum / 20));//总数越大,休息时间越短 else this.millisecondsTimeout = 2; } private readonly double stepLong;//单步长度 private readonly int millisecondsTimeout;//休息时间 private readonly double stepValue;//分步走的单步值 /// <summary> /// 设置进度值 /// </summary> /// <param name="i"></param> public void SetBarValue() { Action<DependencyProperty, object> update = this.baseProgressBar.SetValue; if (stepLong >= 1 ) { var temp = 0.0; do { var setValue = this.baseProgressBar.Value + stepValue; var tagValue = Math.Ceiling(setValue); Dispatcher.Invoke(update, DispatcherPriority.Normal, new object[] { System.Windows.Controls.Primitives.RangeBase.TagProperty, tagValue>100?100:tagValue }); Dispatcher.Invoke(update, DispatcherPriority.Normal, new object[] { System.Windows.Controls.Primitives.RangeBase.ValueProperty, setValue}); temp += stepValue; DoEvents(); Thread.Sleep(millisecondsTimeout);//休息一下,防止刷新速度太快 } while (temp < stepLong); } else//总数大于100,不进行分步走 { var setValue = this.baseProgressBar.Value + stepLong; Dispatcher.Invoke(update, DispatcherPriority.Normal, new object[] { System.Windows.Controls.Primitives.RangeBase.TagProperty, Math.Round( setValue>100?100:setValue)}); Dispatcher.Invoke(update, DispatcherPriority.Normal, new object[] { System.Windows.Controls.Primitives.RangeBase.ValueProperty, setValue}); DoEvents(); Thread.Sleep(millisecondsTimeout); } } /// <summary> /// 刷新界面的方法--- /// </summary> internal void DoEvents() { var frame = new DispatcherFrame(); Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrames), frame); try { Dispatcher.PushFrame(frame); } catch (InvalidOperationException) { } } private object ExitFrames(object frame) { ((DispatcherFrame)frame).Continue = false; return null; } }
二、调用方式
1、----STA线程中调用
public static void TestMethod3() { PrograssBarPrimary prograssBarPrimary = new PrograssBarPrimary(100); prograssBarPrimary.Show(); for (int i = 0; i < 100; i++) { prograssBarPrimary.Dispatcher.Invoke(new Action(() => { prograssBarPrimary.SetBarValue(i + 1); })); Thread.Sleep(200); } prograssBarPrimary.Close(); }
2、跨线程调用---封装一个辅助类--支持不同的程序域传值
说明:利用了管道进行传值-------经测试发现管道的使用方式错
3、跨线程调用的测试方法
public static void TestMethod2(int length) { PrograssBarPrimaryHelper pbrInfo = new PrograssBarPrimaryHelper(); pbrInfo.ShowBeginInvok(length); for (int i = 0; i < length; i++) { pbrInfo.Run(i + 1); //Thread.Sleep(500); } pbrInfo.Dispose(); }
4、修复---上面跨线程的调用方法;故对程序进行一下修改
/// <summary> /// 进度条的帮助类 /// </summary> public class PrograssBarPrimaryHelper : IDisposable { #region public Thread? CurThread; readonly NamedPipeClientStream pipeClient; private readonly string PipeName = "PipesOfProgressBar"; StreamWriter writer; public PrograssBarPrimaryHelper() { pipeClient = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.None); writer = new StreamWriter(pipeClient); } /// <summary> /// 循环向管道内写入值,如果input大于进度条的最大值,抛出异常 /// </summary> /// <param name="input">进度条的Value</param> public void Run(double input) { try { if(pipeClient.IsConnected) { writer.WriteLine(input); writer.AutoFlush = true; pipeClient.WaitForPipeDrain();//这里卡线程,等待server读取后继续 } } catch (System.IO.IOException ex) { // writer = null; Dispose(); Console.WriteLine(ex.Message); } } /// <summary> /// 多线程的调用方法 /// </summary> /// <param name="maxminum"></param> /// <returns></returns> public void ShowBeginInvok(double maxminum) { CurThread = new Thread(() => { ShowBarAction(maxminum); }) { IsBackground = true }; CurThread.SetApartmentState(ApartmentState.STA); CurThread.Start(); pipeClient.Connect(5000); } /// <summary> /// 显示进度条 /// </summary> /// <param name="maxminum"></param> private void ShowBarAction(double maxminum) { var progressBar = new PrograssBarPrimary(maxminum); progressBar.Show(); using NamedPipeServerStream pipeServer = new NamedPipeServerStream(PipeName, PipeDirection.InOut,5); pipeServer.WaitForConnection();//等待客户端连接 using StreamReader sr = new(pipeServer); double result = 0; string temp = "1"; do { if (double.TryParse(temp, out result)) { if (result <= maxminum) progressBar.SetBarValue(); if (result >= maxminum) break; } else break; temp = sr.ReadLine(); } while (result < maxminum - 1); } #endregion #region 接口方法 private bool disposedValue; // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 ~PrograssBarPrimaryHelper() { // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 Dispose(disposing: false); } public void Dispose() { // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 Dispose(disposing: true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: 释放托管状态(托管对象) CurThread?.Join(3000); CurThread?.Abort(); } pipeClient?.Close(); pipeClient?.Dispose(); // TODO: 释放未托管的资源(未托管的对象)并重写终结器 // TODO: 将大型字段设置为 null disposedValue = true; } } #endregion }
使用WIN32API的消息机制
进度条为exe程序,利用win32的消息机制实现通信
进度条程序如下
新建WPF应用程序,新建一个窗体,窗体的XML代码如下
<Window x:Class="Lad.PrograssBarWindow.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:hc="https://handyorg.github.io/handycontrol" mc:Ignorable="d" Title="Lad.进度条Window" Width="800" Background="Transparent" SizeToContent="Height" ShowInTaskbar="False" WindowStyle="None" AllowsTransparency="True" WindowStartupLocation="CenterScreen" Name="window" > <Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml"/> <ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources> <Border Background="Transparent" Effect="{StaticResource EffectShadow3}" > <Grid> <Grid.RowDefinitions> <RowDefinition Height="38"/> <RowDefinition Height="auto"/> </Grid.RowDefinitions> <Border Style="{StaticResource BorderTipPrimary}" Background="#FF2779E6" CornerRadius="8"> <StackPanel Orientation="Horizontal" Margin="0,0,10,0" HorizontalAlignment="Right"> <Button Width="50" BorderBrush="Transparent" Background="Transparent" Foreground="White" hc:IconElement.Geometry="{StaticResource WindowMinGeometry}" hc:IconElement.Width="18" Click="BtnMinWindow_Cliked" /> <Button Width="50" BorderBrush="Transparent" Background="Transparent" Foreground="White" hc:IconElement.Geometry="{StaticResource DeleteGeometry}" Click="BtnCloseWindow_Cliked" /> </StackPanel> </Border> <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="40,0"> <TextBlock Text="{Binding Time}" FontSize="20" Foreground="#FFF1F1F1" Width="200"/> <TextBlock Text="{Binding ProgressStr}" FontSize="20" Foreground="#FFF1F1F1" Margin="60,0"/> </StackPanel> <StackPanel Grid.Row="1" Orientation="Vertical" Margin="0,30" > <hc:WaveProgressBar Name="pbar" Background="Transparent" Value="{Binding Progress}" Maximum="{Binding MaxProgress}" VerticalAlignment="Top" Foreground="White" ShowText="False" Width="250"/> <hc:LoadingLine Style="{StaticResource LoadingLineLarge}" VerticalAlignment="Bottom" Visibility="{Binding VisibiliLoadingLine}" Margin="0,30,0,20"/> <TextBlock Text="{Binding MesgText}" FontSize="20" Foreground="{StaticResource WarningBrush}" HorizontalAlignment="Center"/> </StackPanel> </Grid> </Border> </Window>
后台cs代码
public partial class MainWindow : Window { private readonly PrograsBarViewModel model; public MainWindow() { InitializeComponent(); this.DataContext = model= new PrograsBarViewModel(Visibility.Visible); } private void BtnCloseWindow_Cliked(object sender, RoutedEventArgs e) => this.Close(); private void BtnMinWindow_Cliked(object sender, RoutedEventArgs e) => this.WindowState = WindowState.Minimized; #region 接收消息 // 定义常量消息值 public const int WM_GETTEXT = 0x0D; public const int WM_SETTEXT = 0x0C; public const int WM_SIZEING = 0x0214; public const int WM_COPYDATA = 0x004A; public const int WM_LBUTTONDBLCLK = 0x0203; /// <summary> /// WM_COPYDATA消息,进程间传输信息专用结构 /// </summary> public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; } protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); //new WindowInteropHelper(this) //HwndSource? source = PresentationSource.FromVisual(this) as HwndSource; var source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); source?.AddHook(WndProc); } /// <summary> /// /// </summary> /// <param name="hwnd">窗口句柄</param> /// <param name="msg">消息 ID</param> /// <param name="wParam">消息的 wParam 值</param> /// <param name="lParam">消息的 lParam 值</param> /// <param name="handled">指示该消息是否已处理的值。 如果该消息已处理,请将值设置为 true;否则请将其设置为 false</param> /// <returns></returns> private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { // Handle messages... switch (msg) { case WM_COPYDATA: //将数据从非托管内存块封送到托管对象 COPYDATASTRUCT? MyKeyboardHookStruct = Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT)) as COPYDATASTRUCT?; if (MyKeyboardHookStruct.HasValue) { string temp = MyKeyboardHookStruct.Value.lpData; if (int.TryParse(temp, out var result)) { model.Progress = result; } else if (temp.Contains(';')) { var strs = temp.Split(';'); switch (strs[0]) { case nameof(PrograsBarViewModel.MaxProgress): if (int.TryParse(strs.Last(), out result)) model.MaxProgress = result; break; case nameof(PrograsBarViewModel.MesgText): model.MesgText = strs.Last(); break; default: break; } } else if ("Closed" == temp) Application.Current.Shutdown(); } handled = true; break; default: break; } return IntPtr.Zero; } #endregion }
ViewModel
internal class PrograsBarViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; /// <summary> /// 通知方法,[CallerMemberName]允许获取方法调用方的属性名称 /// </summary> /// <param name="propName"></param> public void DoNotify([CallerMemberName] string? propName = default) { //事件不为空执行,达到通知的效果 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); } private readonly System.Windows.Threading.DispatcherTimer _timer; private int _MaxProgress; private string _MesgText; private int _Progress; private string _ProgressStr; private string _Time; bool _VisibiliLoading; private Visibility _VisibiliLoadingLine; DateTime? PreUpdateTime; private Stopwatch stopwatch; public PrograsBarViewModel(Visibility visibility) { _VisibiliLoading = visibility == Visibility.Visible; _VisibiliLoadingLine = visibility; _MaxProgress = 100; _Time = _ProgressStr = string.Empty; _MesgText = string.Empty; stopwatch = new(); _timer = new System.Windows.Threading.DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) }; _timer.Tick += Timer_Tick; } /// <summary> /// 最大进度值 /// </summary> public int MaxProgress { get { return _MaxProgress; } set { _MaxProgress = value; this.DoNotify(nameof(MaxProgress)); } } /// <summary> /// 消息文本 /// </summary> public string MesgText { get { return _MesgText; } set { _MesgText = value; this.DoNotify(nameof(MesgText)); } } /// <summary> /// 进度值 /// </summary> public int Progress { get { return _Progress; } set { UpdateTime(); _Progress = value > _MaxProgress ? _MaxProgress : value; ProgressStr = $" 已完成:{_Progress}/{_MaxProgress}"; PreUpdateTime = DateTime.Now; this.DoNotify(nameof(Progress)); } } /// <summary> /// 进度值字符串形式 /// </summary> public string ProgressStr { get { return _ProgressStr; } set { _ProgressStr = value; this.DoNotify(nameof(ProgressStr)); } } /// <summary> /// 时间 /// </summary> public string Time { get { return _Time; } set { _Time = value; this.DoNotify(nameof(Time)); } } /// <summary> /// 是否显示直线进度 /// </summary> public Visibility VisibiliLoadingLine { get { return _VisibiliLoadingLine; } set { _VisibiliLoadingLine = value; this.DoNotify(nameof(VisibiliLoadingLine)); } } private void Timer_Tick(object? sender, EventArgs e) { if (stopwatch.IsRunning) Time = $"耗时:{stopwatch.Elapsed:hh\\:mm\\:ss}"; if (!_VisibiliLoading)//直线不显示 if (PreUpdateTime.HasValue && (DateTime.Now - PreUpdateTime.Value).Milliseconds >= 100)//未更新 VisibiliLoadingLine = Visibility.Visible; else VisibiliLoadingLine = Visibility.Hidden; } void UpdateTime() { if (!_timer.IsEnabled) { if (!stopwatch.IsRunning) stopwatch = Stopwatch.StartNew(); _timer.Start(); } } }
新建控制台应用程序进行调用
帮助类如下
internal class Win32APIExtension { #region 定义常量消息值 public const int WM_GETTEXT = 0x0D; public const int WM_SETTEXT = 0x0C; public const int WM_SIZEING = 0x0214; public const int WM_COPYDATA = 0x004A; public const int WM_LBUTTONDBLCLK = 0x0203; #endregion public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; } [DllImport("User32.dll")] public static extern int SendMessage(IntPtr hwnd, int msg, int wParam, ref COPYDATASTRUCT lParam); //接收消息窗体的函数 [DllImport("User32.dll", EntryPoint = "FindWindow")] public static extern IntPtr FindWindow(string? lpClassName, string lpWindowName); [DllImport("user32.dll", EntryPoint = "FindWindowEx")] private static extern IntPtr FindWindowEx(IntPtr hwndParent, uint hwndChildAfter, string lpszClass, string lpszWindow); public static void SendMessageFromName(object msg, string windowName) { IntPtr WINDOW_HANDLER = FindWindow(null, windowName); SendMessageTo(WINDOW_HANDLER, msg); } public static void SendMessageTo(IntPtr WINDOW_HANDLER, object msg) { if (WINDOW_HANDLER != IntPtr.Zero) { string text = msg?.ToString() ?? string.Empty; byte[] sarr = System.Text.Encoding.Default.GetBytes(text); int len = sarr.Length; COPYDATASTRUCT cds; cds.dwData = (IntPtr)100; cds.lpData = text; cds.cbData = len + 1; SendMessage(WINDOW_HANDLER, WM_COPYDATA, 0, ref cds); } } }
public class PrograsBarsOption:IDisposable { private static readonly object locker = new(); private static volatile PrograsBarsOption? uniqueInstance; private readonly string FileName; private int curValue; private Process? process; private bool disposedValue; protected PrograsBarsOption() { var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); FileName = Path.Combine(path, @"PrograsBars\net45-pbr\Lad.PrograssBarWindow.exe"); } public static void Close() { GetInstance().Dispose(); uniqueInstance = null; } public static void Run() { var instance = GetInstance(); Win32APIExtension.SendMessageTo(instance.InitIntPtr(), instance.curValue++); } public static void Run(int value) { Win32APIExtension.SendMessageTo(GetInstance().InitIntPtr(), value); } public static void SetMaxPrograss(int value) { Win32APIExtension.SendMessageTo(GetInstance().InitIntPtr(), $"MaxProgress;{value}"); } public static void SetMessage(string msg) { Win32APIExtension.SendMessageTo(GetInstance().InitIntPtr(), $"MesgText;{msg}"); } static PrograsBarsOption GetInstance() { lock (locker) { // 如果类的实例不存在则创建,否则直接返回 if (uniqueInstance == null) uniqueInstance = new(); } return uniqueInstance; } private IntPtr InitIntPtr() { var windowHandle = Win32APIExtension.FindWindow(null, "Lad.进度条Window"); if (windowHandle == IntPtr.Zero) { if(process==null) { if (!File.Exists(FileName)) throw new ArgumentException($"路径名:{FileName}不存在"); process = new(); process.StartInfo.FileName = FileName; process.Start(); } int count = 0; while (windowHandle == IntPtr.Zero && count<50) { windowHandle = Win32APIExtension.FindWindow(null, "Lad.进度条Window"); Thread.Sleep(100); } } return windowHandle; } protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: 释放托管状态(托管对象) } if(process==null) { Win32APIExtension.SendMessageFromName("Closed", "Lad.进度条Window"); } else { process.Kill(); process.Dispose(); process = null; } uniqueInstance = null; // TODO: 释放未托管的资源(未托管的对象)并重写终结器 // TODO: 将大型字段设置为 null disposedValue = true; } } // // TODO: 仅当“Dispose(bool disposing)”拥有用于释放未托管资源的代码时才替代终结器 //~PrograsBarsOption() //{ // // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 // Dispose(disposing: false); //} public void Dispose() { // 不要更改此代码。请将清理代码放入“Dispose(bool disposing)”方法中 Dispose(disposing: true); GC.SuppressFinalize(this); } }
调用方法
for (int i = 0; i < 100; i++) { PrograsBarsOption.Run(); Thread.Sleep(100); } PrograsBarsOption.SetMessage("我累坏了"); PrograsBarsOption.SetMaxPrograss(50); for (int i = 0; i < 51; i++) { PrograsBarsOption.Run(i); Thread.Sleep(200); } PrograsBarsOption.Close();
效果截图