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

WPF:将我们的应用程序与 Windows 7 任务栏集成(I)

2010 年 7 月 24 日

CPOL

6分钟阅读

viewsIcon

44567

downloadIcon

1085

Windows 7 的任务栏是操作系统的一项重大变化,带来了许多优势。

引言

在上一篇文章中,我们讨论了利用应用程序运行所在的操作系统提供的功能的重要性,并展示了如何将我们的应用程序与 Windows 7 电源系统集成,使其“节能”。这一次,我们将讨论 Windows 7 的任务栏;它是操作系统的一项重大变化,并带来了许多优势,可以促进我们的应用程序与用户之间快速直观的通信。

  • IconOverlay:工具栏的此属性允许我们在代码中,在我们应用程序的图标的右下角放置一个 16x16 的小图标,以便在不显示在屏幕上的情况下,向用户传达应用程序状态的更新。
  • ProgressBar:非常有用,它允许我们在应用程序图标下方放置一个进度条,具有多种状态,能够在用户使用其他应用程序时,向用户传达任务的状态和进度,从而最大限度地提高生产力。
  • Jumplist:Jumplist 包含对我们应用程序部分的快速链接,当您右键单击任务栏上的图标时会出现;我们可以让我们的应用程序直接跳转到某个特定窗口,而无需先启动。
  • Thumbnail toolbars:当您将鼠标悬停在活动应用程序的图标上时,Windows 7 会提供该应用程序窗口的预览;借助 Thumbnail Toolbars,您可以向该视图添加在应用程序中执行的按钮。

在本文中,我们将重点介绍前两个:IconOverlay 和 ProgressBar,其余的将在另一篇文章中讨论。

.NET 3.5 vs. .NET 4

要访问所有这些功能,.NET 3.5 只有一个方法:与非托管 Win32 API 进行互操作(就像有关电源管理的文章一样);然而,.NET 4 将 Windows 7 任务栏的功能整合到框架本身,从而“使用和享受”这些功能变得更加容易。本文中的代码仅适用于 .NET 4。

开始吧

首先,一如既往,一张图片胜过千言万语;这次,我们有两张。

Tab001_small.PNGtab002_small.PNG

正如您所见,我已将原始计划与图标一起放入....是的,我知道,我在设计方面的参与程度与大规模杀伤性武器一样有害……但这正是我成为程序员的原因 :)

让我们开始吧。您必须首先创建一个新的 WPF 应用程序项目(文件 > 新建 > 项目);一旦建立,我们将设计 MainWindow.xaml 文件中的主屏幕。

我在本应用程序中使用的样式可在下载文件 Application.xaml 中找到;如果您对如何使用/创建样式有疑问,请查看我关于此主题的两篇文章:此处此处

好了,要开始使用任务栏,我们将在 XAML 中添加一个类型为 TaskbarItemInfo 的对象。

<Window.TaskbarItemInfo>
    <TaskbarItemInfo/>
</Window.TaskbarItemInfo>

这样,我们就可以轻松快捷地访问 Windows 7 任务栏的许多属性。

IconOverlay

Tab001.PNG

现在我们将开始处理 IconOverlay。第一步是在我们的项目中将要使用的图像文件(PNG 或 ICO 以实现透明效果)添加为资源。

完成此操作后,我们必须为每个图像在我们的窗口上创建一个 DrawingImage 资源;在我的例子中,有五个。

<Window.Resources>
    <DrawingImage x:Key="IconRed">
        <DrawingImage.Drawing>
            <ImageDrawing Rect="0,0,16,16" 
              ImageSource="/WPF%20Icon%20Overlay%20And%20ProgressBar;
                           component/images/FireToy.ico" />
        </DrawingImage.Drawing>
    </DrawingImage>
    <DrawingImage x:Key="IconGreen">
        <DrawingImage.Drawing>
            <ImageDrawing Rect="0,0,16,16" 
               ImageSource="/WPF%20Icon%20Overlay%20And%20ProgressBar;
                            component/images/GreenToy.ico" />
        </DrawingImage.Drawing>
    </DrawingImage>
    <DrawingImage x:Key="IconBlue">
        <DrawingImage.Drawing>
            <ImageDrawing Rect="0,0,16,16" 
     ImageSource="/WPF%20Icon%20Overlay%20And%20ProgressBar;
                  component/images/BlueToy.ico" />
        </DrawingImage.Drawing>
    </DrawingImage>
    <DrawingImage x:Key="IconPink">
        <DrawingImage.Drawing>
            <ImageDrawing Rect="0,0,16,16" 
     ImageSource="/WPF%20Icon%20Overlay%20And%20ProgressBar;
                  component/images/PinkToy.ico" />
        </DrawingImage.Drawing>
    </DrawingImage>
    <DrawingImage x:Key="IconBlack">
        <DrawingImage.Drawing>
            <ImageDrawing Rect="0,0,16,16" 
     ImageSource="/WPF%20Icon%20Overlay%20And%20ProgressBar;
                  component/images/NinjaToy.ico" />
        </DrawingImage.Drawing>
    </DrawingImage>
</Window.Resources>

完成后,我们创建带有按钮等的 TabItem

<TabItem Header="Taskbar IconOverlay" Name="TabItem1">
    <Grid>
        <Grid.Background>
            <ImageBrush ImageSource="pack://application:,,,/WPF Icon Overlay And ProgressBar;
         component/images/overlay.png" Stretch="Uniform" Opacity=".2" />
        </Grid.Background>
        <Grid.RowDefinitions>
            <RowDefinition Height=".33*"></RowDefinition>
            <RowDefinition Height=".33*"></RowDefinition>
            <RowDefinition Height=".33*"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width=".2*"></ColumnDefinition>
            <ColumnDefinition Width=".2*"></ColumnDefinition>
            <ColumnDefinition Width=".2*"></ColumnDefinition>
            <ColumnDefinition Width=".2*"></ColumnDefinition>
            <ColumnDefinition Width=".2*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
                    
        <Label Grid.ColumnSpan="5" Grid.Row="0" VerticalContentAlignment="Center" 
               HorizontalContentAlignment="Center" FontSize="24" Foreground="White"
               Content="Seleccione una imagen para superponer">
        </Label>
                    
        <Button Grid.Column="0" Grid.Row="1" Name="btnIconRed" 
                           Width="64" Height="64" Padding="0">
            <Button.Content>
                <Image Source="{StaticResource IconRed}" Width="56" 
                     Height="56" Stretch="Uniform" Margin="0"></Image>
             </Button.Content>
        </Button>
        <Button Grid.Column="1" Grid.Row="1" Name="btnIconGreen" 
                     Width="64" Height="64" Padding="0">
            <Button.Content>
                <Image Source="{StaticResource IconGreen}" 
                        Width="56" Height="56" Stretch="Uniform" 
                        Margin="0"></Image>
            </Button.Content>
        </Button>
        <Button Grid.Column="2" Grid.Row="1" Name="btnIconBlue" 
                    Width="64" Height="64" Padding="0">
            <Button.Content>
                <Image Source="{StaticResource IconBlue}" 
                      Width="56" Height="56" Stretch="Uniform" 
                      Margin="0"></Image>
            </Button.Content>
        </Button>
        <Button Grid.Column="3" Grid.Row="1" Name="btnIconPink" 
                       Width="64" Height="64" Padding="0">
            <Button.Content>
                <Image Source="{StaticResource IconPink}" 
                      Width="56" Height="56" Stretch="Uniform" 
                      Margin="0"></Image>
            </Button.Content>
        </Button>
        <Button Grid.Column="4" Grid.Row="1" Name="btnIconBlack" 
                       Width="64" Height="64" Padding="0">
            <Button.Content>
                <Image Source="{StaticResource IconBlack}" 
                        Width="56" Height="56" Stretch="Uniform" 
                        Margin="0"></Image>
            </Button.Content>
        </Button>                    
                    
        <Button Grid.ColumnSpan="5" Grid.Row="2" Name="btnIconReset" 
               Width="125" Height="30" Content="Reset Overlay"></Button>
    </Grid>
</TabItem>

所有 IconOverlay 所需的 XAML 都已完成。现在让我们编写代码。第一件事是在我们的 Window Loaded 事件中,将 IconOverlay 的状态设置为 none(无)。

Private Sub MainWindow_Loaded(ByVal sender As Object, _
         ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    Me.TaskbarItemInfo.Overlay = Nothing
End Sub

您可以看到,由于我们在 XAML 中创建的 TaskbarItemInfo 对象,访问任务栏中应用程序图标的属性非常简单。在这种情况下,分配 Nothing 表示我们没有关联的图标。

现在,在我们每个按钮的 Click 事件中,我们将把与按钮关联的图标设置为要放置在任务栏上的图标。

Private Sub btnIconBlack_Click(ByVal sender As Object, _
        ByVal e As System.Windows.RoutedEventArgs) Handles btnIconBlack.Click
    Me.TaskbarItemInfo.Overlay = CType(Resources("IconBlack"), ImageSource)
End Sub

Private Sub btnIconBlue_Click(ByVal sender As Object, _
        ByVal e As System.Windows.RoutedEventArgs) Handles btnIconBlue.Click
    Me.TaskbarItemInfo.Overlay = CType(Resources("IconBlue"), ImageSource)
End Sub

Private Sub btnIconGreen_Click(ByVal sender As Object, _
        ByVal e As System.Windows.RoutedEventArgs) Handles btnIconGreen.Click
    Me.TaskbarItemInfo.Overlay = CType(Resources("IconGreen"), ImageSource)
End Sub

Private Sub btnIconPink_Click(ByVal sender As Object, _
        ByVal e As System.Windows.RoutedEventArgs) Handles btnIconPink.Click
    Me.TaskbarItemInfo.Overlay = CType(Resources("IconPink"), ImageSource)
End Sub

Private Sub btnIconRed_Click(ByVal sender As Object, _
        ByVal e As System.Windows.RoutedEventArgs) Handles btnIconRed.Click
    Me.TaskbarItemInfo.Overlay = CType(Resources("IconRed"), ImageSource)
End Sub

Private Sub btnIconReset_Click(ByVal sender As Object, _
        ByVal e As System.Windows.RoutedEventArgs) Handles btnIconReset.Click
    Me.TaskbarItemInfo.Overlay = Nothing
End Sub

好吧,这并不难,对吧?在文章的开头,我们在窗口中创建了一个 DrawingImage 类型的资源。然后,我们将此资源转换为 ImageSource 类型,这是 TaskbarItemInfo Overlay 属性所期望的类型,并将其分配给它。如果您按下与火玩具关联的按钮,就会得到这个结果。

taskbar_icon.PNG

现在,如果我们按下 Reset Overlay 按钮,我们的应用程序图标将恢复到其原始状态。

taskbar_no_icon.PNG

如您所见,这是一段非常简单的代码,它开辟了与用户沟通的新途径。例如,我们可以显示一个图标,指示应用程序出现故障,或者它需要用户干预某个任务或新数据到达,因为用户不必查看屏幕就知道发生了什么,他们可能正在查看电子邮件、处理 Excel 表格,或仅仅是浏览一些网站,但会立即知道需要干预我们的应用程序。

进度条

tab002.PNG

如果您认为 IconOverlay 的使用很简单,那么在任务栏图标上使用进度条就更简单了。我们将从设计屏幕开始。

<TabItem Header="Taskbar ProgressBar" Name="TabItem2">
    <Grid>
        <Grid.Background>
            <ImageBrush ImageSource="pack://application:,,,/WPF Icon Overlay 
                        And ProgressBar;component/images/progressbar.png" 
              Stretch="Uniform" Opacity=".2" />
        </Grid.Background>
        <Grid.RowDefinitions>
            <RowDefinition Height=".25*"></RowDefinition>
            <RowDefinition Height=".25*"></RowDefinition>
            <RowDefinition Height=".25*"></RowDefinition>
            <RowDefinition Height=".25*"></RowDefinition>
        </Grid.RowDefinitions>
        <Label Grid.Row="0" VerticalContentAlignment="Center" 
                 HorizontalContentAlignment="Center" FontSize="24" Foreground="White"
                 Content="Mueve el Slider para Aumentar/Disminuir">
        </Label>
                    
        <ComboBox Grid.Row="1" Name="cmbEstilos" Height="30" 
               Width="Auto" Margin="5" HorizontalAlignment="Stretch" 
               VerticalAlignment="Stretch">
            <ComboBox.Items>
                <ComboBoxItem Name="ItemNormal" Content="Normal"></ComboBoxItem>
                <ComboBoxItem Name="ItemError" Content="Error"></ComboBoxItem>
                <ComboBoxItem Name="ItemStop" Content="Stop"></ComboBoxItem>
                <ComboBoxItem Name="ItemIndeterminate" 
                           Content="Indeterminado"></ComboBoxItem>
                <ComboBoxItem Name="ItemNone" Content="Ninguno"></ComboBoxItem>
            </ComboBox.Items>
        </ComboBox>
                    
        <ProgressBar Grid.Row="2" Name="Pbar" Height="30" Width="Auto" 
              Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Center"
              Minimum="0" Maximum="100" Value="{Binding Path=Value, ElementName=SlidValue}"/>
                    
        <Slider Grid.Row="3" Name="SlidValue" Height="30" Width="Auto" 
              Margin="5" HorizontalAlignment="Stretch" VerticalAlignment="Center" 
              Minimum="0" Maximum="100" Value="50" SmallChange="1">
        </Slider>
    </Grid>
</TabItem>

XAML 代码很简单。最值得注意和真正有用的是我们应用于 ProgressBarValue 属性的绑定。此绑定指向 Slider 控件的 Value 属性。这样,每次我们移动滑块时,进度条都会自动更新。

正如您所看到的,有一个带有多个选项的组合框;这些是进度条在任务栏上可能具有的状态。

  • Normal 进度条正常显示,绿色。
  • Error 与正常相同,只是这次显示为红色。
  • Stop 进度条已停止,无法前进,颜色为黄色。
  • Indeterminate 条形图显示不确定的进度,进行循环动画。
  • None 不显示任务栏上的进度条。

后台长时间运行的任务可能需要几分钟,用户可以最小化应用程序并查看进度和结果,而无需最大化应用程序,并且可以在使用其他应用程序时进行。您可以看到任务何时停止并需要您的关注(黄色条),何时发生错误(红色条),或者仅仅是任务的进度,并查看是否有时间去喝杯咖啡 :)。如果我们将其与 IconOverlay 结合使用,您就会意识到我们与用户沟通的力量。他们甚至可以在不最大化窗口的情况下,在做其他事情时了解应用程序的状态;不再需要盯着屏幕上的进度条。

代码也很简单;在 Loaded 事件中,我们将进度条状态设置为 None

Private Sub MainWindow_Loaded(ByVal sender As Object, _
         ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
    Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.None
End Sub

现在,控制 Slider 控件的 valueChanged 事件,将值赋给我们在任务栏上的进度条。

Private Sub SlidValue_ValueChanged(ByVal sender As Object, _
            ByVal e As System.Windows.RoutedPropertyChangedEventArgs(Of Double)) 
            Handles SlidValue.ValueChanged
    Me.TaskbarItemInfo.ProgressValue = SlidValue.Value / 100
End Sub

这里有两点。首先,不需要在我们的窗口上有一个进度条来操作任务栏上的进度条,但我认为用户同时拥有两者是必要的,而不是强迫他们在查看我们的窗口时必须查看任务栏。其次,TaskbarItemInfo.ProgressValue 属性的范围是 0 到 1,所以我们必须将我们的 Slider 控件或进度值除以 100,并记住始终将值保持在 0 和 100 之间以避免问题。

最后,我们将处理组合框的 SelectionChanged 事件,以便为进度条更改为下拉列表中指定的类型。

Private Sub cmbEstilos_SelectionChanged(ByVal sender As Object, _
          ByVal e As System.Windows.Controls.SelectionChangedEventArgs) _
          Handles cmbEstilos.SelectionChanged
    Select Case DirectCast(e.AddedItems(0), System.Windows.Controls.ComboBoxItem).Content
        Case "Normal"
            SlidValue.IsEnabled = True
            Pbar.IsIndeterminate = False
            Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.Normal
        Case "Error"
            SlidValue.IsEnabled = True
            Pbar.IsIndeterminate = False
            Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.Error
        Case "Stop"
            SlidValue.IsEnabled = True
            Pbar.IsIndeterminate = False
            Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.Paused
        Case "Indeterminado"
            SlidValue.IsEnabled = False
            Pbar.IsIndeterminate = True
            Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.Indeterminate
        Case "Ninguno"
            SlidValue.IsEnabled = True
            Pbar.IsIndeterminate = False
            Me.TaskbarItemInfo.ProgressState = Shell.TaskbarItemProgressState.None
    End Select
End Sub

这样,我们的应用程序就能够以不同的样式显示进度条。

pbar_normal.PNGpbar_error.PNGpbar_stop.PNGpbar_indeterminate.PNG

历史

  • 2010 年 7 月 24 日 - 初始发布。
© . All rights reserved.