2010-12-31 18 views
27

Tôi có một ứng dụng WPF 4 dựa trên MVVM sử dụng ProgressBar để hiển thị phần trăm hoàn thành một hoạt động dài hạn.Làm thế nào tôi có thể dừng quá trình tạo xung/hoạt hình của WPF khi đạt đến 100%?

<ProgressBar Name="ProgressBar" 
    IsIndeterminate="False" 
    Minimum="0" 
    Maximum="100" 
    Value="{Binding Path=ProgressPercentageComplete, Mode=OneWay}" 
    Visibility="Visible"/> 

Tôi rất vui vì hoạt ảnh "rung" xảy ra trong khi thanh tiến trình đang di chuyển, nhưng khi đạt đến 100%, tôi muốn ngừng hoạt ảnh và chỉ giữ nguyên ở mức 100%.

Tôi đã thử đặt IsIndeterminate="False" nhưng điều này không giúp đỡ và tôi có thể thấy lý do tại sao sau khi đọc các tài liệu MSDN:

Khi thuộc tính này là true, ProgressBar sinh động một vài quán bar di chuyển trên ProgressBar theo cách liên tục và bỏ qua thuộc tính Giá trị.

Có thể dừng hoạt ảnh này không? Hoặc là hoàn toàn, hoặc chỉ 100%.

+1

Gây ra hình ảnh rung động là một ý tưởng tồi vì lý do UI là nó cung cấp phản hồi của người dùng liên tục; để tránh người dùng tin rằng điều gì đó đã bị khóa và không còn phản hồi nữa. –

+4

Các ứng dụng bị khóa với hoạt ảnh UI vẫn hoạt động liên tục. Phải mất nhiều hơn thế để thuyết phục người dùng rằng ứng dụng vẫn còn sống. –

+0

@RobertRossney, tôi đã cố ý khóa luồng giao diện người dùng và * poof *, hoạt ảnh dừng lại, đưa ra phản hồi ngay lập tức. aaron-mciver đúng là xung có mục đích UX. –

Trả lời

7

Bạn có thể thực hiện việc này bằng cách sao chép toàn bộ ControlTemplate cho số ProgressBar, sau đó thêm Trigger cho điều kiện trong đó ProgressBar.Value=100. XAML như sẽ làm cho ProgressBar hoạt động như hiện tại. Hủy bỏ các bình luận Các thẻ ở phía dưới và hoạt ảnh sẽ dừng lại khi thuộc tính Giá trị của ProgressBar đạt đến 100. Điểm yếu duy nhất là khi bạn thay đổi thuộc tính tối đa của ProgressBar, bạn cũng cần thay đổi Trình kích hoạt. Bất cứ ai biết làm thế nào để ràng buộc Trigger với giá trị thực tế của tài sản tối đa?

<Window x:Class="ProgressBarSpike.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525" 
     Loaded="Window_Loaded"> 
    <Window.Resources> 
     <LinearGradientBrush x:Key="ProgressBarBackground" EndPoint="1,0" StartPoint="0,0"> 
      <GradientStop Color="#BABABA" Offset="0"/> 
      <GradientStop Color="#C7C7C7" Offset="0.5"/> 
      <GradientStop Color="#BABABA" Offset="1"/> 
     </LinearGradientBrush> 
     <LinearGradientBrush x:Key="ProgressBarBorderBrush" EndPoint="0,1" StartPoint="0,0"> 
      <GradientStop Color="#B2B2B2" Offset="0"/> 
      <GradientStop Color="#8C8C8C" Offset="1"/> 
     </LinearGradientBrush> 
     <LinearGradientBrush x:Key="ProgressBarGlassyHighlight" EndPoint="0,1" StartPoint="0,0"> 
      <GradientStop Color="#50FFFFFF" Offset="0.5385"/> 
      <GradientStop Color="#00FFFFFF" Offset="0.5385"/> 
     </LinearGradientBrush> 
     <LinearGradientBrush x:Key="ProgressBarTopHighlight" EndPoint="0,1" StartPoint="0,0"> 
      <GradientStop Color="#80FFFFFF" Offset="0.05"/> 
      <GradientStop Color="#00FFFFFF" Offset="0.25"/> 
     </LinearGradientBrush> 
     <LinearGradientBrush x:Key="ProgressBarIndicatorAnimatedFill" EndPoint="1,0" StartPoint="0,0"> 
      <GradientStop Color="#00FFFFFF" Offset="0"/> 
      <GradientStop Color="#60FFFFFF" Offset="0.4"/> 
      <GradientStop Color="#60FFFFFF" Offset="0.6"/> 
      <GradientStop Color="#00FFFFFF" Offset="1"/> 
     </LinearGradientBrush> 
     <LinearGradientBrush x:Key="ProgressBarIndicatorDarkEdgeLeft" EndPoint="1,0" StartPoint="0,0"> 
      <GradientStop Color="#0C000000" Offset="0"/> 
      <GradientStop Color="#20000000" Offset="0.3"/> 
      <GradientStop Color="#00000000" Offset="1"/> 
     </LinearGradientBrush> 
     <LinearGradientBrush x:Key="ProgressBarIndicatorDarkEdgeRight" EndPoint="1,0" StartPoint="0,0"> 
      <GradientStop Color="#00000000" Offset="0"/> 
      <GradientStop Color="#20000000" Offset="0.7"/> 
      <GradientStop Color="#0C000000" Offset="1"/> 
     </LinearGradientBrush> 
     <RadialGradientBrush x:Key="ProgressBarIndicatorLightingEffectLeft" RadiusY="1" RadiusX="1" RelativeTransform="1,0,0,1,0.5,0.5"> 
      <GradientStop Color="#60FFFFC4" Offset="0"/> 
      <GradientStop Color="#00FFFFC4" Offset="1"/> 
     </RadialGradientBrush> 
     <LinearGradientBrush x:Key="ProgressBarIndicatorLightingEffect" EndPoint="0,0" StartPoint="0,1"> 
      <GradientStop Color="#60FFFFC4" Offset="0"/> 
      <GradientStop Color="#00FFFFC4" Offset="1"/> 
     </LinearGradientBrush> 
     <RadialGradientBrush x:Key="ProgressBarIndicatorLightingEffectRight" RadiusY="1" RadiusX="1" RelativeTransform="1,0,0,1,-0.5,0.5"> 
      <GradientStop Color="#60FFFFC4" Offset="0"/> 
      <GradientStop Color="#00FFFFC4" Offset="1"/> 
     </RadialGradientBrush> 
     <LinearGradientBrush x:Key="ProgressBarIndicatorGlassyHighlight" EndPoint="0,1" StartPoint="0,0"> 
      <GradientStop Color="#90FFFFFF" Offset="0.5385"/> 
      <GradientStop Color="#00FFFFFF" Offset="0.5385"/> 
     </LinearGradientBrush> 
     <Style x:Key="ProgressBarStyleStopAnimation" TargetType="{x:Type ProgressBar}"> 
      <Setter Property="Foreground" Value="#01D328"/> 
      <Setter Property="Background" Value="{StaticResource ProgressBarBackground}"/> 
      <Setter Property="BorderBrush" Value="{StaticResource ProgressBarBorderBrush}"/> 
      <Setter Property="BorderThickness" Value="1"/> 
      <Setter Property="Template"> 
       <Setter.Value> 
        <ControlTemplate TargetType="{x:Type ProgressBar}"> 
         <Grid x:Name="TemplateRoot" SnapsToDevicePixels="true"> 
          <Rectangle Fill="{TemplateBinding Background}" RadiusY="2" RadiusX="2"/> 
          <Border Background="{StaticResource ProgressBarGlassyHighlight}" CornerRadius="2" Margin="1"/> 
          <Border BorderBrush="#80FFFFFF" BorderThickness="1,0,1,1" Background="{StaticResource ProgressBarTopHighlight}" Margin="1"/> 
          <Rectangle x:Name="PART_Track" Margin="1"/> 
          <Decorator x:Name="PART_Indicator" HorizontalAlignment="Left" Margin="1"> 
           <Grid x:Name="Foreground"> 
            <Rectangle x:Name="Indicator" Fill="{TemplateBinding Foreground}"/> 
            <Grid x:Name="Animation" ClipToBounds="true"> 
             <Rectangle x:Name="PART_GlowRect" Fill="{StaticResource ProgressBarIndicatorAnimatedFill}" HorizontalAlignment="Left" Margin="-100,0,0,0" Width="100"/> 
            </Grid> 
            <Grid x:Name="Overlay"> 
             <Grid.ColumnDefinitions> 
              <ColumnDefinition MaxWidth="15"/> 
              <ColumnDefinition Width="0.1*"/> 
              <ColumnDefinition MaxWidth="15"/> 
             </Grid.ColumnDefinitions> 
             <Grid.RowDefinitions> 
              <RowDefinition/> 
              <RowDefinition/> 
             </Grid.RowDefinitions> 
             <Rectangle x:Name="LeftDark" Fill="{StaticResource ProgressBarIndicatorDarkEdgeLeft}" Margin="1,1,0,1" RadiusY="1" RadiusX="1" Grid.RowSpan="2"/> 
             <Rectangle x:Name="RightDark" Grid.Column="2" Fill="{StaticResource ProgressBarIndicatorDarkEdgeRight}" Margin="0,1,1,1" RadiusY="1" RadiusX="1" Grid.RowSpan="2"/> 
             <Rectangle x:Name="LeftLight" Grid.Column="0" Fill="{StaticResource ProgressBarIndicatorLightingEffectLeft}" Grid.Row="2"/> 
             <Rectangle x:Name="CenterLight" Grid.Column="1" Fill="{StaticResource ProgressBarIndicatorLightingEffect}" Grid.Row="2"/> 
             <Rectangle x:Name="RightLight" Grid.Column="2" Fill="{StaticResource ProgressBarIndicatorLightingEffectRight}" Grid.Row="2"/> 
             <Border x:Name="Highlight1" Background="{StaticResource ProgressBarIndicatorGlassyHighlight}" Grid.ColumnSpan="3" Grid.RowSpan="2"/> 
             <Border x:Name="Highlight2" Background="{StaticResource ProgressBarTopHighlight}" Grid.ColumnSpan="3" Grid.RowSpan="2"/> 
            </Grid> 
           </Grid> 
          </Decorator> 
          <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"/> 
         </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="LeftDark" Value="Collapsed"/> 
           <Setter Property="Visibility" TargetName="RightDark" Value="Collapsed"/> 
           <Setter Property="Visibility" TargetName="LeftLight" Value="Collapsed"/> 
           <Setter Property="Visibility" TargetName="CenterLight" Value="Collapsed"/> 
           <Setter Property="Visibility" TargetName="RightLight" Value="Collapsed"/> 
           <Setter Property="Visibility" TargetName="Indicator" Value="Collapsed"/> 
          </Trigger> 
          <Trigger Property="IsIndeterminate" Value="false"> 
           <Setter Property="Background" TargetName="Animation" Value="#80B5FFA9"/> 
          </Trigger> 
          <!-- 
          <Trigger Property="Value" Value="100"> 
           <Setter Property="Visibility" TargetName="Animation" Value="Collapsed"/> 
          </Trigger> 
          --> 

         </ControlTemplate.Triggers> 
        </ControlTemplate> 
       </Setter.Value> 
      </Setter> 
     </Style> 
    </Window.Resources> 
    <StackPanel> 
     <ProgressBar Name="Progress" Height="50" Style="{DynamicResource ProgressBarStyleStopAnimation}"/> 
    </StackPanel> 
</Window> 
+0

Có cách nào để không chỉ định 'Giá trị =" 100 "' một cách rõ ràng không? Bởi vì phương pháp này không phổ biến. Nó làm cho tôi đặt 'Maximum' của bất kỳ' ProgressBar' là "100". Tôi đã cố gắng sử dụng 'Binding' nhưng nó không hoạt động: ( – monstr

+0

Người ta nên nghiêm túc xem xét câu trả lời của Dave Clemmer (xem bên dưới) như là thay thế vì thay thế mẫu điều khiển cũng có một số nhược điểm như Dave lưu ý. – user2261015

0

Tôi nghĩ cách duy nhất là tạo kiểu tùy chỉnh cho ProgressBar.
Mô tả của MSDN cho IsIndeterminate đề cập đến một hành vi khác và, theo mặc định, nó là sai để cài đặt nó không thay đổi bất cứ điều gì.

+0

Vui lòng cải thiện tính chính xác của câu trả lời này cho biết lý do tại sao -1. –

1

Bạn có thể trao đổi các chuyển đổi được sử dụng bởi PART_Indicator mà theo mặc định là ProgressBarBrushConverter đó là nơi mà các hoạt hình đến từ ...

... 

TranslateTransform transform = new TranslateTransform(); 
double num11 = num8 * 100; 
DoubleAnimationUsingKeyFrames animation = new DoubleAnimationUsingKeyFrames(); 
animation.Duration = new Duration(TimeSpan.FromMilliseconds(num11)); 
animation.RepeatBehavior = RepeatBehavior.Forever; 
for (int i = 1; i <= num8; i++) 
{ 
    double num13 = i * num7; 
    animation.KeyFrames.Add(new DiscreteDoubleKeyFrame(num13, KeyTime.Uniform)); 
} 
transform.BeginAnimation(TranslateTransform.XProperty, animation); 

... 

Các default logic cho ProgressBarBrushConverter sau đó có thể được sửa đổi để phù hợp với nhu cầu của bạn .

Bạn có thể phải kết thúc các tham số truyền cho trình chuyển đổi để nó có thể kiểm tra giá trị và cung cấp hình động thích hợp hoặc thiếu sự phụ thuộc vào trạng thái của ProgressBar.

8

Câu trả lời của Dabblernl là mạnh mẽ. Dưới đây là một hack (vì nó dựa vào tên nội bộ của phần tử mà không ánh sáng, mà là một chi tiết thực hiện và có thể thay đổi trong một phiên bản tiếp theo):

void SetGlowVisibility(ProgressBar progressBar, Visibility visibility) { 
    var anim = progressBar.Template.FindName("Animation", progressBar) as FrameworkElement; 
    if (anim != null) 
     anim.Visibility = visibility; 
} 

Nếu làm thế nào một ProgressBar được thực hiện thay đổi, hack này có thể ngừng hoạt động. Mặt khác, giải pháp thay thế hoàn toàn XAML và các kiểu có thể khóa và sửa màu, đường viền vv và tắt hành vi có thể được thêm vào phiên bản ProgressBar mới hơn trong tương lai ...

Chỉnh sửa: Chi tiết triển khai đã thay đổi. Đã thay đổi "PART_GlowRect" thành "Animation" - tên cũ chỉ được sử dụng trong aero.normalcolor.xaml, trong khi cái sau được sử dụng trong aero2.normalcolor.xamlaerolite.normalcolor.xaml gần đây nữa.

9

Tôi đã viết một giải pháp tổng quát cho việc này sử dụng một tài sản gắn liền, cho phép tôi để chuyển đổi hành vi về bất kỳ ProgressBar chỉ đơn giản là thông qua một tài sản trực tiếp hoặc phong cách setter, như vậy:

<ProgressBar helpers:ProgressBarHelper.StopAnimationOnCompletion="True" /> 

Mã:

public static class ProgressBarHelper { 
    public static readonly DependencyProperty StopAnimationOnCompletionProperty = 
     DependencyProperty.RegisterAttached("StopAnimationOnCompletion", typeof(bool), typeof(ProgressBarHelper), 
              new PropertyMetadata(OnStopAnimationOnCompletionChanged)); 

    public static bool GetStopAnimationOnCompletion(ProgressBar progressBar) { 
     return (bool)progressBar.GetValue(StopAnimationOnCompletionProperty); 
    } 

    public static void SetStopAnimationOnCompletion(ProgressBar progressBar, bool value) { 
     progressBar.SetValue(StopAnimationOnCompletionProperty, value); 
    } 

    private static void OnStopAnimationOnCompletionChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { 
     var progressBar = obj as ProgressBar; 
     if (progressBar == null) return; 

     var stopAnimationOnCompletion = (bool)e.NewValue; 

     if (stopAnimationOnCompletion) { 
      progressBar.Loaded += StopAnimationOnCompletion_Loaded; 
      progressBar.ValueChanged += StopAnimationOnCompletion_ValueChanged; 
     } else { 
      progressBar.Loaded -= StopAnimationOnCompletion_Loaded; 
      progressBar.ValueChanged -= StopAnimationOnCompletion_ValueChanged; 
     } 

     if (progressBar.IsLoaded) { 
      ReevaluateAnimationVisibility(progressBar); 
     } 
    } 

    private static void StopAnimationOnCompletion_Loaded(object sender, RoutedEventArgs e) { 
     ReevaluateAnimationVisibility((ProgressBar)sender); 
    } 

    private static void StopAnimationOnCompletion_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { 
     var progressBar = (ProgressBar)sender; 

     if (e.NewValue == progressBar.Maximum || e.OldValue == progressBar.Maximum) { 
      ReevaluateAnimationVisibility(progressBar); 
     } 
    } 

    private static void ReevaluateAnimationVisibility(ProgressBar progressBar) { 
     if (GetStopAnimationOnCompletion(progressBar)) { 
      var animationElement = GetAnimationElement(progressBar); 
      if (animationElement != null) { 
       if (progressBar.Value == progressBar.Maximum) { 
        animationElement.SetCurrentValue(UIElement.VisibilityProperty, Visibility.Collapsed); 
       } else { 
        animationElement.InvalidateProperty(UIElement.VisibilityProperty); 
       } 
      } 
     } 
    } 

    private static DependencyObject GetAnimationElement(ProgressBar progressBar) { 
     var template = progressBar.Template; 
     if (template == null) return null; 

     return template.FindName("PART_GlowRect", progressBar) as DependencyObject; 
    } 
} 

Về cơ bản, nó thêm bộ xử lý ValueChanged điều chỉnh khả năng hiển thị của phần tử hoạt ảnh.

Một vài lưu ý:

  • Tôi đang sử dụng "PART_GlowRect" để tìm các yếu tố động, mặc dù có người gọi đây là hack. Tôi không đồng ý: tên phần tử này được chính thức ghi lại thông qua TemplatePartAttribute, mà bạn có thể thấy trong ProgressBar's declaration. Mặc dù sự thật là điều này không nhất thiết đảm bảo rằng phần tử được đặt tên tồn tại, lý do duy nhất nó bị thiếu là nếu tính năng hoạt ảnh không được hỗ trợ chút nào. Nếu nó được hỗ trợ nhưng sử dụng một tên phần tử khác với tên của một tài liệu, tôi sẽ xem xét rằng một lỗi, không phải là một chi tiết thực hiện.

  • Vì tôi đang kéo phần tử ra khỏi mẫu, nên cũng cần xử lý sự kiện Loaded (được nâng lên khi mẫu được áp dụng) để chờ mẫu khả dụng trước khi cố đặt chế độ hiển thị ban đầu, và nếu cần thiết đặt lại khi mẫu được thay thế khi đang di chuyển bằng thay đổi chủ đề.

  • Thay vì chuyển đổi qua lại một cách rõ ràng Visibility giữa CollapsedVisible, Tôi đang sử dụng SetCurrentValue để thiết lập để Collapsed, và InvalidateProperty để thiết lập lại nó. SetCurrentValue áp dụng giá trị không ưu tiên so với các nguồn giá trị khác và InvalidateProperty đánh giá lại thuộc tính mà không xem xét cài đặt SetCurrentValue. Điều này đảm bảo rằng nếu có các kiểu hoặc trình kích hoạt hiện có sẽ ảnh hưởng đến khả năng hiển thị trong điều kiện bình thường (tức là khi nó là không phải ở 100%), nó sẽ đặt lại hành vi đó nếu thanh tiến trình được sử dụng lại (từ 100% trở lại 0%) thay vì được mã hóa cứng thành Visible.

+0

Nó hoạt động tốt, nhưng làm thế nào tôi có thể thiết lập bàn chải màu rắn của thanh tiến trình như trong câu trả lời trước đó? – monstr

+0

@monstr Bạn không thể thực sự làm điều đó một cách đáng tin cậy mà không thay thế toàn bộ mẫu kiểm soát. Về mặt lý thuyết bạn có thể ghi đè lên [OnApplyTemplate] (http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.onapplytemplate (v = vs.110) .aspx) để tìm và sửa đổi các phần tử tạo nên giao diện 3D ("Hoạt hình" và "Lớp phủ"), như tôi đã làm với "PART_GlowRect". Nhưng chúng không được đảm bảo các phần của mẫu, chúng có thể thay đổi tùy thuộc vào chủ đề Windows hiện tại. – nmclean

+1

Tôi đã thực hiện nó bằng cách thêm một số mã trong phương thức 'ReevaluateAnimationVisibility' của bạn. Tôi đã sử dụng phần 'Hoạt hình' của mẫu và được xác minh bằng null, vì vậy ngay cả khi không có phần' Hoạt hình' trong chủ đề Windows hiện tại - nó sẽ không gây ra ngoại lệ. – monstr

Các vấn đề liên quan