A Site Dedicated to Computer Programs Code,Computer Tips and Tricks,Internet Tips and Tricks,Computer Knowledge,Computer and Internet Guide,Printer Guide,AntiVirus Guide,Boost up Computer,Computer Protection, Anti Spam and related articles.
Thursday, 7 August 2014
DoEvents in WPF
Friday, 22 July 2011
SplineSeries for the Silverlight Toolkit - C# Code
// (c) Copyright Microsoft Corporation. // This source is subject to the Microsoft Public License (Ms-PL). // Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. // All other rights reserved. using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Windows.Media; using System.Windows.Shapes; #if !DEFINITION_SERIES_COMPATIBILITY_MODE namespace System.Windows.Controls.DataVisualization.Charting { /// <summary> /// Represents a control that contains a data series to be rendered in X/Y /// line format. /// </summary> /// <QualityBand>Preview</QualityBand> [StyleTypedProperty(Property = DataPointStyleName, StyleTargetType = typeof(LineDataPoint))] [StyleTypedProperty(Property = "LegendItemStyle", StyleTargetType = typeof(LegendItem))] [StyleTypedProperty(Property = "PathStyle", StyleTargetType = typeof(Path))] [TemplatePart(Name = DataPointSeries.PlotAreaName, Type = typeof(Canvas))] [SuppressMessage("Microsoft.Maintainability", "CA1501:AvoidExcessiveInheritance", Justification = "Depth of hierarchy is necessary to avoid code duplication.")] public partial class SplineSeries : LineAreaBaseSeries<LineDataPoint> { #region public PointCollection Points /// <summary> /// Gets the collection of points that make up the spline. /// </summary> public PointCollection Points { get { return GetValue(PointsProperty) as PointCollection; } private set { SetValue(PointsProperty, value); } } /// <summary> /// Identifies the Points dependency property. /// </summary> public static readonly DependencyProperty PointsProperty = DependencyProperty.Register( "Points", typeof(PointCollection), typeof(SplineSeries), null); #endregion public PointCollection Points #region public PathGeometry SplinePoints /// <summary> /// Gets the collection of points that make up the line. /// </summary> public PathGeometry SplinePoints { get { return GetValue(SplinePointsProperty) as PathGeometry; } private set { SetValue(SplinePointsProperty, value); } } /// <summary> /// Identifies the SplinePoints dependency property. /// </summary> public static readonly DependencyProperty SplinePointsProperty = DependencyProperty.Register( "SplinePoints", typeof(PathGeometry), typeof(SplineSeries), null); #endregion public PathGeometry SplinePoints #region public double SplineTension /// <summary> /// Gets or sets the tension in the beziers that make up the spline. /// </summary> /// <remarks> /// The greater the tension, the more straight/linear the spline will look. /// Less tension creates a more curvy spline. /// </remarks> public double SplineTension { get { return (double) GetValue(SplineTensionProperty); } set { SetValue(SplineTensionProperty, value); } } /// <summary> /// Identifies the SplineTension dependency property. /// </summary> public static readonly DependencyProperty SplineTensionProperty = DependencyProperty.Register( "SplineTension", typeof(double), typeof(SplineSeries), new PropertyMetadata(2.5)); #endregion public double SplineTension #region public Style PathStyle /// <summary> /// Gets or sets the style of the Path object that follows the data /// points. /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Path", Justification = "Matches System.Windows.Shapes.Path.")] public Style PathStyle { get { return GetValue(PathStyleProperty) as Style; } set { SetValue(PathStyleProperty, value); } } /// <summary> /// Identifies the PathStyle dependency property. /// </summary> [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Path", Justification = "Matches System.Windows.Shapes.Path.")] public static readonly DependencyProperty PathStyleProperty = DependencyProperty.Register( "PathStyle", typeof(Style), typeof(SplineSeries), null); #endregion public Style PathStyle #if !SILVERLIGHT /// <summary> /// Initializes the static members of the LineSeries class. /// </summary> [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Dependency properties are initialized in-line.")] static SplineSeries() { DefaultStyleKeyProperty.OverrideMetadata(typeof(SplineSeries), new FrameworkPropertyMetadata(typeof(SplineSeries))); } #endif /// <summary> /// Initializes a new instance of the LineSeries class. /// </summary> public SplineSeries() { #if SILVERLIGHT this.DefaultStyleKey = typeof(SplineSeries); #endif } /// <summary> /// Acquire a horizontal linear axis and a vertical linear axis. /// </summary> /// <param name="firstDataPoint">The first data point.</param> protected override void GetAxes(DataPoint firstDataPoint) { GetAxes( firstDataPoint, (axis) => axis.Orientation == AxisOrientation.X, () => { IAxis axis = CreateRangeAxisFromData(firstDataPoint.IndependentValue); if (axis == null) { axis = new CategoryAxis(); } axis.Orientation = AxisOrientation.X; return axis; }, (axis) => axis.Orientation == AxisOrientation.Y && axis is IRangeAxis, () => { DisplayAxis axis = (DisplayAxis)CreateRangeAxisFromData(firstDataPoint.DependentValue); if (axis == null) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_NoSuitableAxisAvailableForPlottingDependentValue); } axis.ShowGridLines = true; axis.Orientation = AxisOrientation.Y; return axis; }); } /// <summary> /// Updates the Series shape object from a collection of Points. /// </summary> /// <param name="points">Collection of Points.</param> protected override void UpdateShapeFromPoints(IEnumerable<Point> points) { if (points.Any()) { PointCollection pointCollection = new PointCollection(); foreach (Point point in points) { pointCollection.Add(point); } //At least two points are necessary to generate a proper spline if (pointCollection.Count >= 2) { PathGeometry geometry = new PathGeometry(); PathFigure figure = new PathFigure(); PointCollection bezierPoints = GetBezierPoints(pointCollection); figure.StartPoint = bezierPoints[0]; for (int i = 1; i < bezierPoints.Count; i += 3) { figure.Segments.Add(new BezierSegment() { Point1 = bezierPoints[i], Point2 = bezierPoints[i + 1], Point3 = bezierPoints[i + 2] }); } geometry.Figures.Add(figure); SplinePoints = geometry; } else { SplinePoints = null; } Points = pointCollection; } else { Points = null; SplinePoints = null; } } #region Bezier Curve Building /* * Formulas and code pulled from Kerem Kat's MapBezier example: * http://www.codeproject.com/KB/silverlight/MapBezier.aspx */ private PointCollection GetBezierPoints(PointCollection pts) { PointCollection ret = new PointCollection(); for (int i = 0; i < pts.Count; i++) { // for first point append as is. if (i == 0) { ret.Add(pts[0]); continue; } // for each point except first and last get B1, B2. next point. // Last point do not have a next point. ret.Add(GetB1(pts, i - 1, SplineTension)); ret.Add(GetB2(pts, i - 1, SplineTension)); ret.Add(pts[i]); } return ret; } private Point GetB1(PointCollection pts, int i, double a) { Point derivedPoint = GetDerivative(pts, i, a); return new Point(pts[i].X + derivedPoint.X / 3, pts[i].Y + derivedPoint.Y / 3); } private Point GetB2(PointCollection pts, int i, double a) { Point derivedPoint = GetDerivative(pts, i+1, a); return new Point(pts[i + 1].X - derivedPoint.X / 3, pts[i + 1].Y - derivedPoint.Y / 3); } private Point GetDerivative(PointCollection pts, int i, double a) { if (pts.Count < 2) throw new ArgumentOutOfRangeException("pts", "Data must contain at least two points."); if (i == 0) { // First point. return new Point((pts[1].X - pts[0].X) / a, (pts[1].Y - pts[0].Y) / a); } if (i == pts.Count - 1) { // Last point. return new Point((pts[i].X - pts[i - 1].X) / a, (pts[i].Y - pts[i - 1].Y) / a); } return new Point((pts[i + 1].X - pts[i - 1].X) / a, (pts[i + 1].Y - pts[i - 1].Y) / a); } #endregion } } #endif
Essentially what it does, is takes prior points to adjust such that they curve into the next. The UpdateShapeFromPoints method was changed from the standard LineSeries and uses all of the helper methods that were added to calculate a Bezier curve.
We're pretty much almost done, except for one more minor touch. We need to have the default style included, such that it complies with how Silverlight Toolkit handles it's control templating. We add the following chunk of code to Themes\generic.xaml of the same project:
<!-- charting:SplineSeries --> <Style TargetType="charting:SplineSeries"> <Setter Property="IsTabStop" Value="False" /> <Setter Property="PathStyle"> <Setter.Value> <Style TargetType="Path"> <Setter Property="StrokeThickness" Value="2" /> <Setter Property="StrokeMiterLimit" Value="1" /> </Style> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="charting:SplineSeries"> <Canvas x:Name="PlotArea"> <Path Data="{TemplateBinding SplinePoints}" Stroke="{TemplateBinding Background}" Style="{TemplateBinding PathStyle}"/> <Polyline Points="{TemplateBinding Points}" /> </Canvas> </ControlTemplate> </Setter.Value> </Setter> </Style>
I've included a "patch" with these files and their updates -- they should be capable of being pasted over their old versions (the Themes\generic.xaml just has the SplineSeries.xaml pasted into it -- I would strongly recommend you doing this with your own source):
https://docs.google.com/viewer?a=v&pid=explorer&chrome=true&srcid=0B9hGvs6yJgfJNzA4YmE1OTctNzNmYy00MzMzLWJhNDQtNTc0M2FlNTA3NzFm&hl=en_US
Tuesday, 12 July 2011
SilverlightToolkit Development/Building Guidance
I've noticed that Silverlight Toolkit has some major issues with the entire build process -- only loading different components and making references to other DLL's (which get deleted if you perform a clean build), not other projects. The second those are removed from the Binaries directory, the entire system goes to hell and back, eventually grabbing references to your DLL's in the Silverlight Toolkit install directory (somewhere in your root drive under Program Files, etc). What a pain! Then you'd have to go back and rebuild the references and re-specify their location. There has to be a better way, right?
Kind of.
What I ended up doing, and I highly recommend this for anyone working with Silverlight Toolkit on a regular basis -- is to build a new solution, rather than use the existing ones. Don't worry about including their Design or Testing projects, unless they're important to you. Then follow this process:
1. Build a new solution and include all relevant projects.
As stated before, place everything that you actually care about building. In my case, I was only concerned about the actual controls - not the testing or design. Since I'm already heavily involved with using the controls, I'll know if they're broken in test or design scenarios.2. (Optional) Rename all projects to their full assembly name, rather than a shorthand version.
When viewing their projects, you'll notice things like Controls.Data or System.Controls, which are all very misleading. Everything falls under System.Windows.*. For sake of clarity, I found myself renaming them, so when I would eventually add references back to other projects, I could see the full name of the project/assembly built.3. Change build path in each project, for all build-types, to a single directory.
You'd be surprised how many different places the assemblies for Silverlight Toolkit get built to. Each project seems to be written by someone else, with their own styles/paths for where assemblies that are built get stored. I simplified everything and made everything build to the Binaries folder -- always.4. Referenced assemblies from other SDKs should be placed in a special folder under the build path.
This is a pattern we have at work -- if you build something that requires a reference to an SDK other than our own, you drop the assembly in a References project and store everything in source control for the entire team to use. You won't have to install the toolkits for other team members at this point, since they'll be in a standard location. For Design and Testing projects, SilverlightToolkit uses other SDKs, so this is vital for those of you wishing to build these.5. Remove all references to SilverlightToolkit projects and re-add them as a reference to the projects in your solution.
As stated before, the references all get built to different places, which can cause for some crazy issues when you perform a clean. They then default back to your SilverlightToolkit install. It's horribly complicated. Referencing your projects will create appropriate dependencies that cause everything to build correctly and together. You can imagine what will happen to System.Windows.Controls.DataVisualizations when it tries to reference System.Windows.Controls and the assemblies are built at different times with different dependencies. If you can't, imagine shear sacrilege. And pain. And suffering.If you follow these guidelines, you should have a much more stable/clean build of SilverlightToolkit, which will make future developments and enhancements easier to bake in (such as targeting a WPF platform for Charting). Unfortunately SilverlightToolkit also no longer accepts user contributions, due to licensing issues, and Microsoft is the sole development team. This makes getting features added a little (read: very) difficult.
Good luck and happy building!
Thursday, 7 July 2011
Embedding (Silverlight) Content in SharePoint Webparts
That being said, I did find a very clever way of embedding a piece of content (in my case, a Silverlight xap -- but this could be anything, a JPEG, PNG, MP3, etc) into a SharePoint webpart. Given my background of making everything work on multiple platforms, I wanted to have the simplest possible way of doing this, without having to continually go back and forth with new code. Given I'm also trying to write a platform of tools and controls, this enables me to make a standardized package where we're not going to have conflicts with different versions of a chunk of content in the layouts folder with different assemblies/features installed.
The jist of this is that we're going to have ASP.NET share the content through a compiled DLL. Seems complicated? Not as much as you'd think. It's a relatively simple process:
- Add content to your project.
- Add information about the resource to your assembly.
- Utilize the resource.
1. Add content to your project.
Originally, I wanted to do things the "right" way for embedding my content -- I wanted to add each file into the mapped Layouts folder that Visual Studio 2010 gracefully provides. However, a minor issue arose from this -- my content is changing dynamically, and I needed to add it as a linked item (like the way I've done with Silverlight and WPF controls). Since the file is linked and not actually in the folder, Visual Studio doesn't have it in the directory and thus, it is not built. You could potentially make a script to copy it back/forth after the content is built (in my case, it's a Silverlight xap), but then you have issues with checking it out, etc.
Instead, we want to link it, since that's much, much, much easier. It prevents a lot of issues that could also result with conflicting and different features, in the event of any major issues with deployment.
So, I created a folder in my project called "Resources" where I would link this in. In this example, I have a Silverlight application called "HelloWorld.xap" and will be linking in the existing xap from the Release directory. Make sure that this is included as an "Embedded Resource" from the "Build Actions."
2. Add information about the resource to your assembly.
In the SharePoint project, we need to open up AssemblyInfo.cs under Properties and edit it very minorly. We want to add in the new resource we have. I had to add in a reference to System.Web, and you likely will too.
At the bottom of the file, you will need to add one entry per resource, as follows:
[assembly: System.Web.UI.WebResource("EmbeddedSPContent.Resources.HelloWorld.xap", "application/x-silverlight-2")]
The first parameter of System.Web.UI.WebResource is the name of the file and its location in the project. Notice that to notate paths/directories, we need to use periods instead of slashes.
The second parameter of System.Web.UI.WebResource is the mime-type. What do you want the DLL to tell the browser that the file is? In my case, I want it to be recognized as a Silverlight application. This could potentially be a PNG, MP3, and so forth.
Still hanging on there? Cool, because we're almost done.
I added the following code to render it (a very quick/dirty Silverlight HTML mockup):
string resourceUrl = Page.ClientScript.GetWebResourceUrl(GetType(), "EmbeddedSPContent.Resources.HelloWorld.xap"); string control = @" <div id=""silverlightControlHost""> <object data=""data:application/x-silverlight-2,"" type=""application/x-silverlight-2"" width=""400px"" height=""400px""> <param name=""source"" value=""{MY_URL}"" /> <param name=""minRuntimeVersion"" value=""4.0.50826"" /> <a href=""http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0"" style=""text-decoration:none""> <img src=""http://go.microsoft.com/fwlink/?LinkId=108181"" alt=""Get Microsoft Silverlight"" style=""border-style:none"" /> </a> </object> </div>"; control = control.Replace("{MY_URL}", resourceUrl); writer.Write(control);
and look at the below, it works!
The sample is included on MSDN for others to use, so this will hopefully make it easier to follow what was done:
http://code.msdn.microsoft.com/Embedding-Resources-for-d61b251d
Please note that in order to get this working, we have to deploy it as a Farm Solution, rather than a Sandboxed Solution.
Saturday, 2 July 2011
VisualTreeHelper
/// <summary> /// Navigates the control tree to find a child of a specific type. /// </summary> /// <param name="element">The parent element to search under.</param> /// <param name="type">The type to search for.</param> /// <returns>The first child of the specified type on success or null on failure.</returns> protected object GetChildOfType(FrameworkElement element, Type type) { int count = VisualTreeHelper.GetChildrenCount(element); for (int i = 0; i < count; i++) { FrameworkElement child = (FrameworkElement)VisualTreeHelper.GetChild(element, i); if (child != null) { if (child.GetType() == type) return child; object deeperChild = GetChildOfType(child, type); if (deeperChild != null) return deeperChild; } } return null;}
Enjoy!
Wednesday, 22 June 2011
The XAML Message From Hell: "Error HRESULT E_FAIL has been returned from a call to a COM component."
Why Microsoft didn't decide to catch it and give a more appropriate exception escapes me. However, some things I've learned about this:
Your XAML is failing to load!
Now, your XAML may actually be correct, is the funny thing -- but maybe your project is missing the references. For example, I'm using the Silverlight Toolkit and setting styles at runtime on the fly, allowing for potential theming. I kept getting this error, even though my other projects which use the same code are building fine -- so why is my latest project failing to build?
I missed a reference that was used in the other project. When it came to load the XAML style, it was likely getting hung up on the missing reference and saying, "OK, I see you want to use System.Windows.Controls.Layout.Toolkit... but what in the world is that?"
To rectify this issue, it was necessary to add references all over again.
So, my troubleshooting advice is:
- Make sure the XAML is correct. When writing cross-platform (i.e. Silverlight and WPF), some of the controls/targets behave differently or have different properties. Visual Studio generally points this out as a build error.
- Make sure that you are referencing all dependent assemblies. Due to Silverlight's dependencies, if I write a library on top of Silverlight Toolkit, which then gets used in an application, that application needs to have the references to Silverlight Toolkit.
Saturday, 21 May 2011
Using the “Copy XAML” Feature in Expression Design to Create HTML5 SVG Path Data
Introduction
One of the things that I recently had someone show me was using the “Copy XAML” feature in Expression Design to create HTML5 SVG Path Data. I later found out that this was demoed at MIX11 in a session called HTML5 for Silverlight Developers. So with that said, I don’t take credit for discovering this, just documenting it for others to use.If you want to see the final product, then click here. Go ahead and right click on the page and you will see it’s just path data (no image – just straight up HTML).
Let’s get started:
- Fire up Microsoft Expression Design 4.
- Create a New Document (Hitting CTRL-N).
- Draw something cool. (Yes, I’m a big fan of the Dark Knight.)
- Hit CTRL-A to select everything.
- Click Edit-> Copy XAML.
- Go ahead and open something like Notepad2 and paste what is in your clipboard.
- You now want to clean this up a bit. The only parts we are
interested in keeping is the path data starting with M as shown below.
Go ahead and remove everything else in Red.
- And ending with the Z from the Path Statement. Again, remove the part in Red below.
- Now that we have one continuous line of points, let’s drop that into the path of a HTML5 SVG as shown below:
<!DOCTYPE html> <html lang="en"> <head> <title>Expression Web and HTML5</title> </head> <body> <svg xmlns="http://www.w3.org/2000/svg"> <path fill="black" d="M 218.806,168.146C 218.02,167.674 217.049,168.699 216.14,168.812C 215.258,168.922 214.362,168.812 213.473,168.812C 208.585,168.812 203.667,169.352 198.808,168.812C 184.373,167.208 168.511,159.798 160.813,147.482C 152.479,134.146 145.963,119.41 142.149,104.154C 141.28,100.678 141.685,96.9641 140.816,93.4883C 140.594,92.5995 140.263,91.731 140.149,90.822C 140.039,89.9401 140.259,89.0376 140.149,88.1556C 139.766,85.0927 138.546,79.7737 135.483,80.1566C 131.418,80.6648 129.048,85.2586 126.151,88.1556C 121.053,93.254 117.974,100.039 114.152,106.153C 106.638,118.177 97.8295,129.467 91.4886,142.149C 86.3243, 152.477 86.098,164.668 84.8227,176.145C 83.2401,190.388 81.0727,204.563 79.4901,218.806C 77.0578,240.697 80.0911,262.943 82.823,284.798C 85.246, 304.182 98.7341,323.407 115.486,333.458C 123.447,338.235 132.674,340.521 141.482,343.457C 144.959,344.616 148.511,345.669 152.148,346.123C 153.057, 346.237 153.905,346.676 154.814,346.79C 155.696,346.9 156.786,347.345 157.48,346.79C 158.196,346.218 158.261,345.033 158.147,344.124C 158.024, 343.138 157.163,342.388 156.814,341.457C 155.727,338.56 154.567,335.689 153.481,332.792C 150.491,324.818 148.88,316.39 146.815,308.128C 140.451, 282.673 138.927,248.691 157.48,230.138C 169.285,218.333 194.244,210.479 206.807,221.472C 214.962,228.607 219.511,239.624 222.139,250.135C 223.028, 253.69 223.916,257.246 224.805,260.801C 225.021,261.663 224.695,262.585 224.805,263.467C 224.919,264.376 224.686,265.662 225.472,266.133C 226.257, 266.605 227.434,266.053 228.138,265.467C 228.902,264.831 229.027,263.689 229.471,262.8C 230.763,260.217 231.79,257.506 232.804,254.801C 233.194, 253.761 234.137,253.024 234.804,252.135C 235.471,251.246 236.307,250.462 236.804,249.469C 238.983,245.11 239.971,240.103 242.803,236.137C 246.394, 231.11 251.942,227.568 257.468,224.805C 261.912,222.583 267.313,223.344 272.133,222.139C 277.346,220.835 282.917,218.836 288.131,220.139C 292.951, 221.344 298.229,223.033 301.462,226.805C 305.762,231.821 306.039,239.202 308.128,245.469C 312.152,257.542 314.549,270.171 316.127,282.798C 317.408, 293.047 322.499,302.456 326.126,312.128C 330.006,322.474 332.112,333.404 334.791,344.124C 335.952,348.767 338.963,352.812 340.124,357.455C 342.804, 368.175 346.393,378.665 348.79,389.451C 349.812,394.053 350.205,398.773 350.789,403.45C 350.9,404.331 350.679,405.234 350.789,406.116C 350.903, 407.025 350.54,408.782 351.456,408.782C 354.204,408.782 352.86, 403.466 353.456,400.783C 354.425,396.424 354.789,391.918 354.789,387.452C 354.789,374.779 353.456, 362.129 353.456,349.456C 353.456,318.713 349.327,286.254 360.122,257.468C 361.866,252.816 365.043,248.788 366.787,244.136C 368.183,240.414 369.102, 236.058 372.12,233.471C 378.307,228.168 386.212,224.782 394.117,222.805C 402.958,220.595 414.528,217.542 421.447,223.472C 425.528,226.97 427.264, 232.559 429.446,237.47C 432.051,243.331 433.317,249.771 434.112,256.135C 434.461,258.924 433.968,264.134 436.779,264.134C 437.772,264.134 437.871, 262.431 438.112,261.467C 438.327,260.605 438.112,259.69 438.112,258.801C 438.112,256.135 438.112,253.468 438.112,250.802C 438.112,242.754 437.952, 234.341 440.778,226.805C 442.877,221.207 448.31,216.257 454.11,214.806C 467.435,211.475 482.765,214.802 494.771,221.472C 499.469,224.082 505.646, 225.098 508.77,229.471C 513.223,235.706 513.234,244.13 515.436,251.469C 517.051,256.853 519.405,262.013 520.768,267.467C 522.93,276.113 520.93, 285.483 518.768,294.13C 516.939,301.449 518.346,310.399 513.436,316.127C 510.321,319.761 504.586,319.779 500.104,321.46C 495.004,323.372 489.511, 324.117 484.106,324.793C 483.224,324.903 482.329,324.793 481.44,324.793C 480.551,324.793 479.513,324.3 478.773,324.793C 477.947,325.344 478.143, 326.756 477.44,327.459C 476.738,328.162 474.774,327.799 474.774,328.792C 474.774,329.903 476.498,330.203 477.44,330.792C 478.283,331.319 479.176, 331.776 480.107,332.125C 484.194,333.658 488.56,334.309 492.772,335.458C 502.534,338.121 512.675,339.403 522.768,340.124C 538.339,341.236 553.853, 345.162 569.429,344.124C 578.687,343.506 590.053,343.17 596.092,336.125C 605.017,325.713 614.528,315.766 622.755,304.795C 631.916,292.582 636.108, 277.037 639.42,262.134C 648.329,222.042 638.769,177.679 622.089,140.149C 618.044,131.048 610.467,123.861 603.425,116.819C 585.079,98.4733 559.059, 89.2664 534.766,80.1566C 522.464,75.543 510.497,68.9426 497.438,67.4916C 487.719,66.4116 476.855,63.7848 468.108,68.1581C 462.583,70.9209 457.542, 76.693 456.776,82.823C 456.264,86.9241 461.186,89.8992 464.109,92.8217C 470.886,99.5995 478.355,105.818 484.106,113.486C 489.533,120.722 495.649, 128.508 496.771,137.483C 497.674,144.705 494.661,151.999 492.105,158.814C 488.813,167.593 483.392,176.508 475.441,181.477C 467.57,186.397 457.392, 186.81 448.111,186.81C 437.214,186.81 426.278,186.68 415.448,185.477C 408.514,184.706 401.553,183.836 394.784,182.144C 389.259,180.763 381.333, 181.239 378.786,176.145C 377.46,173.493 377.724,170.291 376.786,167.479C 371.339,151.138 370.787,133.378 370.787,116.152C 370.787,109.931 370.787, 103.709 370.787,97.4878C 370.787,94.8215 370.787,92.1552 370.787,89.4888C 370.787,88.6 371.649,87.0381 370.787,86.8225C 369.709,86.553 368.672, 87.8577 368.121,88.8223C 367.68,89.5939 368.336,90.6263 368.121,91.4885C 367.88,92.4526 367.136,93.2245 366.787,94.1549C 366.466,95.0127 366.343, 95.9324 366.121,96.8212C 364.722,102.418 364.035,108.326 361.455,113.486C 357.646,121.103 345.242,121.208 336.791,120.152C 328.631,119.132 322.813, 111.064 317.46,104.82C 312.957,99.566 307.13,95.3571 303.462,89.4888C 301.972,87.1055 301.144,84.2787 300.796,81.4898C 300.686,80.6079 300.906, 79.7054 300.796,78.8235C 300.682,77.9144 301.045,76.1572 300.129,76.1572C 299.018,76.1572 298.718,77.8813 298.129,78.8235C 297.603,79.6661 297.145, 80.5594 296.796,81.4898C 295.434,85.1232 295.071,89.0571 294.13,92.8217C 291.377,103.834 293.384,115.535 292.13,126.817C 290.754,139.202 287.593, 153.371 278.132,161.48C 270.644,167.898 260.036,169.349 250.802,172.812C 244.917,175.018 238.422,175.478 232.138,175.478C 228.521,175.478 224.416, 175.581 221.472,173.478C 220.568,172.833 219.969,171.806 219.473,170.812C 219.063,169.993 219.592,168.617 218.806,168.146 Z" /> </svg> </body> </html>
- Now load the page into a browser and you will get the following. You can test this in a browser by going here:
Conclusion
That was pretty easy to do and only took about 5 minutes. There are a lot of things you can do with path data as sometimes it is easier to drop in path data instead of add an image to a project. I hope you found this post helpful.
Monday, 9 May 2011
How to get the Windows user identity name in Silverlight.
- Open the ASPX page that carries the Silverlight XAP file inside the Object tag and add a new param (let's call it Initparams):
- Open the App.xaml.cs file and declare a global variable:
- Declare a variable in your Mainpage.xaml or the navigation page, and assign the global variable value to the local variable, and you have the current logged on Windows user name.
Observable Collection, second look.
Observable Collection by definition provide notification when an item is added, removed or refreshed. It is pretty cool nifty item to have. So if you want a type of collection that automatically notifies the UI to update when underlying data changes, it has to be Observable Collection. Less code, more feature out of the box. But I want to point out two things when using Observable Collection.
Most of the people forget the very import thing, if you are using Observable collection then for every change, Observable Collection fires an event. If it is very small numbers then you will not notice the performance issue. But if for some reason you are adding and/or removing 100s or 1000s of rows then you will see visible performance degradation. If your data is flat or if it does not have a collection inside the collection then there should not be any performance hit either. I wrote a sample code to time it so that I can document the performance issue if any.
Before we go into the example, I would like to point out the second issue of Memory Leak when using Observable Collection. Once you initialize a bound observable collection, do not new up again, rather, clear the collection and then add new items to the same collection. You can read about it in here and also here.
Now lets look at the performance timings.
Scenario 1: Simple observable collection class.
My backend data model is PERSON class
public class Person{public string Name { get; set; }public int Age { get; set; }}
Now the XAML
<Grid x:Name="LayoutRoot" Background="White"><Grid.RowDefinitions><RowDefinition Height="80"/><RowDefinition Height="*"/></Grid.RowDefinitions><Grid HorizontalAlignment="Center"><Grid.ColumnDefinitions><ColumnDefinition Width="Auto"/><ColumnDefinition Width="Auto"/><ColumnDefinition Width="Auto"/></Grid.ColumnDefinitions><Button Name="Add" Content="Add" Click="Add_Click" Grid.Column="0" HorizontalAlignment="Center" Margin="4,4,4,4"/><Button Name="Clear" Content="Clear" Click="Clear_Click" Grid.Column="1" HorizontalAlignment="Center" Margin="4,4,4,4"/><TextBox Name="Numbers" Grid.Column="2" Width="100"/></Grid><c1:C1FlexGrid Name="_grid" Grid.Row="1" /></Grid>
The top part has two buttons, one to add rows and another one to remove rows from the collection. The third is a textbox, where you can enter how many rows you want to add or remove. What we are currently interested in is, adding rows to the grid on the fly.
private void Add_Click(object sender, RoutedEventArgs e){DateTime dt = DateTime.Now;for(int i=0;i<int.Parse(Numbers.Text);i++)_people.Add(new Person() { Age = i + 10, Name = "Name" + i.ToString() });DateTime dt1 = DateTime.Now;TimeSpan dt2 = dt1.Subtract(dt);Debug.WriteLine(string.Format("{0}:{1}:{2}:{3}", dt2.Hours, dt2.Minutes, dt2.Seconds, dt2.Milliseconds));}
Armed with above code and XAML if you would run, the time it takes to add 1000 new object to the collection is 107ms. Not bad, no performance hit.
Scenario 2: Hierarchical Collection using Observable Collection.
We modify the Person Class as follows to include a children of same type.
public class Person{public string Name { get; set; }public int Age { get; set; }public ObservableCollection<Person> Children { get; set; }public Person(){Children = new ObservableCollection<Person>();}}
With this change, if you would run the same code, it takes 10.263s. You can see why the time jumped by 10 seconds, if I would remove the constructor from the code, it still comes down to 9.623ms. So the constructor is not the problem here.Lets move on to the third scenario.
Scenario 3: Hierarchical Collection using Custom Class derived off of Observable collection.
This is an interesting scenario that I did not think about it till I ran into a performance issue with grid. The idea behind this scenario is that, since by definition, Observable Collection fires an event notification for any change to the data, in our case, when we are adding a bunch of data, we do not want to fire event for each and individual change rather, fire an event at the end of all the changes are made. So the solution is to derive a new class off of Observable Collection and make sure you turn off the notification before adding range and at the end turn the notification on. I followed the Smart Collection explained in Daamir blog. Just in case, I added the class definition right here as well.
If you look at the code, three piece of information interesting to current article. SuspendCollectionChangeNotification, which is to turn off the notification and ResumeCollectionChangeNotification to turn on the notification. These two methods need to be invoked if you are adding one row at a time through your code. On the other hand, if you are adding a collection to the Observable Collection, then call AddRange method, which internally will take care of turning on and off the notification. Now lets look at the code changepublic class SmartCollection<T> : ObservableCollection<T>{public SmartCollection(): base(){_suspendCollectionChangeNotification = false;}public SmartCollection(List<T> list) : base(list) { }bool _suspendCollectionChangeNotification;protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e){if (!_suspendCollectionChangeNotification){base.OnCollectionChanged(e);}}public void SuspendCollectionChangeNotification(){_suspendCollectionChangeNotification = true;}public void ResumeCollectionChangeNotification(){_suspendCollectionChangeNotification = false;}public void AddRange(IEnumerable<T> items){this.SuspendCollectionChangeNotification();int index = base.Count;try{foreach (var i in items){base.InsertItem(base.Count, i);}}finally{this.ResumeCollectionChangeNotification();var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);this.OnCollectionChanged(arg);}}public void Repopulate(IEnumerable<T> items){this.Clear();this.AddRange(items);}}
public class Person{public string Name { get; set; }public int Age { get; set; }public SmartCollection<Person> Children { get; set; }public Person(){Children = new SmartCollection<Person>();}}
Modify the code bind and change all Observable Collection Reference to SmartCollection. Also change the for loop where we add individual items to SmartCollection to List as follows
private void Add_Click(object sender, RoutedEventArgs e){DateTime dt = DateTime.Now;Debug.WriteLine(string.Format("{0}:{1}:{2}:{3}", dt.Hour, dt.Minute, dt.Second, dt.Millisecond));List<Person> ppl = new List<Person>();for(int i=0;i<int.Parse(Numbers.Text);i++)ppl.Add(new Person() { Age = i + 10, Name = "Name" + i.ToString() });_people.AddRange(ppl);DateTime dt1 = DateTime.Now;TimeSpan dt2 = dt1.Subtract(dt);Debug.WriteLine(string.Format("{0}:{1}:{2}:{3}", dt2.Hours, dt2.Minutes, dt2.Seconds, dt2.Milliseconds));}
With these changes if you would run the program, you will see the 1000 row insertion only took 31ms. Now the question is, why can’t we do the step as creating temporary collection and then assign it back to observable collection? Wouldn’t it work the same way? One approach would be
var concatList = _people.Concat(pp);
_people = new ObservableCollection(concatList)
in my opinion, newing Observable collection had some memory problem in Silverlight. The recommendation was always new up only once and from there on, if you want to add items to it, you add item to it or clear and then add item to it. That is the only reason I did not try it out.
So the bottom line is that, when we use Observable Collection take care not to fire event when you are working on too large of data. As I mentioned earlier, if your data is flat then you will not incur any performance problems but if you have hierarchical data then I would recommend you to switch to a model to turn on and off the notification model.
If any of you have good way to do it, please feel free to drop me a note. I am very much interested in learning and understand new ways of doing things.



