65.9K
CodeProject 正在变化。 阅读更多。
Home

战地 4 进度指示器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (5投票s)

2013年12月15日

CPOL

2分钟阅读

viewsIcon

23453

downloadIcon

307

用这个控件将您的个人游戏偏好隐藏在显眼的地方

引言

我喜欢游戏《战地 4》。
这款游戏在几乎所有方面都设计得非常好。
我特别喜欢这种想法:你从一个终端开始游戏,在那里你可以看到一些闪烁的图像(扭曲的),显示地图正在加载,同时提示符在右上角等待输入。

当页面在 Battlelog 上加载时,该提示符也用作进度指示器。

设计

提示符的设计看起来像一个旧终端提示符,高度大约有 10 行。每一行之间都有明显的间隙,表明这是一款非常老的显示器。
当提示符上升到全提示符时,也会出现一个伪像,例如光晕,表示刷新率很慢。
该提示符根据游戏的状态显示三种不同的模式。在 Battlelog 上,它始终是相同的模式。

进度控件

我决定将提示符创建为进度控件,可以像任何其他进度控件一样使用。
这将允许我和其他喜欢《战地》的人在我们的、原本沉闷的金融应用程序中展示进度,脸上带着微笑。

进度指示器可以这样使用

//
// Using Bf4ProgressIndicator
//
<Grid>
  <controls1:Bf4ProgressIndicator Margin="5" Value="{Binding ...}" PromptColor="White"/>
</Grid>  

该控件具有以下属性

指示器将在 3 种模式之间切换

  • 模式 1) 值低于最大值的 1/3。一个完整的提示符只是在闪烁。
  • 模式 2) 值在最大值的 1/3 到 2/3 之间。下划线向上移动。
  • 模式 3) 值高于最大值的 1/3。在完整提示符和下划线之间切换。

每种模式都有自己的样式。
样式在值更改时设置,并根据上述规则设置。

这是 ProgressChanged 方法

/// <summary>
/// Progresses the changed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="DependencyPropertyChangedEventArgs"/> 
/// instance containing the event data.</param>
protected void ProgressChanged(Object sender, DependencyPropertyChangedEventArgs e)
{
  OnValueChanged(new RoutedPropertyChangedEventArgs<double>(
    e.OldValue != null?(double)e.OldValue:0, 
    e.NewValue!=null?(double)e.NewValue:0));
  var oldStyle = Style;
  var diff = Maximum - Minimum;
  Style = Value < Minimum + diff/3 ? style1 : Value > Maximum - diff/3 ? style3 : style2;
  if (oldStyle == Style) return;
  if (promptStoryboard != null && partPrompt != null) 
    promptStoryboard.Stop(partPrompt);
  if (promptStoryboard1 != null && partPrompt1 != null) 
    promptStoryboard1.Stop(partPrompt1);
  if (promptStoryboard2 != null && partPrompt2 != null) 
    promptStoryboard2.Stop(partPrompt2);
  if (promptStoryboard3 != null && partPrompt3 != null) 
    promptStoryboard3.Stop(partPrompt3);
  promptStoryboard = GetTemplateChild("PART_StoryBoard") as Storyboard;
  promptStoryboard1 = GetTemplateChild("PART_StoryBoard1") as Storyboard;
  promptStoryboard2 = GetTemplateChild("PART_StoryBoard2") as Storyboard;
  promptStoryboard3 = GetTemplateChild("PART_StoryBoard3") as Storyboard;
  partPrompt = GetTemplateChild("PART_Prompt") as Rectangle;
  partPrompt1 = GetTemplateChild("PART_Prompt1") as Rectangle;
  partPrompt2 = GetTemplateChild("PART_Prompt2") as Rectangle;
  partPrompt3 = GetTemplateChild("PART_Prompt3") as Rectangle;
  if (promptStoryboard != null && partPrompt != null) { 
    promptStoryboard.Begin(partPrompt, true); }
  if (promptStoryboard1 != null && partPrompt1 != null) { 
    promptStoryboard1.Begin(partPrompt1, true); }
  if (promptStoryboard2 != null && partPrompt2 != null) { 
    promptStoryboard2.Begin(partPrompt2, true); }
  if (promptStoryboard3 != null && partPrompt3 != null) { 
    promptStoryboard3.Begin(partPrompt3, true); }
} 

在具有运行动画的不同样式之间切换将需要手动停止和启动情节提要。

进度指示器的视觉部分是作为样式创建的。
这是一个模式 1 的样式示例

<Style x:Key="Bf4ProgressIndicatorStyle1" TargetType="{x:Type local:Bf4ProgressIndicator}">
  <Setter Property="Background" Value="Transparent"/>
  <Setter Property="Foreground" Value="White"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:Bf4ProgressIndicator}">
        <Border BorderBrush="{TemplateBinding BorderBrush}" 
         BorderThickness="{TemplateBinding BorderThickness}" 
         Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
          <Grid>
            <Grid.RowDefinitions>
              <RowDefinition Height="1*"/>
              <RowDefinition Height="9*" MinHeight="{TemplateBinding MinHeight}"/>
              <RowDefinition Height="1*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="1*"/>
              <ColumnDefinition Width="1*"/>
              <ColumnDefinition Width="9*" MinWidth="{TemplateBinding MinWidth}"/>
              <ColumnDefinition Width="1*"/>
              <ColumnDefinition Width="1*"/>
            </Grid.ColumnDefinitions>
            <Rectangle x:Name="PART_Prompt" Grid.Row="1" Grid.Column="2" 
               Width="Auto" Height="Auto" Stretch="Fill">
            <Rectangle.Resources>
              <SolidColorBrush x:Key="PromptBrush" 
               Color="{Binding Path=PromptColor, 
               RelativeSource={RelativeSource AncestorType={x:Type local:Bf4ProgressIndicator}}}" />
            </Rectangle.Resources>
            <Rectangle.Fill>
              ...
            </Rectangle.Fill>
            <Rectangle.Effect>
              <DropShadowEffect BlurRadius="10" Color="{Binding Path=PromptColor, 
               RelativeSource={RelativeSource AncestorType={x:Type local:Bf4ProgressIndicator}}}" 
               Opacity="1" ShadowDepth="0" />
            </Rectangle.Effect>
            <Rectangle.Triggers>
              <EventTrigger RoutedEvent="Loaded">
                <BeginStoryboard>
                  <Storyboard x:Name="PART_StoryBoard">
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_Prompt" 
                     Storyboard.TargetProperty="Opacity" AutoReverse="False" 
                     RepeatBehavior="Forever">
                      <LinearDoubleKeyFrame Value="1" KeyTime="0:0:.1" />
                      <LinearDoubleKeyFrame Value="1" KeyTime="0:0:.2" />
                      <LinearDoubleKeyFrame Value="0" KeyTime="0:0:.3" />
                      <LinearDoubleKeyFrame Value="0" KeyTime="0:0:.7" />
                    </DoubleAnimationUsingKeyFrames>
                  </Storyboard>
                </BeginStoryboard>
              </EventTrigger>
            </Rectangle.Triggers>
          </Rectangle>
          <Rectangle x:Name="PART_HighLight" Grid.Row="1" Grid.Column="1" 
           Grid.ColumnSpan="3" Width="Auto" Height="Auto" 
           Stretch="Fill" Opacity="0">
            <Rectangle.Fill>
            ...
            </Rectangle.Fill>
            <Rectangle.Triggers>
              <EventTrigger RoutedEvent="Loaded">
                <BeginStoryboard>
                  <Storyboard x:Name="PART_StoryBoard2">
                    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="PART_HighLight" 
                     Storyboard.TargetProperty="Opacity" AutoReverse="False" 
                     RepeatBehavior="Forever">
                      <LinearDoubleKeyFrame Value=".25" KeyTime="0:0:.1" />
                      <LinearDoubleKeyFrame Value=".25" KeyTime="0:0:.15" />
                      <LinearDoubleKeyFrame Value="0" KeyTime="0:0:.2" />
                      <LinearDoubleKeyFrame Value="0" KeyTime="0:0:.7" />
                    </DoubleAnimationUsingKeyFrames>
                  </Storyboard>
                </BeginStoryboard>
              </EventTrigger>
            </Rectangle.Triggers>
          </Rectangle>
        </Grid>
      </Border>
    </ControlTemplate>
  </Setter.Value>
  </Setter>
</Style>

示例应用

示例应用程序说明了如何使用进度指示器以及不同的模式。
在游戏中,它主要用作黑底白字,但该控件允许使用任何颜色。

关注点

我尝试将使用的图像 (Geometry) 保留为静态资源,但我没有找到动态更改颜色的方法。
我最终将几何图形添加到视觉树中的每个元素中,以便我可以绑定到控件上的依赖项属性。

历史

  • 2013 年 12 月 15 日:版本 1.0.0.0
  • 2013 年 12 月 16 日:添加了 Nuget 包的链接
© . All rights reserved.