Ian 的个人资料Silverlight for Business照片日志列表更多 ![]() | 帮助 |
|
5月16日 Silverlight for Business is movingBecause of the ongoing limitations of hosting this blog on spaces, I am moving over to WordPress.
There is nothing for you to do for browsing this site, all existing links will still work, and once the DNS changes have propagated you will be taken to the new blog automatically when you visit http://silverlightforbusiness.net
However you will need to update your links for your favourite reader to http://silverlightforbusiness.net/feed
Cheers
Ian 4月7日 Silverlight for Mobile – further thoughts
I thought a lot of the comments he made were spot on. We have been lead up the garden path by the mobile team and in particular Amrit Chopra who has been announcing Silverlight for Mobile for 3 years now. In fact I was expecting that he would announce it again at Mix year and then deliver nothing again! Turns out he took a back seat in the 6.5 overview instead and no significant news on Silverlight Mobile was forthcoming. In my opinion though, Microsoft are playing with smoke and mirrors because they want to make a big as splash as possible when Windows Mobile 7 arrives. So what will be in the next version of Windows Mobile?Note: By saying “next version” I am adopting the same language that Microsoft seem to be using – could be 7, could be 6.5, could be 7 in 6.5 timeframe…but I am referring to the next significant release.
Add all that up and it is looking pretty exciting from a user and a developer story. Let’s hope it is sooner rather than later. Cheers Ian Technorati Tags: Windows Mobile 7,Silverlight 3月27日 Silverlight Pulse Grok PresentationUnfortunately the Silverlight UK User Group meeting was cancelled last night, because Mark Mann injured his ankle – I understand he is recuperating though (http://twitter.com/mark_mann) – hope it gets better soon Mark :-) I was due to do a Grok on SilverlightPulse.net and my presentation is below. Mark has also asked me to do a more in-depth talk on this and it’s current migration to Silverlight 3 beta in a forthcoming meeting, which I am looking forward to. Stay tuned (as they say) Silverlight pulse grok View more presentations from IanBlackburn. Cheers Ian Technorati Tags: silverlight,Silverlightpulse.net 3月17日 Updated Silverlight Pulse now on http://silverlightpulse.netI have updated the app I created for keeping track of Silverlight tweets and now hosted it on http://silverlightpulse.net (DNS changes have just been made so may to a few hours to propagate to you). So far I have had an overwhelming positive response, so thanks everyone who has taken a look. Changes include:
I have plenty of other ideas for this app, so stay tuned, but even in it’s current form I think it will be interesting to see how the Twitter world lights up (or not) for the SL announcements at Mix 09 this week. Cheers Ian Silverlight Pulse – a twitter silverlight appJust a quick note to point you towards a little app I have been playing with to keep track of Silverlight news on Twitter. Thought it might be interesting to keep an eye on this week with all the Silverlight announcements. This is a very early attempt at some ideas I have for a richer Silverlight Twitter app – I will blog about it more later, but for now, you can have a play below or by opening this link:
Cheers Ian Technorati Tags: Silverlight,Twitter 3月9日 Brad Adams talks about “Silverlight 3 for Great Business Apps”Robert Hess has a interesting chat with Brad Adams about silverlight 3 – this is a sneak peak of Brad’s session for Mix09, without, as he calls “the super secret stuff”. Highlights:
Cheers Ian 3月2日 Silverlight M-V-VM ResourcesThe Model-View-ViewModel pattern has been gaining a lot of popularity for WPF, and there have been a growing number of posts and resources about adapting it for Silverlight. This is a short summary post of some of those resources. I will follow this up later with a detailed post on M-V-VM where I will also explore the business case for adopting such a design (design patterns are great but I am wary about adopting them for the sake of it like this)
If you want to pick one to get started with I’d recommend the Tim Heuer’s screencast: Cheers
Ian Technorati Tags: Silverlight,M-V-VM 2月24日 Windows Live Spaces: Top 5 Wish ListWindows Live wave 3 brought great improvements to live.com, and was part of my motivation for hosting this blog on Spaces (which has only been updated slightly). But I am starting to regret it. Here’s my top 5 reasons why:
I am hoping that there is an update for Spaces on the way – but I haven’t heard anything about that. At the moment there is a possibility that the current setup is going to harm potential business by reducing it’s visibility, and that is obviously not acceptable. So if anyone has any pointers or hints on solutions to the above, I would love to hear them! Cheers Ian
Silverlight Visualisation Example – how much energy is wasted in US households?This example was produced rather quickly in response to a challenge by Tim Heuer and shows the energy consumption of US households over the last few years and then predicted to 2030. I was inspired by an article in National Geographic that I was reading, and it highlights I hope, the rather dramatic amount of energy is wasted through “electrical loss” (though I was a little disappointed from the visualisation point of view, how little the data changed in the prediction, made for a less dramatic animation!). This is US data, but I would expect the UK would be very similar.
The code is very simple really, and illustrates how easy it is to do this sort of thing in Silverlight. It could easily be improved with some nicer animation and design though. The following are illustrated in the code:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:EnergyConsumptionControls" > <Style TargetType="controls:EnergyConsumptionBubble"> <Setter Property="Template" > <Setter.Value> <ControlTemplate TargetType="controls:EnergyConsumptionBubble"> <Grid Margin="10 0" > <Ellipse Fill="{TemplateBinding Fill}" Width="{TemplateBinding EnergyValue}" StrokeThickness="{TemplateBinding StrokeThickness}" Stroke="{TemplateBinding Stroke}" Height="{TemplateBinding EnergyValue}" /> <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center"> <TextBlock TextAlignment="Center" FontWeight="Bold" Foreground="{TemplateBinding TextForeground}" TextWrapping="NoWrap" Text="{TemplateBinding EnergyText}" ></TextBlock> <Image Source="{TemplateBinding ImageSource}" Stretch="UniformToFill"/> </StackPanel> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary> it is used in the page like this:
<controls:EnergyConsumptionBubble Stroke="Red" TextForeground="Black" StrokeThickness="10" Fill="Red" EnergyValue="{Binding Converter={StaticResource EnergyValueConverter}, Path=Electricity}" EnergyText="Electricity" Canvas.Top="0" Canvas.Left="-21"/> WebClient client = new WebClient(); client.OpenReadAsync(new Uri("EnergyConsumption.xml", UriKind.Relative)); client.OpenReadCompleted += (o, args) => { XElement xelement = XElement.Load(args.Result); var results = from c in xelement.Elements("EnergyConsumption") select new EnergyConsumption() { Electricity = double.Parse(c.Element("Electricity").Value), ElectricityRelatedLosses = double.Parse(c.Element("Electricity_Related_Losses").Value), LiquidFuels = double.Parse(c.Element("Liquid_Fuels").Value), NaturalGas = double.Parse(c.Element("Natural_Gas").Value), RenewableEnergy = double.Parse(c.Element("Renewable_Energy").Value), Year = int.Parse(c.Element("Year").Value) }; DataGrid1.ItemsSource = results; foreach (EnergyConsumption item in results) { records.Add(item); } this.DataContext = records.First(); YearSlider.SmallChange = 1; YearSlider.LargeChange = 5; dataLoaded = true; };
private void YearSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { if (dataLoaded) { this.DataContext = records.Where(c => c.Year == Math.Round(YearSlider.Value)).First(); } }
That’s pretty much it – full source is available on my sky drive – feel free to download it and have a play. Cheers
Ian 2月7日 Implementing Validation Logic in Silverlight 2In Silverlight 3 we have seen that the Alexandria Framework will have a rich validation and business logic framework that is really very powerful and productive. Silverlight 3 is due to be announced at Mix 09, and I would expect a CTP or Beta to be available then. But what can you do now? Even if we get a go-live license on a SL 3 Beta it will be too early for many corporate teams to adopt. This post will look at using Silverlight 2 and:
Replicating validation logic from an Entity Model on the Server exposed via an Ado.Net Data ServiceIf you have not created Entity Models and Ado.Net Data Services you may want to refer to my earlier post here. Alexandria (a.k.a. SL3) provides an automatic way of doing this which is not available in SL2, however we can provide a reasonably simple manual process to achieve a similar result. The key is that the service reference you create will generate a client proxy partial class, with partial methods available in Silverlight that mimic the ones available in the model on the server. So if on the server I create the following extremely simple Entity Model I can add some validation logic by creating the following partial class on the server on then implementing the partial methods I want (in this case the OnUnitPriceChanging) public partial class Products { partial void OnUnitPriceChanging(decimal? value) { if (value < 0) { throw new ArgumentOutOfRangeException("UnitPrice","Must be positive"); } } }
I now add an Ado.Net Data Service as follows to “surface” that data: public class NorthwindDataService : DataService< NorthwindEntities > { // This method is called only once to initialize service-wide policies. public static void InitializeService(IDataServiceConfiguration config) { // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc. // Examples: config.SetEntitySetAccessRule("*", EntitySetRights.All); config.SetServiceOperationAccessRule("*", ServiceOperationRights.All); }
and add a Service reference to it from the Silverlight project. This will cause Visual Studio to generate two proxy classes for use with the service. If you want to see these you will need to turn on “Show All Files” in the project and then expand the Service Reference folder until you see Reference.cs Interestingly this also builds an entity data model (service.edmx) that cannot be viewed using the Entity Data Model Designer, though you could open it up in the xml editor. However we are not interested in that for this scenario. Rather we want to have a look at those generated proxy classes. From the class diagram below we can see that the Products entity has been replicated to the Silverlight project but the partial OnUnitPriceChanging method has not been implemented (i.e. our implementation has not been carried over from the server) However since this is a partial class it is easy for us to create own own partial Products class in the Silverlight project and then implement this method. In this case we can copy and paste the code directly from our server version of this class. Ideally of course, this would be generated for us automatically, and this is in fact what Alexandria in Silverlight 3 does, but in Silverlight 2 it is a manual process. Note that you will need to make sure your namespace is correct and matches the one used by the service reference generated class. namespace SilverlightValidation.DataServiceReference1 { public partial class Products { partial void OnUnitPriceChanging(decimal? value) { if (value < 0) { throw new ArgumentOutOfRangeException("UnitPrice","Must be positive"); } } } }
Ok so we now have data validation logic replicated on the client. How can we use it? Handling Validation Logic in the UII am going to build a very simple UI that shows how we can handle our validation logic. It has a button that retrieves a single product given a product ID and then displays the productname and unit price. Editing the UnitPrice should raise our validation check, and if it fails we want to indicate that in the UI by making something go red and displaying a message somewhere. Here is the Xaml: <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <TextBox x:Name="TextBox1" Text="1" Margin="5" /> <Button Content="Get Product" Margin="5" Click="Button_Click"/> <TextBlock Foreground="Red" x:Name="TextBlockError"/> </StackPanel> <StackPanel Grid.Row="1" > <TextBlock Text="{Binding ProductName}"/> <TextBox BindingValidationError="TextBox_BindingValidationError" > <TextBox.Text> <Binding Mode="TwoWay" Path="UnitPrice" ValidatesOnExceptions="True" NotifyOnValidationError="True" /> </TextBox.Text> </TextBox> </StackPanel> </Grid>
And here is the code behind: public partial class Page : UserControl { DataServiceReference1.NorthwindEntities entities =
Let’s have a look at this bit by bit. First the Button_Click code: this builds a DataServiceQuery and then executes it, setting the DataContext of our page to the first result (there should only ever be one since we are querying by primary key). There is no exception handling in here though, so an invalid product Id will just throw an exception; feel free to add your own. Setting the DataContext on the page to the resulting Product entity will cause the data to be shown in our little data form as follows:
In the Xaml I have set the following binding properties on the TextBox: <Binding Mode="TwoWay" Path="UnitPrice" ValidatesOnExceptions="True" NotifyOnValidationError="True" />
so having both the last two settings set to True means that any validation errors set in our Products entity can be handled in a single BindingValidationEvent. In this case I have handled this event on the textbox itself but you may handle it further up the tree (e.g. a stack panel or the page) in a more complicated data form. <TextBox BindingValidationError="TextBox_BindingValidationError" > This event handler is then simply changing the background colour of the textbox to red and displaying the error message. To test this you will need to:
Happily we also get data type validation for free using this model. So entering some text into this field (which should of course be a number) also raises the validation error:
Moving ForwardThis validation model works but we have to do more work than we would have to in WPF (see here and here). We can also reasonably expect Alexandria (SL3) to provide a richer model for validation – watch out from the sessions at Mix 09 for more details on that. cheers Ian 2月5日 Silverlight Accessibility and creating DDA Compliant applications in the UKIn the UK the DDA (The Disability Discrimination Act, 1995) makes it a legal requirement for service providers to ensure access for disabled customers and if necessary make reasonable adjustments to the way they deliver their services; this includes web sites (see Part III, 2.2, 4.7, 2.13-2.17, 5.23 and 5.26). This almost certainly isn’t news to most of you, but I think there is a large degree of confusion about what creating a DDA Compliant site actually means, if not among the legal profession, it certainly appears to be among the development teams I generally come across. It seems that in order to be able to claim that a website is DDA compliant, it must be tested against the three checkpoint levels laid down in the WAI guidelines issued by the World-Wide-Web Consortium (W3C). These are of course the commonly quoted A, AA and AAA (levels 1, 2 and 3). Level AA is recommended and level A is the minimum acceptable standard for accessibility. However I believe that most developers look at the A, AA, and AAA levels against WCAG (Web Content Accessibility Guidelines) version 1, which were published in 1999 and required sites that contain no CSS or Script.
Version 2 was published on 11 December 2008 and “applies to more advanced technologies, and is more precisely testable” than the previous version. For example, scripting is not forbidden and is even included as techniques to enhance accessibility. And where WCAG 1.0 essentially did not allow flashing or other movement, WCAG 2.0 allows it within defined parameters that won't cause seizures. So WCAG version 2 opens up the ability to build compliant applications using Silverlight 2, and I have it on good authority that “you can build a WCAG 2.0 acceptable experience using Silverlight”. So that means:
I will post further on what you will need to do in terms of your Silverlight app to achieve this, but for me at least this is a significant realisation, and I know of at least one client where not being able to do this would have been a show-stopper for Silverlight adoption in their new product. Perhaps too, with the more realistic WCAG v2 now published, we can start developing web applications in general that are useful, accessible, and legal without resorting to producing a “fall-back WCAG v1 compliant version” and that the majority of web sites that are currently in breach of the law, and have been for over 6 years, can come into line. Cheers
Ian 1月2日 Microsoft.com gets Silverlight on it’s home pageWe are starting to see Microsoft push Silverlight out on more of it’s assets. Microsoft.com home page (one of the most visited in the world) now shows the following to users without Silverlight installed And the following once it is installed; an accordion type control highlighting current content on the site (By the way Microsoft, SkyDrive now offers 25GB of storage for free, not 5GB as stated on this page, at least my account does. CORRECTION – the site has just been updated as I wrote this to show 25GB of free space!)
Interestingly they have also added a more prominent Live Search at the top of the page. Live Search is still struggling to get traction so perhaps this will help, though it does not go as far as some have suggested. This is good for developers using or considering Silverlight in their solutions because it will drive usage up, and because Microsoft are actively pushing it rather than taking the “no-installation-experience” option I highlighted in my previous post that is shown on some other Microsoft sites. It would be nice to summarise all the Microsoft sites that are using Silverlight, but I haven’t found an easy way to do this – if anyone has any ideas on how to get or find that information please let me know. Cheers Ian Technorati Tags: Silverlight,Installation 12月25日 Computer Weekly: Silverlight Skills are Hot!Nick Langley at Computer weekly just published an article titled: Hot Skills: Silverlight. It’s nice to see that there is a growing understanding of the importance of Silverlight for business’s working in the RIA space and that rates of pay are reflecting that. Nick is quoting the following rates of pay:
If you want to get up to speed on Silverlight then silverlight.net is a great place to start, but bbits also offer customised Silverlight training and seminars for designers and developers. Also look out for our new online based training in the new year… Ian 12月18日 itv.com helps push Silverlight – but is the installation experience good enough and why aren’t more Microsoft sites using Silverlight?itv.com pushes Silverlight installation in the UKSometime ago I was pleased to see that itv.com were using Silverlight to live stream their 4 TV channels. However it was a disappointment that the Silverlight player was only shown if the user already had Silverlight installed – if they hadn’t pre-installed it then itv would “fall back” to their standard Flash player. This could be termed a “no-installation-experience” perhaps. However it is nice to see that they are now actively pushing Silverlight as their “upgraded” video player. On the home page they now display this link: which leads to this page (http://www.itv.com/silverlight2/): This is a much more aggressive “installation experience” and I think significant in the UK, because of the strength of the ITV brand and the fact that they are pushing Silverlight right on their home page.
Interestingly itv.com have chosen to link to http://www.microsoft.com/silverlight/resources/install.aspx for the actual installation rather than provide a link directly to the Silverlight.2.0.exe (which is what the standard installation experience is when you create a project in Visual Studio for example – see my post here for details on customising that). I’m not sure that is the best choice since it takes the user to another site, but does provide a measure of comfort in that that user can be reassured the installation is from Microsoft, which takes me to the second point of this post. Is the installation experience of Silverlight good enough?No matter how you wrap it up with context and assistance for the user, users will eventually will see this: I have had comments from friends when trying Silverlight for the first time that this looked like a virus and they refused to install it. It is slightly strange to me that Microsoft have chosen to provide a link directly to the exe and not use the browser info bar, in the way that Adobe do with Flash, and Microsoft do with many other ActiveX controls. This I think passes the “mum test” much more easily, and is a more reassuring experience, more clearly showing what you are installing and from who, rather than the slightly cryptic: silverlight.dlservice.microsoft.com url that the above dialog presents. How long before some dodgy web site provides a silverlight installation that points to a fake Silverlight.2.0.exe that is a virus?
Current Silverlight Penetration
Do we have any figures on current installation levels for Silverlight? Scott Guthrie said in October:
That’s a strange way to state a figure: my dad has “access to my computer” which has Silverlight installed, but it doesn’t mean he is using it on his own, still it is a positive statement overall, and seeing an increasing number sites like itv.com using Silverlight is only going to help. An interesting site showing stats on various ria technologies is here: http://riastats.com/. The statistics shown on this site are collected through a small javascript included on various websites so it cannot be deemed definitive but it is an interesting comparison. It currently shows Silverlight staying at around 12% since release of version 2 – that obviously needs to improve and having Silverlight on high profile sites is the way that will happen. Silverlight on Microsoft SitesPerhaps surprisingly we have not seen a huge take up of Silverlight on Microsoft sites, but as Steve Ballmer stated at Mix08 this is only a matter of time, and we are certainly starting to see more. For example http://photos.live.com has a recently added a Silverlight slide show (though they use the “no-installation experience” – and roll back to an Ajax version with no silverlight player detected). Still it is frustration there are not many more and that Microsoft sites consistently display: Cheers Ian
12月17日 Third Party & Free Silverlight Controls – Demo Pages
A quick look at the current Silverlight controls available, starting with the paid for ones, and then the surprisingly good free ones! The screenshots list all the controls available in the collections. If I have missed any, please let me know. The Paid For OnesInfragistics NetAdvantagehttp://labs.infragistics.com/silverlight/lobsamples DevExpress Silverlight Controlshttp://demos.devexpress.com/Silverlight/ NB: Also see the free DevExpress controls below ComponentOnehttp://demo.componentone.com/Silverlight/ControlExplorer/ ComponentArthttp://www.componentart.com/webui/roadmap.aspx#sl Telerikhttp://demos.telerik.com/silverlight The Free Ones!Silverlight Contribhttp://www.codeplex.com/Silverlightcontrib & http://silverlightcontrib.org Silverlight Toolkithttp://www.codeplex.com/Silverlight
Dev Express AgDataGridhttp://demos.devexpress.com/AgDataGridDemos/ Dev Express AgMenuhttp://demos.devexpress.com/AgMenuDemos/
Cheers Ian Technorati Tags: Silverlight,Controls Silverlight Drag and Drop and HitTest on any layout (not just Canvas)A common requirement for a rich UI in a business application is to support dragging and dropping of UI elements. For this to be successful you will need to be able to detect what elements you are dragging over, and you will need to support dragging over any type of layout (such as a complex grid layout), which is a contrast to many examples out there that only show dragging and dropping in relation to absolute layout with a Canvas. In addition many blogs appear to mention that the UIElement.HitTest method, available in beta’s of Silverlight 2, has since disappeared. However we have the VisualTreeHelper.FindElementsInHostCoordinates method instead that can perform a point hittest for us (that is it will return the elements a point is inside). So to that end I have created a simple DragManager class that makes it easy to make any element draggable, and raises events when the dragged element collides with one or more elements. To use the DragManager simply add the class to your project, create an instance of it, wire up the collision event and call EnableDragableElement on the elements you want to drag. Note that since this uses the MouseLeftButtonDown, Move and Up events, any control that handles these events internally will not work (e.g. the Button). DragManager dm = new DragManager(LayoutRoot);
dm.Collision+= dm_Collision;
dm.EnableDragableElement(Ellipse1);
dm.EnableDragableElement(TextBlockStatus);
dm.EnableDragableElement(Image1);
void dm_Collision(object sender, CollisionEventArgs e) { TextBlockStatus.Text = ((FrameworkElement)e.Element).Name + " " + e.Position.X + "," + e.Position.Y + ": "; foreach (UIElement element in e.CollidedElements) { TextBlockStatus.Text += ((FrameworkElement)element).Name + " "; } }
Of the interesting methods in the DragManager, the following performs the move for the element, and uses a TranslateTransform to move the element. This is in contrast to many examples which use the Canvas.Top and Canvas.Left properties, and has the benefit of working with any layout. void elementToDrag_MouseMove(object sender, MouseEventArgs e) { if (isDragging) { UIElement element = (UIElement)sender; TranslateTransform transform = GetTranslateTransform(element); Point currentMousePosition = e.GetPosition(layoutRoot); double mouseX = currentMousePosition.X - lastMousePosition.X; double mouseY = currentMousePosition.Y - lastMousePosition.Y; transform.X += mouseX; transform.Y += mouseY; if (Collision != null) { List<UIElement> collidedElements = VisualTreeHelper.FindElementsInHostCoordinates( currentMousePosition, layoutRoot) as List<UIElement>; collidedElements.Remove(element); collidedElements.Remove(layoutRoot); if (collidedElements.Count() > 0) { CollisionEventArgs args = new CollisionEventArgs() { Element = element, Position = currentMousePosition, CollidedElements = collidedElements }; Collision(this, args); } } lastMousePosition = currentMousePosition; } }
This method calls GetTranslateTransform which looks for an existing TranslateTransform on the element or adds one if not present. It also calls the VisualTreeHelper.FindElementsInHostCoordinates method to determine if a collision has taken place and returns a collection of the elements that have been hit (note that we need to remove the layout root and the element we are dragging since they will always be returned.) Full source is available here:
Cheers Ian 12月11日 Creating Reusable Animations in Silverlight 2I answered a post on the Silverlight forums recently about creating reusable animations for an Image, and thought that it was worth a blog entry to expand on it a little. I am looking here at two common approaches to creating reusable animations within a single project:
Of course you can also have a combination of the two (so you could create the animation in blend, then create a copy of it in code and manipulate it) Using the Xaml ApproachHere's an example of the first approach. This is the Xaml created by recording an animation in Blend against "Image1". Notice that I have given the two DoubleAnimationUsingKeyFrames elements names (GrowImageScaleXAnimation and GrowImageScaleYAnimation) so that it is easy to change their properties in code later on:
<Storyboard x:Name="GrowImage" AutoReverse="True"> <DoubleAnimationUsingKeyFrames x:Name="GrowImageScaleXAnimation" BeginTime="00:00:00" Storyboard.TargetName="Image1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"> <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="1.1" KeySpline="0.24,0.75,0.9,1"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames x:Name="GrowImageScaleYAnimation" BeginTime="00:00:00" Storyboard.TargetName="Image1" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)"> <SplineDoubleKeyFrame KeyTime="00:00:00.5000000" Value="1.1" KeySpline="0.24,0.75,0.9,1"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </UserControl.Resources> So if you have two images like this: <Image x:Name="Image1" Width="100" Height="100" Source="Images/beta.jpg"
You can write the following code to play the animation on either one: private void PlayXamlImageAnimation(string ImageName) { GrowImage.Stop(); Storyboard.SetTargetName(GrowImageScaleXAnimation, ImageName); Storyboard.SetTargetName(GrowImageScaleYAnimation, ImageName); GrowImage.Begin(); } Using the Pure Code ApproachFor the pure code approach you create the animation in code and attach it to the image. Here is my code to create the animation: private Storyboard CreateAnimation(DependencyObject image) { Storyboard board = new Storyboard(); board.AutoReverse = true; DoubleAnimationUsingKeyFrames scaleXAnim = This produces exactly the same animation that was shown in the Xaml above, but is quite a bit more work (in truth I still find it easier to record this in Blend, then manually convert it to code from the Xaml) The benefit is that we have more control and we can get as many animation instances we want so that we can have them all playing at the same time on different images. So in this example I create two Storyboards and then instantiate them against my images in the constructor for the page:
Storyboard image1Animation;
Storyboard image2Animation;
public Page()
{
InitializeComponent();
image1Animation = CreateAnimation(Image1);
image2Animation = CreateAnimation(Image2);
}Then it is simple a matter of playing the animations when needed. You can download the source from my sky drive: Cheers Ian 12月7日 Working with Data in Silverlight 2 (Entity Framework, Ado.Net Data Services, and DataGrid)Since the announcement from Tim Mallalieu (Program Manager, LINQ to SQL and Entity Framework) that “as of NET 4.0 the Entity Framework will be our recommended data access solution for LINQ to relational scenarios” your first decision on how to get data into and out of your databases has been made easier: EF is the way to go when looking at solutions from Microsoft (of course you may still decide that you would prefer to use something else from a third party, perhaps LLBLGEN, or maybe a hand crafted solution?). But after that decision you still have another before we get the data anywhere Silverlight – how to “surface” or expose it. Here you could use a WCF service with a basicHttpBinding a-la the “Silverlight enabled wcf service” template offered to us by Visual Studio 2008 with the Silverlight tools installed. However a more interesting approach and one that appears to be a better fit with the way Alexandria (i.e. Silverlight 3) will work is to use an Ado.Net Data Service. So what is an Ado.Net Data Service? The documentation provides a nice summary:
So that means that we can surface our data very simply via a REST interface. Bottom line – you don’t have to write a whole load of methods in your service such as GetProducts, GetProductsById, GetOrder(int Id), UpdateOrder etc etc. Instead you simple expose your data model through the service. At this point I usually hear a gasp of belief – “what all our data is available read/write over the web?” – erm yes, but only if you choose not to lock it down. Ado.Net Data Services combined with the EF give you plenty of scope to control access to data based on a users authentication and to inject business and validation logic. But that is a blog post for another time. What I think is particularly interesting from Silverlight when you use this combination of EF and ADO.Net Data Services is that the code you write feels as if you are writing it on the same tier as the database and application logic – in fact, it is not much different, and that is quite compelling. It makes the classic find data, edit it with business logic applied, and sent back to the database quite easy (with a few gotcha’s which I will list below) When I show this to delegates on a course who are used to using the Asp.Net Datagrid it gets another gasp, but this time in a positive way (well maybe not a gasp - but certainly a look of bewilderment that belies the effort that have had to put into doing the same thing in Asp.net previously). Ok so lets see a little code – I am going to demonstrate editing data from the Products table in the Northwind sample database. I created a Silverlight Project then added a Ado.Net Entity Data Model to the web site and used the Wizard to add all the tables in the Northwind database to it. At this point you will most likely want to at least add some validation logic, or for more complex systems, extend the model. I will cover this is later posts, but for now you can get more info on using EF from zeeshanhirani's free entity framework learning guide.pdf including a walkthrough on this very item. The msdn documentation is getting better too. Next we add an Ado.Net Data Service to the web site and configure it to use our Entities type (in this case NorthwindEntities). Also in the example below I have allowed full read/write access to all the data in the model (gasp!). For more info on controlling access to the entities see the msdn documentation and I will be blogging about this in more details later. public class NorthwindDataService : DataService< NorthwindEntities > { // This method is called only once to initialize service-wide policies. public static void InitializeService(IDataServiceConfiguration config) { // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc. // Examples: config.SetEntitySetAccessRule("*", EntitySetRights.All); config.SetServiceOperationAccessRule("*", ServiceOperationRights.All); } } Ok we have our data available. At this point you can browse the svc file and see your data exposed as ATOM. You can try navigating through the data by using the Url. For example try the following (changing the port to your own web site of course :-) Although this looks like read-only data, the service supports updates too through other HTTP verbs (e.g PUT). So lets put that to the test by adding a service reference to Silverlight and creating an editable grid for the Products table (we will just edit one column for reasons that will become clear). In the Silverlight project select Add Service Reference, click Discover and give it appropriate name (e.g. NorthwindDataService). This will build a proxy class for us that supports the Ado.Net Data Service in Silverlight. We are going to use the DataGrid to display and edit the data. What we want to do is bind the data from our service to the DataGrid, use the grid to edit it, then send the changes back in a batch update. Currently the DataGrid has a few issues, not least we do not have an event that lets us know when row data has been edited (this was available in beta’s of the grid but was removed in the final release – a strange decision, one that I would expect to be resolved sometime in the future). To stack up another blog post with the others I have declared in this post, I will take a look at the free AgDataGrid from DevExpress in a later post which does not suffer from this. We will work around this problem by using a DataTemplate and attaching to the LostFocus event of the TextBox that we will use to edit the data. Here is the Xaml for our UI (if you can call it that – just a couple of buttons and a one column grid) <Grid x:Name="LayoutRoot" Background="White"> <Grid.RowDefinitions> <RowDefinition Height="30"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal"> <Button Content="Get data " Click="Button_Click" Margin="5" ></Button> <Button Content="Save data " Click="Button_Click_1" Margin="5"></Button> </StackPanel> <data:DataGrid x:Name="DataGrid1" AutoGenerateColumns="False" Grid.Row="1"> <data:DataGrid.Columns > <data:DataGridTemplateColumn Header="Product"> <data:DataGridTemplateColumn.CellTemplate> <DataTemplate> <TextBlock Text="{Binding ProductName, Mode=TwoWay}"></TextBlock> </DataTemplate> </data:DataGridTemplateColumn.CellTemplate> <data:DataGridTemplateColumn.CellEditingTemplate> <DataTemplate> <TextBox LostFocus="TextBox_LostFocus" Tag="{Binding ProductID}" In the code behind we will need to instantiate our service as a class scoped variable. Notice that the constructor takes a Uri pointing to the svc file we want to use. NorthwindDataService.NorthwindEntities entities = We can then use the DataServicesQuery type to create and execute a query when the Get Data button is clicked. Ideally we would just bind the results to the DataGrid, but this does not currently work, so instead I am also storing the results of the query in a List<Products> and binding that – this is really a hack to get around the DataGrid problem. private List<Products> loadedProducts;private void Button_Click(object sender, RoutedEventArgs e) { DataServiceQuery<Products> query = (DataServiceQuery<Products>) from c in entities.Products where c.Discontinued ==false select c; DataGrid1.ItemsSource = null; query.BeginExecute(new AsyncCallback(c => { IEnumerable<Products> results= query.EndExecute(c); loadedProducts = results.ToList(); DataGrid1.ItemsSource = loadedProducts; }),query); } Ok so when this button is clicked the DataGrid will show the result of the query using it’s DataTemplate: Selecting a cell, then clicking again in it will switch the DataGrid it to edit mode (or you could double-click, but that’s not very web ui ;-). After the data is edited we want to update our entity data with the changes. Ideally the DataGrid would do this for us automatically, but unfortunately it does not at the moment, so instead we have hooked the LostFocus event on the textbox: private void TextBox_LostFocus(object sender, RoutedEventArgs e) { int changedItemId = (int)((TextBox)sender).Tag; Products changedItem = (from c in loadedProducts where c.ProductID == changedItemId select c).Single(); entities.UpdateObject(changedItem); } You will notice that in the DataTemplate for the grid, I embedded the ID for the item in a tag property for the TextBox – I am using this to find the actual product data in the List<Products> we populated when we got the data. We then call UpdateObject on our entities object and pass the updated item to it. This has not made any call to the database at this point, just updated our –in-memory entities collection. Our Save Data event handler looks like the following, most of which is examining the OperationalResponses fro the Ado.Net Data service for exceptions. The nice bit is that we only need to call BeginSaveChanges on our entities object to update all the changes in the database. entities.BeginSaveChanges(SaveChangesOptions.ContinueOnError, new AsyncCallback(c => { try { DataServiceResponse response = entities.EndSaveChanges(c); var operationalResponses = response.GetEnumerator(); while (operationalResponses.MoveNext() == true) { MessageBox.Show(operationalResponses.Current.StatusCode + " " + operationalResponses.Current.Error); } MessageBox.Show("Saved"); } catch (DataMisalignedException ex) { MessageBox.Show("Misaligned " + ex.Message); } catch (DataServiceClientException ex) { MessageBox.Show("Client " + ex.Message); } catch (DataServiceRequestException ex) { MessageBox.Show("Request " + ex.Message); var operationalResponses = ex.Response.GetEnumerator(); while (operationalResponses.MoveNext() == true) { string message = string.Empty; if (operationalResponses.Current.Error != null) { message = operationalResponses.Current.Error.Message; } message += " Status code: " + operationalResponses.Current.StatusCode; MessageBox.Show(message); } } catch (DataServiceQueryException ex) { MessageBox.Show("Query " + ex.Message); } }), entities); I hope that has given you an insight to using data with Silverlight 2 – I will be adding more posts to drill deeper into these topics, but you may want to look at the Msdn docs for this for further info I have uploaded this sample project to my skydrive here:
Cheers Ian 11月27日 Serializing Objects to Isolated Storage in Silverlight 2Silverlight 2 does not have a BinaryFormatter like the full framework does so you have to take a different approach to serializing data to Isolated Storage. However there is a simple solution by using the DataContractSerializer. You will need to add a reference to System.Runtime.Serialization. This will serialize the object to an Xml format and is what Silverlight uses when you add a service reference. So if we have an object called Person, we can use the DataContract and DataMember attributes to mark it up as follows: [DataContract] public class Person { [DataMember] public int PersonId { get; set; } [DataMember] public string Name { get; set; } } You can then use the DataContractSerializer as follows to save Person data to Iso storage: Person p = new Person() { PersonId = 1, Name = "Fred" }; using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForSite()) { IsolatedStorageFileStream stream = store.CreateFile("Person.dat"); DataContractSerializer serializer = new DataContractSerializer(typeof(Person)); serializer.WriteObject(stream, p); stream.Close(); } MessageBox.Show("Data Saved"); And the following to read it: Person p; using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForSite()) { IsolatedStorageFileStream stream = store.OpenFile("Person.dat", FileMode.Open); DataContractSerializer serializer = new DataContractSerializer(typeof(Person)); p = (Person)serializer.ReadObject(stream); stream.Close(); } MessageBox.Show("Data Loaded"); TextBlock1.Text = p.Name + " " + p.PersonId; If you want to make this more generic you could create a generic method as follows: private void SaveData<T>(T dataToSave,string fileName) { using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForSite()) { IsolatedStorageFileStream stream = store.CreateFile(fileName); DataContractSerializer serializer = new DataContractSerializer(typeof(T)); serializer.WriteObject(stream, dataToSave); stream.Close(); } }
Then you can call it as follows: SaveData<ObservableCollection<Person>>(people, "People.dat"); SaveData<Person>(person, "Person.dat");
Cheers Ian 11月12日 Working with Xml Files in Silverlight and Isolated StorageThere was a question regarding this over on the Silverlight Forums and I put together a little example there. This struck me as a scenario that many corporate developers will come across and it is actually so easy and powerful with the combination of Linq to XML and Silverlight Isolated Storage that I thought it was worth a post here. Isolated Storage is one of the areas that offers a clear advantage to Silverlight – local protected (sandboxed) storage on a per application or site basis that works just like a file system. Careful use of this can reap great performance gains as demonstrated by Jesse here And Linq to Xml is Microsoft’s best api yet for working with Xml documents (and we’ve had a few!). I can see this being one of the easiest “Linq” wins for corporate developers. So on to the simple example: Here is the Xaml: <StackPanel x:Name="LayoutRoot" Background="White"> <StackPanel Orientation="Horizontal"> <Button Margin="10" Content="Create Xml" Click="Button_Click"></Button> <Button Margin="10" Content="Save Xml file" Click="Button_Click_3"></Button> <Button Margin="10" Content="Read Xml file" Click="Button_Click_1"></Button> <Button Margin="10" Content="Add Child" Click="Button_Click_2"></Button> <Button Margin="10 10 0 10" Content="Remove Child" Click="Button_Click_4"></Button> Which will look like this: Here is the code for the first button which will create an arbitrary XDocument: private void CreateXdoc() { _xdoc = new XDocument( new XComment("Created " + DateTime.Now.ToLongTimeString()), new XElement("Root", new XElement("Child", "data1",new XAttribute("attr", 1)), new XElement("Child", "data2", new XAttribute("attr", 2)), new XElement("Child", "data3", new XAttribute("attr", 3)), new XElement("Child", "data4", new XAttribute("attr", 4)) ) ); ShowXDoc(); } ShowXDoc() is simple showing the XDoc in the TextBox: private void ShowXDoc() { TextBox1.Text = _xdoc.ToString(); } To Save the XDocument into isolated storage we can use the following: private void SaveXDoc() { using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) { IsolatedStorageFileStream stream = store.OpenFile("MyXmlFile.Xml", FileMode.Create, FileAccess.Write); _xdoc.Save(stream); stream.Dispose(); } MessageBox.Show("Saved"); } And to read it back: private void ReadXDoc() { using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) { IsolatedStorageFileStream stream = store.OpenFile("MyXmlFile.Xml", FileMode.Open, FileAccess.Read); _xdoc = XDocument.Load(stream); ShowXDoc(); stream.Dispose(); } } Because XDocument can work with streams these operations are really simple. How about manipulating the XDocument? That’s where Linq to XML comes into play. It is so easy and intuitive to use that it can be done in a few lines of code. Here is how to add a new Element to the XDocument: XElement newChild = new XElement("Child","newdata" ,(new XAttribute("attr", "somevalue"))); _xdoc.Element("Root").Add(newChild); And here is how to find a particular element based on an attribute value and remove it: var item = from c in _xdoc.Element("Root").Elements("Child") where c.Attribute("attr").Value == TextBox2.Text select c; item.Remove(); Full project available here: Cheers Ian |
|
|