Using vector data for AppBar icons in XAML
| CommentsI was helping a friend today doing some over-the-shoulder code review and suggestions for his Windows app he’s writing for the store. In doing this I asked a question about how to perform a certain action. He indicated that he put those functions in the AppBar and was it not obvious I was supposed to use them. I looked at the AppBar again and found out why I wasn’t drawn to them. First, the labels he used weren’t descriptive to me and relied too much on me looking at the actual icon. Second, however, is that when I hovered over them I received no visual feedback I was expecting and immediately thought they were just disabled (the color was slightly gray as well which probably didn’t help this).
I mentioned these to him and noted he should use the AppBarButtonStyle base definition that comes in the Visual Studio templates as a guide and just set the appropriate content. He proceeded to let me know that he received the vector data from Syncfusion’s Metro Studio product. We then began to examine the vector data. Metro Studio is doing probably too much than it needs to for AppBar button definitions. To be fair, it serves a goal to get Windows UI style icons for the developers in XAML form. This is great! However, if my intention is to use them for AppBar buttons, then it is doing too much. It led me down a path to see how easy it was to use vector data with the default AppBarButtonStyle definition. Turns out it wasn’t as simple as I thought. Let me first explain the core issue then walk through a step on how to do this with vector data.
Fill versus Foreground
The main culprit is Fill vs. Foreground. The default AppBarButtonStyle has the different visual states for setting the correct default/inverted colors for your icon within the ellipse. These visual states however set these colors based on changing the Foreground property of the ContentPresenter. When your content is text – as is the case with the default styles that are commented out – this works fine. However the Path element in XAML understands Fill and Foreground doesn’t really apply. So while the vector data could be represented in the icon location, it wasn’t working on the different states because the Foreground value changing had no effect!
Let’s see how we can accomplish this…
Step 1: Get your vector data
I mentioned that we discovered this using Syncfusion’s Metro Studio, but this is one way where you can get vector-based artwork for XAML. Another is http://xamlproject.com which is similar in how it does things. Both will give you vector data based on a selected icon.
NOTE: The one thing I don’t like about The XAML Project site is the sizer doesn’t tell me what size I’m using. Metro Studio gave me the option to set a specific size.
When we launch Metro Studio we can search for different items based on keywords. Since the Segoe UI Symbol font includes a ton of these for us (all of which are represented in a commented-out style definition in StandardStyles.xaml you can just uncomment and use) I will pick an obscure one called “wash” from Metro Studio. When I select to edit it I’m presented with a default UI to modify the data:
Now notice how it gives me options for background shapes, etc.? For an AppBar button you do not these shapes so you can uncheck that option. Secondarily the ideal size is 20px with no padding. The colors (background color and icon color) don’t really matter here as we aren’t going to use them to get the default AppBar button style. My final options look like this:
Notice I unchecked the Background Shapes option, chose Custom Size and entered 20 with 0 padding.
Now what I do is click the XAML button and am presented with the full definition. You don’t need it all. Just copy only the path Data to your clipboard or wherever you can get it later.
Now that we have our vector data we need to put it in our button.
Step 2: Creating the path button definition
Because of the Fill versus Foreground issue we can’t just simply use the AppBarButtonStyle as the button style and just set our vector data. We will not receive the correct state visuals when it is used. So we first need to re-define a base that we can use. Since I may use other vector data in the future, I’ll encapsulate this in a new base style I’ll call PathBasedAppBarButtonStyle. This will allow me to remap Foreground to my Fill property to achieve the desired outcome. The resulting style is this:
1: <Style x:Key="PathBasedAppBarButtonStyle" BasedOn="{StaticResource AppBarButtonStyle}" TargetType="ButtonBase">
2: <Setter Property="ContentTemplate">
3: <Setter.Value>
4: <DataTemplate>
5: <Path Width="20" Height="20"
6: Stretch="Uniform"
7: Fill="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=TemplatedParent}}"
8: Data="{Binding Path=Content, RelativeSource={RelativeSource Mode=TemplatedParent}}"/>
9: </DataTemplate>
10: </Setter.Value>
11: </Setter>
12: </Style>
Notice that for Fill and Data I’m using Binding with the RelativeSource being the TemplatedParent…the actual Button. This allows me to essentially get at those properties that will be set and put them in my Path element that makes up the container for the vector data.
Step 3: Setting our custom vector data
Using the exported definition from my “wash” icon above, I can now use the style in my AppBar resulting in something like this:
1: <Page.BottomAppBar>
2: <AppBar IsOpen="True">
3: <Button Style="{StaticResource PathBasedAppBarButtonStyle}"
4: AutomationProperties.Name="Wash" AutomationProperties.AutomationId="WashAppBarButton"
5: Content="M9.6803506,4.8474294C10.209365 ... 3.5652149,0z" />
6: </AppBar>
7: </Page.BottomAppBar>
Please note that I trimmed the Path data so as not to take up so much space for your reading. I set my Button to use the base style, then set my label (via AutomationProperties.Name) and the Content is the exported vector data from the tool.
NOTE: AutomationProperties.Name is used here in the base AppBarButtonStyle to enable us to expose the Content property as the icon and still have a label. By using this property you also get accessibility for free in using these styles for this particular instance. Screen readers will read out the name you set when using this.
The resulting visual is correct in the AppBar UI guidelines:
Now when my users mouse-hover or press this button the correct visuals and state changes will happen.
Summary
The default AppBarButtonStyle base definition provided from the Visual Studio templates is optimized for the 150+ styles provided in StandardStyles.xaml and font-based vector data. For most, this is likely more than enough to use the same icons that are used throughout the operating system to give a consistent feel. If you find one in the 150+ styles provided you should use that. However if there is something entirely custom or not available in those, you can use your own vector data to provide your AppBar icon. Using fonts and vectors make it easy to scale up to the different resources without having to provide different images if you used that method. Defining a new base for vector data isn’t difficult and provides us the flexibility when we need it!
Hope this helps!