转自:http://blogs.microsoft.co.il/blogs/tomershamam/archive/2010/07/22/how-to-bind-toolbar-items.aspx
工具条中的按钮和窗口中的按钮是相同的,唯一的区别是可视化外观。ToolBar采取了覆盖某些类型资源的默认样式。如果你不想使用工具条默认采取的样式,你可以手动设置Button.Style属性。 这篇文章介绍了将ToolBar绑定到一个包含CommandModel类型的ObservableCollection,CommandModel是一个包含三个属性的类型,在Window.Resources中定义了应用到此数据对象的模板(参考:DataTemplate.DataType 属性),然后再这个数据模板中出现的Button,其样式被定义为ToolBar为按钮应用的默认样式(参考:ToolBar.ButtonStyleKey 属性)
How to bind ToolBar items
You have created a view-model for your ToolBar control, and you have a collection of commands exposed by the view-model as a property (Commands). Now you want to bind your ToolBar.ItemsSource with that property, so you have something like this:
<ToolBar ItemsSource="{Binding Commands}" Height="64" />
Of course, you want to have each command as a button, so you’ve created a DataTemplate:
<DataTemplate DataType="{x:Type local:CommandModel}"> <Button Command="{Binding Command}"> <StackPanel> <Image Source="{Binding Icon}" Width="32" Height="32" /> <TextBlock Text="{Binding Title}" VerticalAlignment="Center" /> </StackPanel> </Button> </DataTemplate>
Gluing all together you end up like this:
Now you’re asking yourself, why does the toolbar buttons looks totally different comparing to toolbar without binding?
The answer to this question, like other weirdo things in WPF, hidden inside the control’s code.
Looking with Reflector at ToolBar.PrepareContainerForItemOverride, you find this:
What happens here is that the toolbar picks a well known style-key based on the element type (the container) and not the item type (our CommandModel). For example, if the item added to the toolbar is of type Button, the toolbar picks the ButtonStyleKey, and set the button default-style-key with that key. This is how a button added directly to the toolbar gets a special style.
Using data binding, we are not adding Buttons… we are actually adding CommandModel instances, so the toolbar ignores it, ‘name’ stays null and nothings special is really happens, so our DataTemplate’s button stays with its normal style.
To overcome this issue, we simply set our button style explicitly with the ToolBar’s ready-made button style like this:
<DataTemplate DataType="{x:Type local:CommandModel}"> <Button Command="{Binding Command}" Style="{DynamicResource ResourceKey={x:Static ToolBar.ButtonStyleKey}}"> <StackPanel> <Image Source="{Binding Icon}" Width="32" Height="32" /> <TextBlock Text="{Binding Title}" VerticalAlignment="Center" /> </StackPanel> </Button> </DataTemplate>
That's it, now our data-bound toolbar works as expected.
Download the code from here.
<Window x:Class="WPFToolbarBinding.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPFToolbarBinding" Title="Toolbar Binding" SizeToContent="WidthAndHeight"> <Window.Resources> <DataTemplate DataType="{x:Type local:CommandModel}"> <Button Command="{Binding Command}" Style="{DynamicResource ResourceKey={x:Static ToolBar.ButtonStyleKey}}"> <StackPanel> <Image Source="{Binding Path=Icon}" Width="32" Height="32" /> <TextBlock Text="{Binding Path=Title}" VerticalAlignment="Center" /> </StackPanel> </Button> </DataTemplate> </Window.Resources> <Window.DataContext> <local:CommandsViewModel /> </Window.DataContext> <ToolBar ItemsSource="{Binding Path=Commands}" Height="64"/> </Window>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Input; using System.Windows.Media; using System.Collections.ObjectModel; using System.Windows; namespace WPFToolbarBinding { public class MessageCommand : ICommand { public string Message { get; set; } public MessageCommand(string message) { Message = message; } public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { MessageBox.Show(Message); } } public class CommandModel { public ICommand Command { get; set; } public string Icon { get; set; } public string Title { get; set; } } public class CommandsViewModel // ViewModelBase ;-) { private readonly ObservableCollection<CommandModel> _commands = new ObservableCollection<CommandModel>(); public ObservableCollection<CommandModel> Commands { get { return _commands; } } public CommandsViewModel() { _commands.Add(new CommandModel() { Command = new MessageCommand("Printing..."), Icon = "Icons/Printer.ico", Title = "Print" }); _commands.Add(new CommandModel() { Command = new MessageCommand("Disk Usage..."), Icon = "Icons/Disk.ico", Title = "Disk" }); _commands.Add(new CommandModel() { Command = new MessageCommand("Statistics..."), Icon = "Icons/Statistics.ico", Title = "Statistics" }); _commands.Add(new CommandModel() { Command = new MessageCommand("Users..."), Icon = "Icons/User.ico", Title = "Users" }); _commands.Add(new CommandModel() { Command = new MessageCommand("Settings..."), Icon = "Icons/Toolbox.ico", Title = "Settings" }); } } }