欢迎来到陆季疵的博客

人生莫作远行客,远行莫戍黄沙碛。黄沙碛下八月时, 霜风裂肤百草衰。尘沙晴天迷道路,河水悠悠向东去。 胡笳听彻双泪流,羁魂惨惨生边愁。原头猎火夜相向, 马蹄蹴蹋层冰上。不似京华侠少年,清歌妙舞落花前。

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();

 

效果截图

 

posted @ 2021-10-29 09:26  陆季疵  阅读(547)  评论(0编辑  收藏  举报
//《!--看板娘--> //https://www.cnblogs.com/ZTianming/p/14618913.html