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






4.50/5 (8投票s)
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。
开始吧
首先,一如既往,一张图片胜过千言万语;这次,我们有两张。
正如您所见,我已将原始计划与图标一起放入....是的,我知道,我在设计方面的参与程度与大规模杀伤性武器一样有害……但这正是我成为程序员的原因 :)
让我们开始吧。您必须首先创建一个新的 WPF 应用程序项目(文件 > 新建 > 项目);一旦建立,我们将设计 MainWindow.xaml 文件中的主屏幕。
我在本应用程序中使用的样式可在下载文件 Application.xaml 中找到;如果您对如何使用/创建样式有疑问,请查看我关于此主题的两篇文章:此处和此处。
好了,要开始使用任务栏,我们将在 XAML 中添加一个类型为 TaskbarItemInfo
的对象。
<Window.TaskbarItemInfo>
<TaskbarItemInfo/>
</Window.TaskbarItemInfo>
这样,我们就可以轻松快捷地访问 Windows 7 任务栏的许多属性。
IconOverlay
现在我们将开始处理 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
属性所期望的类型,并将其分配给它。如果您按下与火玩具关联的按钮,就会得到这个结果。
现在,如果我们按下 Reset Overlay 按钮,我们的应用程序图标将恢复到其原始状态。
如您所见,这是一段非常简单的代码,它开辟了与用户沟通的新途径。例如,我们可以显示一个图标,指示应用程序出现故障,或者它需要用户干预某个任务或新数据到达,因为用户不必查看屏幕就知道发生了什么,他们可能正在查看电子邮件、处理 Excel 表格,或仅仅是浏览一些网站,但会立即知道需要干预我们的应用程序。
进度条
如果您认为 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 代码很简单。最值得注意和真正有用的是我们应用于 ProgressBar
的 Value
属性的绑定。此绑定指向 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
这样,我们的应用程序就能够以不同的样式显示进度条。
历史
- 2010 年 7 月 24 日 - 初始发布。