How to reuse icons (xaml-paths) in a large project? - silverlight

I am part of the development of a larger-scale Silverlight 4 project, where we will have a set of symbols that should be used across different parts of the GUI (see the example below).
These icons are made from multiple paths directly in Blend, and will be used, either singly or as different visual states in usercontrols (with the same icon used in more than one context). In order to facilitate changing the design of a single icon, and having it propagate throughout the application, what is the best way to store these?
I have tried creating styles from them (right click -> edit style..), but this only allows me to create an empty style, without any path data. manually putting the xaml code for the grid containing the paths into a dictionary hasn't helped either, what am I missing?
How do I save the path and style (colour, stroke, fill, etc) information in an easy way, preferably in a resource dictionary, so I can easily reuse them in usercontrols and elsewhere, while maintaining the easy updating?
Example of the icons I'm trying to reuse:

<Style x:Key="MyIcon" TargetType="ContentControl">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Path Stretch="Fill" Fill="Red" Data="F1 M 24,13C 27.1521,13 29.9945,14.3258 32,16.4501L 32,11L 35,14L 35,22L 27,22L 24,19L 29.5903,19C 28.217,17.4656 26.2212,16.5 24,16.5C 20.1969,16.5 17.055,19.3306 16.5661,23L 13.0448,23C 13.5501,17.3935 18.262,13 24,13 Z M 24,31.5C 27.8031,31.5 30.945,28.6694 31.4339,25L 34.9552,25C 34.4499,30.6065 29.738,35 24,35C 20.8479,35 18.0055,33.6742 16,31.5499L 16,37L 13,34L 13,26L 21,26L 24,29L 18.4097,29C 19.783,30.5344 21.7787,31.5 24,31.5 Z "/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
And then use style with contentcontrol type (or derivates):
<ContentControl Style="{StaticResource MyIcon}" Width="20" Height="20" Grid.Row="0"/>
All styles can be inside some resource dictionary:
This is quite informative article:
http://blogs.infosupport.com/tips-for-effective-usage-of-resource-dictionaries-in-silverlight-and-wpf/

Related

How do I access the styled object from the value of a setter?

I am attempting to style every DataRecordPresenter in a XamDataGrid according to a VisualBrush that should flex according to the presenter in question. Specifically I'm aiming to highlight some rows, where the highlighting pattern is potentially complex (i.e. arbitrarily large, albeit in practice probably not more than 5-6 colours).
The solution I'm hoping to use looks something like this:
<!-- idp bound to namespace for Infragistics DataPresenters -->
<idp:XamDataGrid>
<idp:XamDataGrid.Resources>
<Style TargetType={x:Type idp:DataRecordPresenter>
<Style.Setters>
<!-- THE RELATIVESOURCE FAILS HERE -->
<Setter Property="Tag">
<Grid DataContext={Binding RelativeSource={RelativeSource Mode=FindAncestor,TargetType={x:Type idp:DataRecordPresenter}}}>
<!-- Content that relies on properties of the DataRecordPresenter -->
</Grid>
</Setter>
<Setter Property="Background">
<!-- THE RELATIVESOURCE WORKS HERE -->
<VisualBrush
ViewPort="12,12,12,12"
Visual="{Binding Tag,RelativeSource={RelativeSource Mode=FindAncestor,TargetType={x:Type idp:DataRecordPresenter}}"
>
</VisualBrush>
</Setter>
</Style.Setters>
</Style>
</idp:XamDataGrid.Resources>
</idp:XamDataGrid>
The issue is that whilst I am able to identify the DataRecordPresenter when constructing the actual VisualBrush (tested using Snoop - as intended the result is the contents of the Tag property), whilst trying to find the same object via the same mechanism from the context of the setter for the Tag property, I fail to identify any such ancestor.
I suspect this is because the Tag property is not associated with the visual (or logical) trees, whereas Background is, but I haven't managed to come up with a solution as yet that addresses the issue. I'd equally be happy to move the Grid into the VisualBrush, but I believe a VisualBrush is also detached from the relevant trees, so I don't think that'll work, or at least not with a simple inline definition.

Path.Data styling works only on first instance of styled object

A have a ListBox of items, every ListBoxItem contains an icon in the form of a Path object, like so:
<ListBox.ItemTemplate>
<DataTemplate>
<Grid ...>
...
<Path Margin="4" Style="{StaticResource ErrorIconPath}"
Stretch="Uniform" Width="26" Height="26"
RenderTransformOrigin="0.5,0.5" Grid.Column="1" Grid.Row="1"
UseLayoutRounding="False"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
The Path's style is contained in Appl.xaml (Application.Resources section) and is the following:
<Style x:Key="ErrorIconPath" TargetType="Path">
<Setter Property="Data" Value="F1M874.094,289.369L854.3,254.63C854.028,254.151 853.515,253.856 852.958,253.856 852.403,253.856 851.89,254.151 851.617,254.63L831.824,289.369C831.555,289.84 831.559,290.416 831.835,290.883 832.111,291.348 832.618,291.634 833.165,291.634L872.752,291.634C873.299,291.634 873.805,291.348 874.081,290.883 874.357,290.416 874.361,289.84 874.094,289.369 M855.653,287.189L850.264,287.189 850.264,282.745 855.653,282.745 855.653,287.189z M855.653,279.41L850.264,279.41 850.264,266.077 855.653,266.077 855.653,279.41z" />
</Style>
The trouble is that only the first item in the ListBox binds the Data property as expected, the other ones don't bind it at all (hence they appear as blank space, but match the size of the Path). Also when I use the style anywhere else (i.e. outside the ListBox), only the first instance that occurs will bind.
The weird thing is that if I define for example the Fill property in the Style instead of inline, it works just fine and doesn't exibit the same problems as the Path property.
My guess is that is has something to do with Data not being a primitive type, but I haven't found any fixes.
EDIT: Interestingly, when I bind the Data property directly to System.String resource, it works. I would still like to be able to define this property via a Style though.
EDIT 2: I've just came across the same issue in WPF, when setting Path to a Content of a Button via a Style that is used across more buttons. The path shows up in just one buttons, the others are blank.
Path.Fill is a DependencyProperty, while Path.Data isn't. Instead do:
<DataTemplate>
<Grid ...>
...
<ContentPresenter Content="{StaticResource MyPath}"/>
</Grid>
</DataTemplate>
ContentPresenter.Content is a DependencyProperty so this should work:
<Path x:Key="MyPath" Margin="4" Style="{StaticResource ErrorIconPath}"
Stretch="Uniform" Width="26" Height="26" VerticalAlignment="Center"
RenderTransformOrigin="0.5,0.5" Grid.Column="1" Grid.Row="1"
UseLayoutRounding="False" HorizontalAlignment="Center"
Data="F1M874.094,289.369L854.3,254.63C854.028,254.151 853.515,253.856 852.958,253.856 852.403,253.856 851.89,254.151 851.617,254.63L831.824,289.369C831.555,289.84 831.559,290.416 831.835,290.883 832.111,291.348 832.618,291.634 833.165,291.634L872.752,291.634C873.299,291.634 873.805,291.348 874.081,290.883 874.357,290.416 874.361,289.84 874.094,289.369 M855.653,287.189L850.264,287.189 850.264,282.745 855.653,282.745 855.653,287.189z M855.653,279.41L850.264,279.41 850.264,266.077 855.653,266.077 855.653,279.41z"/>
I am guessing that Geometry cannot be shared. Have you tried setting the x:Shared= "false" to:
<Style x:Key="ErrorIconPath" TargetType="Path">
I've experienced the same behavior in Silverlight and asked a similar question here on StackOverflow.com
( https://stackoverflow.com/q/13426198/1796930), but as I'm writing this, it's been 1 month and I've yet to get even a single answer.
However, as you mentioned in your first edit, I too was able to perform a workaround by creating a resource with my geometry data as a string and then binding the Data property of the Path objects to the string resource resource.
I also had to create two instances of the Path objects that were identical other than each one using a different resource (i.e. two different icons) and then binding the visibility of each to a property in my ViewModel to display the appropriate one.
I am very sure that you did not forgot the stroke here in Path style
<Setter Property="Stroke" Value="Red"/>
I have tested you code on my machine , it worked fine if above line added in style
My first tought was your Path would be broken or not valid. But then I saw you are using the Syncfusion Metro Studio. I tried it with exactly the same code you have and it worked very well. In a Data Template of 5 Items or as a single Path Item.
Have you tried to set the Fill statically to Red or something?
Also maybe try this for the Style definition
<Style x:Key="ErrorIconPath" TargetType="{x:Type Path}">
Third suggestion would be to move the style definition from the App to your Page or even to your Control itself.
To be sure there will be no default styles applied, try
OverridesDefaultStyle="True"
Hope this helps :)

Silverlight 4: ContentTemplate Background Change

I have a Header style for my datagrid custom header. I am using theme for my application. The problem here is the header background of the datagrid is not changing however when I remove the styles, the header background has no problem, it change.
Here's the themes
Here's my sample application, right click the grid and context menu will appear for the list of themes, select the different themes. I have two columns namely, with header style and without header style. See the difference. Thank you for your help.
<Style x:Key="DataGridHeaderStyle" TargetType="primitive:DataGridColumnHeader">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding}" Grid.Column="0" HorizontalAlignment="Left" />
<filter:DataGridColumnFilter Grid.Column="1" HorizontalAlignment="Right" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Thank you
I did a quick test, and it does not seem to work with either the tag approach or the ImplicitStyleManager attribute approach. This is probably because the style setter is outside the scope and therefore gets applied without a theme.
Suggestion 1: Look at the way that the Jet Pack theme handles it with resource dictionaries in App.xaml, and try to do something similar. (You will need the theme.xaml file for this. EDIT: This link might help.) If you want to change the theme dynamically, then consider this (old) post about swapping themes in resource dictionaries (I haven't tried it, but it should work).
Suggestion 2: You need to think about style inheritance. Since "BasedOn" needs an x:Key tag, you could instead copy the theme's implicit DataGridColumnHeader style and only modify the parts you are interested in. (I don't know if there's a more elegant way.)
If this is not what you meant, then please provide more sample code.
EDIT [2010-12-09]:
I looked at the code, and I believe the root of the problem is the absence of style inheritance. Your "DataGridHeaderStyle" is saying "Do not use the normal DataGridColumnHeader style, but instead use this TextBlock inside this Grid." So Silverlight does just that: it gives you a styled TextBlock in an unstyled default DataGridColumnHeader.
Proof: Update your "Home.xaml" and add a TextBox in the second column of your "DataGridHeaderStyle" style, next to the current TextBlock. Notice how the theme for the TextBox in the header is changing every time you change the theme (look at the TextBox backgroud), but the background of the column header is stuck on the default colour. Like I said, your custom style is telling Silverlight to ignore the implicit style.
FIX: I do not know how to inherit from implicit styles without an "x:Key" attribute. I googled quite a bit but could not find anything helpful. You will either have to a) create a custom column definition style for all your datagrid headers, or you will have to b) bother the Silverlight team for a new feature in the next version of Silverlight. Or c) pick one theme to stick with and edit a copy of the theme's implicit column header style as your new "DataGridHeaderStyle" style.
My holiday is about to start, so I hope this helped. At least now you know where the problem lies.

WPF Dynamic GUI elements

In WinForms it was relatively easy to swap out Panels at runtime for other panels. In WPF this seems to be rather more complex (especially from XAML).
Can anyone provide clear guidance on the 'best practice' way of swapping gui elements at runtime (think pages in a wizard type situation).
Many thanks.
This can be approached in XAML using datatemplates and/or triggers. For example, if each page in your wizard were represented in an underlying model as a separate class or object, you could use one of the following two options... Both use a ContentControl, which is the perfect control for when the content will vary greatly between different views of the same data.
Please note that the bindings are intended as pseudocode examples, just to convey intent!
DataTemplate-based, using different classes for each page:
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type WizardPageOne}">
<!-- page 1 layout here -->
</DataTemplate>
<DataTemplate DataType="{x:Type WizardPageTwo}">
<!-- page 2 layout here -->
</DataTemplate>
<!-- ... etc -->
</Grid.Resources>
<ContentControl Content="{Binding CurrentPageModel, Source=Wizardmodel}" />
</Grid>
Or Trigger based, using a property that indicates the current page:
<ContentControl Content="{Binding WizardModel}">
<ContentControl.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentPageIndex} Value="1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- page 1 layout here -->
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CurrentPageIndex} Value="2">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- page 2 layout here -->
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<!-- .... etc -->
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Both options will only load the control for each page as it's required, so you don't have all of the controls "loaded but hidden" in the window.
The underlying concepts of WinFomrs and WPF is different. In WPF it is not advisable to play around with UIElements(Controls) directly. Make use of DataBinding/DataContexts and just operate on the data and then the UI will function accordingly. This concept is all about WPF MVVM pattern. You can look in to some MVVM samples and try it before doing more complex WPF projects.
A simple example, Suppose you need to dynamically disply a number of items in a ListBox, The typical winform way to do this is to create Items and add directly to the ListBox. But in WPF you create an ObservableCollection<Customer> and bind that to the ListBox.ItemsSource. then define a DataTemplate for Customer Data Type, this ensure the WPF system to understand how a Collection of Customers being displayed in the application. So when you add a new customer instance to the collection, magically your ListBox will get updated with one more item. Seems pretty straight forward and a very loosely coupled way of Data and View right?.
Best wishes on your WPF learning. -
http://www.bing.com/search?q=WPF+MVVM
So the high level clue to your question is, make the View appropriately for the Data and when Data/Property Change happens, WPF will take care of changing the Panels/Controls. So it is really simple than WinForms way when you approach from the Data and View perceptive.
A couple options come to mind. If you create your components as UserControls, and make use of Data Binding, then you should be able to do what you need with minimal fuss.
Option one is to load each component into your parent container (grid, canvas, whatever) with Visibility="Collapsed", and then show and hide them as needed. This has the advantage that you can do this declaratively in XAML.
The other option is to load the components as you need them, so in the event handler of a button, or some other UI element. In this case you would probably want to remove the current displaying item from the Children collection of your host component, and then instantiate your next control, set the DataContext (this is why binding is important), and add it to the Children collection.
(disclaimer: this is based on my experience doing basically what you are asking in Silverlight 3.0, so there may be some WPF quirks I am unaware of).
The MVVM suggestions here are all good. But if you're designing a page-oriented UI that needs to be navigable, you can use Structured Navigation, too.
I got no idea if this is considered good practice, but what we did on one of our project is quite simple. We defined panels that were all on top of each other and would simply set the visibility to either hidden or visible when it was needed.

Using XAML vector graphics in WPF application

I have a vector image that I've defined in XAML. What is the proper way to use this resource in a WPF application?
I want to have the vector image in its own XAML file, and then add the image to other UserControls in my application. What should be the top-level element in my XAML vector image? How do I refer to that image in other UserControls?
http://learnwpf.com/post/2006/06/04/How-do-I-Include-Vector-Based-Image-Resources-in-my-WPF-Application.aspx explains how to do it.
<ContentControl Template="{StaticResource Credit-Card}" />
It is extremely difficult to use vector graphics in a reusable way in WPF and Silverlight.
These two StackOverflow questions discuss some of the options available:
XAML Icons - How to use?
WPF What is the correct way of using SVG files as icons in WPF
After reading through these questions and answers, I think the best solution is to stick with with a bitmap/raster format like PNG until Microsoft decides to support SVG.
Here's how to do it in a reusable style-able way:
https://github.com/alansingfield/VectorIcon/blob/master/README.md
<Style x:Key="CarIcon"
TargetType="local:VectorIcon">
<Style.Setters>
<Setter Property="Geometry">
<Setter.Value>
<PathGeometry Figures="M18,18H6V6H18M18,4H6A2,2 0 0,0 4,6V18A2,2 0 0,0 6,20H18A2,2 0 0,0 20,18V6C20,4.89 19.1,4 18,4Z" />
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
<local:VectorIcon Style="{StaticResource CarIcon}" Foreground="Green"/>

Resources