|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
ContentsIntroductionOn my last WPF article MyFriends (VS2008 comp winner dont you know) I used some 3D WPF stuff, and I have to say I quite liked it. Dont get me wrong I find it hard, but it looks very cool. Anyway since my last WPF article I have been on a break to New York where I met up with Josh Smith ( read about it here if you are interested), and have been awarded a codeproject MVP and a Microsoft C# MVP. Which is truly excellent news I am well thrilled by both these awards...but I think one is expected to carry on doing good work in order to keep these awards. So I got over my holiday slackness put the old WPF gloves on again, had a think and came up with the idea of being able to browse Amazon and represent the results of a search query in a 3d space. Where each 3d item would represent one search item result. These search items would be clickable to allow a deeper view of the related Amazon data to be examined. I also thought this idea had a fair ammount of scope. What Is In This ArticleAs I say I will be using the Amazon Web Services (AWS) to gather results that match a query string, and it will be a WPF based solution and really thats about it. Simple Eh However in order to do this, I wanted to use a 3D type view (cos it looks nice), so by the time you read to the end of this article I would hope that you may know about some (or all) of the following :
The Demo AppThe following sub sections will go through what I consider to be the important parts of the demo application Whats it do / what does it look likeAs this application is fairly 3d based, I thought it may be better to show a small video of what it looks like running. To this end, you may click the video below to see what it all looks like in action.
Amazon Web ServicesSome time ago I saw a link on the coding4fun web site about using Amazon Web Services (AWS) with C#, and I thought it was quite neat. Since then I have been messing around with WPF and some WCF stuff. I am a big fan of Amazon the shop, so I thought why not try and use the web service to create a nice WPF app. So thats exactly what I did, and this article is it. The first step in using the AWS is to actually register at Amazons site Amazon Web Services (AWS), this allows you to obtaina key to be able to use the web services. But dont worry I've already registered and obtained a key, which I WILL be keeping in the attached demo application, so you don't have to register with Amazon. But please dont abuse my key. Anyway once you have obtained a AWS key, you are ready to try and use the AWS code in your own applications. With the release of WCF and also VS2008 things changed slightly when it comes to services and how they can be used. Not much, but enough that I feel I should write a little bit about how to configure the AWS to be used in your own applications There are several options/tools available to developers using WCF or web services with VS2008. VS2005 is the same as it always was, simply add a web reference. But as I now use VS2008, ill be focusing on that. //make sure we have the correct using statment for the service
using AmazonService;
....
....
private Details[] doAmazonSearch(string keyword, string mode)
{
try
{
KeywordRequest keywordReq = new KeywordRequest();
keywordReq.locale = "us";
keywordReq.type = "lite";
keywordReq.sort = "reviewrank";
keywordReq.mode = mode;
keywordReq.keyword = keyword;
keywordReq.tag = this.SubscriberID;
keywordReq.devtag = this.SubscriberID;
AmazonSearchPortClient ams = new AmazonSearchPortClient();
ProductInfo productInfo = ams.KeywordSearchRequest(keywordReq);
return productInfo.Details;
}
catch { return null; }
}
But wait isnt there more to this than meets the eye. Well actually there is a fair bit more to this than meets the eye, there are 2 vital bits of code / configuration that allow us to simply point and click to reference and start using the AWS. If you didnt know better you may actually not know or even care about these details. Luckily I am a chap that both wants to know stuff, and does care about the details. So let me tell you some more about these 2 extra details.
They are in fact
So what I'm going to do now is talk a little bit about each of these items, and how they are created, and what your options are for creating these items Service ConfigurationIn order for the application to communicate with the AWS we need to add some entries to the App.Config file. The configuration section that we will need to add is to do with Binding and Endpoint. There are several options available in the creation of theses sections in the App.Config file. Theses options vary in compexity. So ill start with the easiest Option 1 : Use VS2008 <?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="AmazonSearchBinding" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None"
realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://soap.amazon.com/onca/soap3" binding="basicHttpBinding"
bindingConfiguration="AmazonSearchBinding" contract="AmazonService.AmazonSearchPort"
name="AmazonSearchPort" />
</client>
</system.serviceModel>
</configuration>
This is cool, VS2008 created this for us. Nice of it huh. But we do have other options Option 2 : Use SVCConfig Editor Option 3 : Use svcutil.exe The last option is to use the command line tool svcutil.exe, which is capable of creating an entire App.Config and a proxy class (discussed below) from a single command line. The command line to use is as follows To do this with svcutil.exe we can simply use the following command line svcutil.exe http://soap.amazon.com/schemas3/AmazonWebServices.wsdl /language:c#
Proxy codeIn order for us to communicate with AWS (or any other web service) we need to have some proxy code, that knows how to serialize the data and calls. To this end any call made in C#|VB .NET will always have to call this proxy object. The proxy simply takes our calls and knows how to call the actual service and get the correct return types etc etc. If we look at a small section of the generated proxy class, say for the [System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
public partial class AmazonSearchPortClient :
System.ServiceModel.ClientBase<AmazonService.AmazonSearchPor>,
AmazonService.AmazonSearchPort {
public AmazonSearchPortClient() {
}
public AmazonSearchPortClient(string endpointConfigurationName) :
base(endpointConfigurationName) {
}
public AmazonSearchPortClient(string endpointConfigurationName,
string remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public AmazonSearchPortClient(string endpointConfigurationName,
System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public AmazonSearchPortClient(System.ServiceModel.Channels.Binding binding,
System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress) {
}
public AmazonService.ProductInfo KeywordSearchRequest(
AmazonService.KeywordRequest KeywordSearchRequest1) {
return base.Channel.KeywordSearchRequest(KeywordSearchRequest1);
}
public AmazonService.ProductInfo TextStreamSearchRequest(
AmazonService.TextStreamRequest TextStreamSearchRequest1) {
return base.Channel.TextStreamSearchRequest(TextStreamSearchRequest1);
}
.....
.....
.....
But just how do we get one of these here proxy objects? Well if you are using VS2008, as soon as you have added the Service Reference (as discussed above) you will (if you look for it) find that you actually have a proxy class in your <your project>Service References\<your service ref name>\ folder. The screen shot below shows the proxy for the attached project. VS2008 created this automatically
But what if we dont have VS2008, what can we do. As before, svcutil.exe, is a valueable tool in the creation of proxy classes as it was for configuration files.
svcutil.exe http://soap.amazon.com/schemas3/AmazonWebServices.wsdl /language:c#
This will result in 2 items being created. Proxy code, and an example App.Config (as discussed earlier), so in essence svcutil.exe is doing the same as VS2008, with the exception that it is not a nice GUI type interface but rather a command line tool. Ill leave the debate of what tool is best up to you. I just wanted to make you aware that you could do the same without having VS2008. If you are interested in how svcutil.exe can be used with WCF you may also like to read my other WPF/WCF chat article Distrust Of EverythingAs I am using a 3d party web service (AWS remember), I am a little catious. Basically what I am saying is, that, if I make a call to the Amazon Web Service do I really know how and when it will yield a result to me? I came up with an answer of NO to this question. To this end I had a think about the problem and formalised it into this requirement "I would like to be able to call the AWS asynchonously, and if after some pre-defined time had elapsed, I got a valid result and made use of the AWS result, or I alerted the user to the apparent time out". I felt that pretty much covers what I am trying to do, its a failry simple requirement, so lets see how that translates into code, shall we //delegate that will be called asynchronously to fetch Amazon Details
internal Details[] FetchAmazonDetailDelegate(string searchword, string category);
...
...
//Method called by aysychronous delegate to fetch Amazon Details
private Details[] FetchAmazonDetail(string searchword, string category)
{
return doAmazonSearch(searchword, category);
}
...
...
try
{
//Fetch the details asynchronously. Basically assume we will not get results quickly
FetchAmazonDetailDelegate fetchDetails = FetchAmazonDetail;
IAsyncResult asynchResult = fetchDetails.BeginInvoke(searchword, category,null, null);
//wait for the results from the asynch call.
while (!asynchResult.AsyncWaitHandle.WaitOne(5000, false))
{
//waiting for result for exactly 5 seconds
}
//get the results of the asynch call
details = fetchDetails.EndInvoke(asynchResult);
if (details != null)
{
//use the Amazon Details gathered
}
}
//As its more than likely the web service, not much I can do about it,
//just catch the Exception
catch
{
MessageBox.Show("Error obtaining amazon results");
}
I think the code is fairly self explanatory, its basically an asynch call, that uses a WaitHandle to wait for 5 seconds, before finishing the asynch call. However this all needs to be wrapped in a try-catch as we may get an Exception, or possible then EndInvoke may cause something strange to occur 3DOne of the main things I wanted to do with this app, wa make it funky. To this end I think the attached demo app has done a fair job. Basically when a new Amazon search is entered, a number of 3d Meshes are created which are positioned randomly in 3D space, are created. Then for each one of the meshes the Anyway the important code of note is as follows private void createViewPortCamera()
{
cam = new PerspectiveCamera();
cam.Position = new Point3D(0,0,10);
cam.FarPlaneDistance = 600;
cam.NearPlaneDistance = 0.1;
cam.FieldOfView = 90;
cam.LookDirection = new Vector3D(0,0,-1);
view3D.Camera = cam;
}
private void createViewPortLight()
{
model = new ModelVisual3D();
model.Content = new AmbientLight();
view3D.Children.Add(model);
}
....
....
p3s = new Point3D[details.Length];
//for each Detail obtained for current search, create a new 3d Mesh and store
//its Point3D. To allow camera to be animated to later
for (int i = 0; i < details.Length; i++)
{
MeshGeometry3D plMesh = this.TryFindResource("planeMesh") as MeshGeometry3D;
InteractiveVisual3D mv = new InteractiveVisual3D();
mv.IsBackVisible = true;
mv.Geometry = plMesh;
mv.Visual = createAmazonDetail(details[i]);
view3D.Children.Add(mv);
//position item randomly in 3D space, but always ensure Z is (-)
Matrix3D trix = new Matrix3D();
double x = ran.NextDouble() * 50 - 50;
double y = ran.NextDouble() * 2 - 2;
double z = -i * 10;
p3s[i] = new Point3D(x, y, z);
trix.Append(new TranslateTransform3D(x, y, z).Value);
mv.Transform = new MatrixTransform3D(trix);
}
//create animation, and bring item 1 into view
pa = new Point3DAnimation(p3s[0], TimeSpan.FromMilliseconds(300));
pa.AccelerationRatio = 0.3;
pa.DecelerationRatio = 0.3;
pa.Completed += new EventHandler(pa_Completed);
cam.BeginAnimation(PerspectiveCamera.PositionProperty, pa);
fetching = false;
dt.Tick += new EventHandler(dt_Tick);
....
....
private void dt_Tick(object sender, EventArgs e)
{
dt.Stop();
if (count == detailsCount-1) count = 0;
else count++;
pa = new Point3DAnimation(new Point3D(p3s[count].X,
p3s[count].Y + 0.5, p3s[count].Z + 2), TimeSpan.FromMilliseconds(500));
pa.AccelerationRatio = 0.3;
pa.DecelerationRatio = 0.3;
pa.Completed += new EventHandler(pa_Completed);
cam.BeginAnimation(PerspectiveCamera.PositionProperty, pa);
}
private void pa_Completed(object sender, EventArgs e)
{
//need to try and catch as user may some times change the search 1/2 way
//through an animation and the current animation has not yet completed.
try
{
pa = new Point3DAnimation(new Point3D(p3s[count].X,
p3s[count].Y + 0.5, p3s[count].Z + 1.6), TimeSpan.FromMilliseconds(3100));
pa.Completed += new EventHandler(dt_Tick);
cam.BeginAnimation(PerspectiveCamera.PositionProperty, pa);
}
catch { }
}
3DToolsAs I stated way back at the start of this article, I had seen in the past a Amazon search app written in C#. But it was WinForms, and this is WPF. So I thought why not go to town. To this end I am not only using the 3D just described, but I am also allowing the following for the 3D viewport
TrackballThe trackball is simply great, you just wrap it around your <!-- 3D Viewport, wrapped up in some of the nice 3DTools classes
to allow 2D UI elements on 3D and allow trackball functions
in 3D space -->
<inter3D:TrackballDecorator x:Name="inter3d" DockPanel.Dock="Bottom" Height="Auto">
<inter3D:Interactive3DDecorator>
<Viewport3D x:Name="view3D"/>
</inter3D:Interactive3DDecorator>
</inter3D:TrackballDecorator>
By using these couple of lines, you are able to tilt and zoom the 3d view port. The left mouse button does the tilt/while the right mouse button does the zoom. I've put a screen shot here, but this really doesn't do it justice, I would look at the video in the Whats it do / what does it look like section for a better demonstration
2D object interaction of 3D surfaceThe abiility to place a 2D control on a 3D surface is quite compelling to me. This means that I can animate a camera (which is what I do) through a 3D viewport of 3D meshes, where each of the meshes contains a standard 2D MeshGeometry3D plMesh = this.TryFindResource("planeMesh") as MeshGeometry3D;
InteractiveVisual3D mv = new InteractiveVisual3D();
mv.IsBackVisible = true;
mv.Geometry = plMesh;
mv.Visual = createAmazonDetail(details[i]);
view3D.Children.Add(mv);
....
....
....
private StackPanel createAmazonDetail(Details amazonDetail)
{
StackPanel sp = new StackPanel();
sp.Background = Brushes.Transparent;
AmazonItem item = new AmazonItem(amazonDetail);
item.ItemClicked += new AmazonItem.AmazonItemClickedEventHandler(item_ItemClicked);
sp.Children.Add(item);
return sp;
}
Simply huh, we now have a 2d interactive 2D By using these couple of lines, you are able to tilt and zoom the 3d view port. The left mouse button does the tilt/while the right mouse button does the zoom. I've put a screen shot here, but this really doesn't do it justice, I would look at the video in the Whats it do / what does it look like for a better demonstration LINQ and ReflectionAs I have stated throughout this article, each search made to the AWS proxy yields a search result, namely in the form of an array of So I had a think, of course I could some LINQ, couldnt I. LINQ is for querying inline collections. An array of So I had another think about this...Reflection to the rescue. I could simply using LINQ/Reflection together to query only those properties on the declaring Heres the code snippet Details det = AmazonDetail;
Type amazonType = det.GetType();
//get all public and instance fields only
PropertyInfo[] props = amazonType.GetProperties(
BindingFlags.Public | BindingFlags.Instance);
//now use some LINQ with some added reflection for good measure
//to obtain the fields from the Amazon details that arent null
var nonNullProps = ( from prop in props where
prop.GetValue(det, null) != null &&
!prop.GetValue(det, null).ToString().EndsWith("[]")
select new AmazonParameterDetail
{
PropertyName = prop.Name,
PropertyValue = prop.GetValue(det, null).ToString()
}
);
....
....
//Now I can use a loop, safe in the knowledge I only have those properties for the current
//Amazon Detail that aren't null or empty
foreach (AmazonParameterDetail nonNullprop in nonNullProps)
{
....
....
}
FlowDocument
When you use FlowDocuments, there are several container WPF container controls which you may host a FlowDocument in. These WPF container controls vary in what they provide. Lets see the difference shall we
For those of who have not come across the FlowDocument, here is a list of some of the things that can be done with it
PARAGRAPH In XAML <Paragraph FontSize="11">
This page is a simple FlowDocument that is part of the Windows.Document namespace, and it
has been included in this application, simply to show how easy it is to create simple Documents
which have Paragrpahs/Links/Images and can be scaled up/down using the FlowDocumentReader control.
This only really touches the surface of what you can do with FlowDocument(s) in WPF, you can also
use all sort of text effects, like subscript/superscript/underline. You can also use tables. In fact
with FlowDocument(s) you can acheive some pretty slick looking documents. At least this should give you a
a flavour of what can be done. I hope.
</Paragraph>
In C# code behind Paragraph paraHeader = new Paragraph();
paraHeader.FontSize = 12;
paraHeader.Foreground = headerBrsh;
paraHeader.FontWeight = FontWeights.Bold;
paraHeader.Inlines.Add(new Run("Paragraph Text"));
flowDoc.Blocks.Add(paraHeader);
HYPERLINKS
In XAML <Paragraph FontSize="11">
<Hyperlink Click="hl_Click" NavigateUri="www.google.com">Click Here</Hyperlink>
</Paragraph>
In C# code behind Paragraph paraValue = new Paragraph();
Hyperlink hl =new Hyperlink(new Run("click here"));
hl.Click += new RoutedEventHandler(hl_Click);
paraValue.Inlines.Add(hl);
flowDoc.Blocks.Add(paraValue);
EMBEDDING UI ELEMENTS
In XAML <BlockUIContainer>
<Button Width="60" Height="60" Click="Button_Click">
Click me
</Button>
</BlockUIContainer>
In C# code behind
BlockUIContainer uiCont = new BlockUIContainer();
Button b = new Button();
b.Width = b.Height = 60;
b.Click += new RoutedEventHandler(Button_Click);
b.Content ="Click me"
uiCont.Child = b;
flowDoc.Blocks.Add(uiCont);
Within the demo application I have constructed a
Of course this is only touching the surface of what can be done with FlowDocuments. But it gives you an idea of how flexable the formatting of documents is with WPF. FavouritesFrom the
The favourite items are shown in a scrollable
It can be seen that the Styles / TemplatesI have only really used Styles / Templates in a couple of areas in this application. The following outlines these areas
I know its probably a lot of code but i'll list all the Favourites Area ScrollViewer Control StyleThe entire Favourites The result of applying the
Favourites Items Control DataTemplateLook as follows <!-- This is where the look and feel of the items is defined for the
icFavourites ItemsControl -->
<DataTemplate x:Key="favItemsTemplate">
<Button Content="{Binding Price}" VerticalAlignment="Top" HorizontalAlignment="Left"
Padding="3" Width="130" Height="30" FontFamily="Arial Rounded MT"
FontSize="12" FontWeight="Normal" Foreground="#FFEF3800"
Template="{StaticResource OrangeGelButton}"
Margin="5,5,5,5"
Click="btnFavMain_Click">
<Button.ToolTip>
<Border Background="White" CornerRadius="5,5,5,5" Width="200">
<DockPanel Width="Auto" Height="Auto" LastChildFill="True">
<Label Margin="2,2,2,2" VerticalAlignment="Top" Width="Auto" Height="Auto"
Content="Amazon Favourite" Background="#FF000000"
FontFamily="Arial Rounded MT"
FontSize="14" Foreground="#FFFFFFFF"
DockPanel.Dock="Top"/>
<TextBlock Margin="2,2,2,2" Width="Auto" Height="Auto"
TextWrapping="Wrap">
<Run Language="en-gb">You have saved this Amazon
item as a favourite. You can click it to re open it,
or click on the close button to delete it from the
favourites list</Run>
</TextBlock>
</DockPanel>
</Border>
</Button.ToolTip>
</Button>
</DataTemplate>
And the results of this are as shown below (Note that this
Favourites Item OrangeGelButton ControlTemplateLook as follows <!-- Gel Button Template For Amazon Favourite -->
<ControlTemplate x:Key="OrangeGelButton" TargetType="Button">
<Grid Background="#00FFFFFF">
<Border BorderBrush="#FF000000" CornerRadius="6,6,6,6"
BorderThickness="1,1,0,0" Opacity="0.9">
<Border.BitmapEffect>
<BlurBitmapEffect Radius="1" />
</Border.BitmapEffect>
</Border>
<Border BorderBrush="#FFFFFFFF" CornerRadius="6,6,6,6"
BorderThickness="0,0,0.6,0.6" Opacity="0.7" />
<Border Margin="1,1,1,1" CornerRadius="6,6,6,6" Name="background">
<Border.Background>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0" Color="#FFFBD19E" />
<GradientStop Offset="1" Color="#FFF68F15" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Border.Background>
<Grid Margin="1,1,1,1" ClipToBounds="True">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle Width="{TemplateBinding FrameworkElement.Width}"
Fill="#FFFFFFFF" Opacity="0.34" Grid.Row="0" />
</Grid>
</Border>
<Border Margin="1,1,1,1" BorderBrush="#FFFFFFFF" CornerRadius="6,6,6,6"
BorderThickness="0,0,0,0" Opacity="0.3">
<Border.BitmapEffect>
<BlurBitmapEffect Radius="1" />
</Border.BitmapEffect>
</Border>
<Border Margin="1,1,1,1" BorderBrush="#FF000000" CornerRadius="6,6,6,6"
BorderThickness="0,0,0.6,0.6" Opacity="1">
<Border.BitmapEffect>
<BlurBitmapEffect Radius="1" />
</Border.BitmapEffect>
</Border>
<Image Source="resources/Amazon.png" Width="60" Height="11" Stretch="Fill"
HorizontalAlignment="Right" VerticalAlignment="Top" Margin="5,5,50,5"/>
<ContentPresenter Margin="5,13,5,5" HorizontalAlignment="Left"
VerticalAlignment="Top" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
Content="{TemplateBinding ContentControl.Content}" />
<Button x:Name="btnSub" Click="btnDeleteFavourite_Click" HorizontalAlignment="Right"
Content="X" Margin="0,0,5,0" Width="20" Height="20" FontFamily="Arial Rounded MT"
FontSize="11" FontWeight="Normal" Template="{StaticResource CloseButton}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<Storyboard.Children>
<ColorAnimation To="#FFFBD19E" FillBehavior="HoldEnd"
Duration="00:00:00.4000000" Storyboard.TargetName="background"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops).[0].(GradientStop.Color)" />
<ColorAnimation To="#FFF68F15" FillBehavior="HoldEnd"
Duration="00:00:00.4000000" Storyboard.TargetName="background"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops).[1].(GradientStop.Color)" />
</Storyboard.Children>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<Storyboard.Children>
<ColorAnimation To="#FFFAF688" FillBehavior="HoldEnd"
Duration="00:00:00.2000000" Storyboard.TargetName="background"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops).[0].(GradientStop.Color)" />
<ColorAnimation To="#FFF6D415" FillBehavior="HoldEnd"
Duration="00:00:00.2000000" Storyboard.TargetName="background"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops).[1].(GradientStop.Color)" />
</Storyboard.Children>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
<Trigger Property="ButtonBase.IsPressed" Value="True">
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<Storyboard.Children>
<ColorAnimation To="#FFFAF688" FillBehavior="Stop"
Duration="00:00:00.4000000" Storyboard.TargetName="background"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops).[0].(GradientStop.Color)" />
<ColorAnimation To="#FFF6D415" FillBehavior="Stop"
Duration="00:00:00.4000000" Storyboard.TargetName="background"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops).[1].(GradientStop.Color)" />
</Storyboard.Children>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<Storyboard.Children>
<ColorAnimation To="#FFFAA182" FillBehavior="HoldEnd"
Duration="00:00:00.2000000" Storyboard.TargetName="background"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops).[0].(GradientStop.Color)" />
<ColorAnimation To="#FFFD6420" FillBehavior="HoldEnd"
Duration="00:00:00.2000000" Storyboard.TargetName="background"
Storyboard.TargetProperty="(Panel.Background).
(GradientBrush.GradientStops).[1].(GradientStop.Color)" />
</Storyboard.Children>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
And the results of this are as shown below (Note that this
Favourites Item CloseButton ControlTemplateLook as follows <!-- Close Button Used As Part Of Gel Button Template For Amazon Favourite -->
<ControlTemplate x:Key="CloseButton" TargetType="Button">
<Border Opacity="0.5" Name="bord" Margin="0"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
BorderBrush="#FF000000"
BorderThickness="2,2,2,2" CornerRadius="5,5,5,5"
Padding="0,0,0,0"
Background="#FFFFFFFF">
<ContentPresenter Margin="{TemplateBinding Control.Padding}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
Content="{TemplateBinding ContentControl.Content}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="bord" Property="Background" Value="#FFFC0C0C"/>
<Setter TargetName="bord" Property="Opacity" Value="1.0"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
And the results of this are as shown below
Known IssuesOccassionally crashes with Null Reference, but there is no Exception raised in VS2008, and the InnerException is null, there is no message at all to indicate what/where or how the error is being raised. As such I can't track it down. So if anyone finds wher the blighter is, please let me know and ill fix the code. Historyv1.1 27/02/08 : Minor coding change in Styles | ||||||||||||||||||||