Linked In Twitter RSS

Request throttling in ASP.NET Web API

3. March 2012  · Comments (1)

 

Real world scenario

 

When you start to build real world REST API you will surely try to limit access to you API. You can limit number of concurrent requests by using http://www.iis.net/download/dynamiciprestrictions

or some other tools.

 

In this post I will try to limit access to my ASP.NET Web API by using my own code building my own custom DelegatingHandler.

 

What you will see here

 

In this code bellow is ThrottleRateHandler that will be used to limit number of calls to your API in hour. Also these same code will limit how many times you can call API in seconds.

These class is using code similar as StackOverflow has used for their throttle rate handler.

Also these code is based on Token authorization approach.

 

I will also use HTTPRuntime.Cache to implement this functionality. I plan for my real world REST API to use MemBase caching. So question for my readers: Is it smart to replace HTTPRuntime.Cache with MemBase later ?

 

Here is code for ThrottleRateHandler :

public class ThrottleRateHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            string query = request.RequestUri.Query;
            string accessToken = HttpUtility.ParseQueryString(query).Get("accessToken");
            double seconds = Convert.ToDouble(WebConfigurationManager.AppSettings["throttleTimeoutForMethodCall"]);
            int numberRequests = Convert.ToInt32(WebConfigurationManager.AppSettings["tokenRequestsPerHour"]);

            if (accessToken != null)
            {
                var throttleKey = string.Format(
                    "{0}-{1}", request.RequestUri.AbsolutePath, accessToken);
                var tokenKey = accessToken;
                var allowExecute = false;

                if (HttpRuntime.Cache[throttleKey] == null)
                {
                    // this check was added for integration testing
                    if (!seconds.Equals(0.0))
                    {
                        HttpRuntime.Cache.Add(
                            throttleKey,
                            true,
                            null,
                            DateTime.Now.AddSeconds(seconds),
                            Cache.NoSlidingExpiration,
                            CacheItemPriority.Low,
                            null);
                    }

                    allowExecute = true;
                }

                if (allowExecute)
                {
                    var hit = (HitInfo)(HttpRuntime.Cache[tokenKey] jQuery152020203585348297026_1330795835814 new HitInfo());

                    if (hit.Hits > numberRequests)
                    {
                        allowExecute = false;
                    }
                    else
                    {
                        hit.Hits++;
                    }

                    if (hit.Hits == 1)
                    {
                        HttpRuntime.Cache.Add(
                            tokenKey,
                            hit,
                            null,
                            DateTime.Now.AddMinutes(60),
                            Cache.NoSlidingExpiration,
                            CacheItemPriority.AboveNormal,
                            null);
                    }

                    if (!allowExecute)
                    {
                        return this.ReturnException(string.Format("You can call API {0} times per hour", numberRequests));
                    }
                }
                else
                {
                   return this.ReturnException(string.Format("You can call API every {0} seconds", seconds));
                }
            }
            else
            {
                var response = new HttpResponseMessage
                {
                    Content =
                        new StringContent("You must supply valid token to access method!"),
                    StatusCode = HttpStatusCode.Unauthorized
                };
                return Task<HttpResponseMessage>.Factory.StartNew(() => response);
            }

            return base.SendAsync(request, cancellationToken);
        }

        private Task<HttpResponseMessage> ReturnException(string message)
        {
            var response = new HttpResponseMessage
            {
                Content = new StringContent(message),
                StatusCode = HttpStatusCode.Conflict
            };
            return Task<HttpResponseMessage>.Factory.StartNew(() => response);
        }

        public class HitInfo
        {
            public HitInfo()
            {
                Hits = 1;
            }

            public string Token { get; set; }
            public int Hits { get; set; }
        }
    }

ASP.NET Web API Authorization using Tokens

3. March 2012  · Comments (11)

 

Planning real world REST API

 

When you try to plan how to build real world REST API like other major players like Facebook or Foursquare have you will soon realize that all major players use OAuth 2.0 .

ASP.NET Web API comes with support for authorize attribute and that’s nice, but for real world API I want to support token based approach.

 

OAuth 2.0 Server

 

For supporting token based approach  you must have some kind of server that will issue tokens. Building token server can be complex and most major players have implemented OAuth 2.0 server based on draft 10 OAuth documentation.

We hope that Microsoft will provide us with their own OAuth 2.0 server for free in final version of ASP.NET MVC 4.

Meanwhile I will just assume that you already have your own OAuth 2.0 server.

 

Building ActionFilterAttribute

 

I have solved my problem with authorization by implementing RequireAuthorize ActionFilterAttribute. This attribute also have scope property. Scope property is used for limiting access to your REST API.

 

You just need to decorate controllers or actions in controllers with this attribute and optionally set required scope for accessing these actions.

 

Here is RequireAuthorizeAtribute:

public class RequireAuthorization : ActionFilterAttribute
    {
        public string Scope { get; set; }

        public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            string[] scope = null;
            if (!string.IsNullOrEmpty(Scope))
            {
                scope = Scope.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
            }

            string query = actionContext.Request.RequestUri.Query;
            string accessToken = HttpUtility.ParseQueryString(query).Get("accessToken");

            // we first check for valid token
            if (accessToken != null)
            {
                IAccessTokenValidator accessTokenValidator = new AccessTokenValidator();
                bool validToken = accessTokenValidator.ValidateToken(accessToken, scope);

                if (!validToken)
                {
                    var response = new HttpResponseMessage
                    {
                        Content =
                            new StringContent("This token is not valid, please refresh token or obtain valid token!"),
                        StatusCode = HttpStatusCode.Unauthorized
                    };
                    throw new HttpResponseException(response);
                }
            }
            else
            {
                var response = new HttpResponseMessage
                {
                    Content =
                        new StringContent("You must supply valid token to access method!"),
                    StatusCode = HttpStatusCode.Unauthorized
                };
                throw new HttpResponseException(response);
            }

            base.OnActionExecuting(actionContext);
        }
    }



And here is AccessTokenValidator class:

public class AccessTokenValidator : IAccessTokenValidator
    {
        public bool ValidateToken(string token, string[] scope)
        {
            // replace this logic with dataBase access to table with tokens
            if (token != "someToken")
            {
                return false;
            }
            return true;
        }
    }

Force JSON DelegatingHandler for ASP.NET Web API

3. March 2012  · Comments (0)

 

Building real world REST API-s

 

When you decide to build real world REST API-s like Facebook or Foursquare and many others have you must think on lot of things such as security, scoping, throttling and similar.

One of the first thing you will encounter is need to support various media formats such as JSON, XML, PNG … in easy way.

ASP.NET Web API support JSON and XML formats out of the box and provide us with easy way to support any custom mediaType format.

I will try to provide you with series of posts in which I will try to explain how I have created real world REST API using ASP.NET Web API.

 

Common ways to enforce custom format

 

There are several ways to enforce custom format in REST API :

1) You can pass mediaType in request header .

2) You can pass mediaType in URI using following syntax : http://api.mySite.com/v1/contacts/1/JSON

3) You can pass mediaType in URI using following syntax : http://api.mySite.com/v1/contacts/1.JSON

4) You can pass mediaType in URI using following syntax : http://api.mySite.com/v1/contacts/1?mediaType=JSON

 

 

My preferred solution using ASP.NET Web API

 

First approach – I will support 

In REST API you will sure want to support first approach in which user passes mediaType in request header.

Second approach – I will not support

For second approach things are little difficult .

To support this way you will probably need to change routes like this where you pass optional parameter ext for mediaType extension :

config.Routes.MapHttpRoute(
                "Default", 
                "{controller}/{id}/{ext}", 
                new { id = RouteParameter.Optional, ext = RouteParameter.Optional } 
            );

Problem with this approach is that your code will only work for URI like this where you try to get particular contact with id 1 in JSON format:

http://api.mySite.com/v1/contacts/1/JSON

When you try to get all contacts in JSON format using URI like this it will fail:

http://api.mySite.com/v1/contacts/JSON

Instead of trying to get all contacts in that format in will try to find contact which has id with value “JSON” .

One possible way to resolve this is to have two controllers for same model. One called ContactController another ContactsController.

I just don’t prefer this solution. If you know any other solution to resolve this feel free to comment. You can read more about this in this sites :

http://codebetter.com/glennblock/2012/02/28/why-are-there-2-controllers-in-the-asp-net-web-api-contactmanager-example-rest-has-nothing-to-with-it-2/

http://wekeroad.com/2012/02/28/someone-save-us-from-rest/

Third approach – I will not support

You may be wondering why it is so nice and clean? I don’t like dots in URI in that way. You cannot parse that QueryString safely to get that extensions behind dot.

You can try do something like this :

requestString.EndsWith(".JSON")

But then you will get another wall. In this version of ASP.NET Web API when you try to change Request.RequestUriinside DelegatingHandlers method SendAsync and pass changed RequestUri to base, ASP.NET Web API still try to goto old RequestUri. It seams like routing is performed before DelegatingHandler…So if we try to get mediaType in DelegatingHandler from RequestUri :

http://api.mySite.com/v1/contacts/1.JSON

then substring URI to become:

http://api.mySite.com/v1/contacts/1

and pass new RequestUri to base we will not be successful.

Fourth approach – I will support

I have choose to support fourth approach. For using this approach you will need to add this line of code in GlobalAsax.cs :

config.MessageHandlers.Add(new UriFormatExtensionMessageHandler());

Here is my code for UriFormatExtensionMessageHandler :

public class UriFormatExtensionMessageHandler : DelegatingHandler
   {    
       private bool shouldForceJson;
       private bool sholudForceXml;
       public void CheckFormats(HttpRequestMessage request)
       {
           // uri must contain mediaType parameter to enforce that media type
           string query = request.RequestUri.Query;
           string mediaType = HttpUtility.ParseQueryString(query).Get("mediaType");
           if (mediaType != null)
           {
               switch (mediaType)
               {
                   case "JSON":
                       shouldForceJson = true;
                       break;
                   case "XML":
                       sholudForceXml = true;
                       break;
               }
           }
       }

protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, 
System.Threading.CancellationToken cancellationToken)
       {
           CheckFormats(request);
           if (shouldForceJson)
           {
               // clear the accept and replace it to use JSON.
               request.Headers.Accept.Clear();
               request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
               shouldForceJson = false;
           }

           if (sholudForceXml)
           {
               request.Headers.Accept.Clear();
               request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
               sholudForceXml = false;
           }
             
           return base.SendAsync(request, cancellationToken);
       }
   }

UPDATE :

For fourth approach Microsoft has provided solution in this version of WebApi.You should you code bellow :

config.Formatters.JsonFormatter.MediaTypeMappings.Add(
              new QueryStringMapping("mediaType", "json", "application/json"));

            config.Formatters.XmlFormatter.MediaTypeMappings.Add(
               new QueryStringMapping("mediaType", "xml", "application/xml"));
 

ItemClick event on Silverlight ItemsControl

16. March 2009  · Comments (2)

Recently I have been playing with Rik Robinson’s project called Silverlight 2 Scroller. You can read more about this control on this address :

http://www.wintellect.com/CS/blogs/rrobinson/archive/2009/01/22/yngwie-malmsteen-syndrome-silverlight-2-scroller-revisited.aspx

After looking at ItemsControl I have realize that you probobly want to implement click event when user click on your item in ItemsControl.ItemsControl is widely used in Silverlight.I have little modify project above to implement that click event.

Proccess is simple.I have created delegate that reference to method witch have ItemControlEventArgs parameter.I have also created ItemControlEventArgs class that inherits from EventArgs class and inside this class I have add property that represents Item data that is shown inside my  user control.

 public delegate void ItemClickDelegate(ItemControlEventArgs e);

        public class ItemControlEventArgs:EventArgs

        {

            public Employee ItemEmployee{get;set;}   

        }

        public event ItemClickDelegate ItemClicked;

One problem is that PresentationFrameworkCollection class does not have string indexer implemented, so we can’t write something like this :

(sender as Grid).Children["NameOfControl"]

If you have your own user control that use ItemsControl you probably have grid or stackPanel as your container control.I have implement on Grid_MouseLeftButtonDown event some code that iterate trough collection controls and find controls with particular names.

        private void Grid_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)

        {

            //clicked Employee

            Employee emp = new Employee();

            //iterate trough collection of children controls in grid to get emp info

            foreach (UIElement element in

            (sender as Grid).Children)

            {

                if (element != null)

                {

                    if (element.GetType() == typeof(System.Windows.Controls.StackPanel))

                    {

                        foreach (UIElement childrenElement in (element as StackPanel).Children)

                        {

                            Type childType = childrenElement.GetType();

                            if (childType == typeof(System.Windows.Controls.TextBlock))

                            {

                                if ((childrenElement as TextBlock).Name == "FullName")

                                {

                                    emp.FullName = (childrenElement as TextBlock).Text;

                                }

                                else if ((childrenElement as TextBlock).Name == "Title")

                                {

                                    emp.Title = (childrenElement as TextBlock).Text;

                                }

                                else if ((childrenElement as TextBlock).Name == "Age")

                                {

                                    emp.Age = Convert.ToInt32((childrenElement as TextBlock).Text);

                                }

                            }                 

                        }

                    }

 

                    //image property we don't use for anything

                        emp.ImagePath = "";

                }

 

            }

            ItemControlEventArgs args=new ItemControlEventArgs();

            args.ItemEmployee=emp;

            ItemClicked(args);

        }

After that on page where I use my user control I have event ItemClicked

 SilverlightScroller:ScrollerControl x:Name="theScrollerControl" 

            Margin="0,50,0,0" HorizontalAlignment="Center" VerticalAlignment="Top"

            ScrollTime="250" Width="940" Height="620" ItemClicked="item_Clicked"

and on codebehind I can get my itemData:

 private void item_Clicked(ScrollerControl.ItemControlEventArgs e)

        {

            MessageBox.Show(

            e.ItemEmployee.FullName + " " + e.ItemEmployee.Title + " " +

            e.ItemEmployee.Age.ToString() + " " + e.ItemEmployee.ImagePath);

        }

You can look picture bellow.

Proba1

SqlException class does'nt have public constructor

8. March 2009  · Comments (5)

I am currently preparing ado.net 3.5 course so I try to throw SqlException in one of my examples and realize that SqlException class is sealed and has no public constructor.

I have found solution on Rido blog http://blogs.msdn.com/rido how to create SqlException using reflection.

        public static SqlException CreateSqlException(string errorMessage, int errorNumber)

        {

 

            SqlErrorCollection collection = GetErrorCollection();

            SqlError error = GetError(errorNumber, errorMessage);

            MethodInfo addMethod = collection.GetType().

            GetMethod("Add", BindingFlags.NonPublic | BindingFlags.Instance);

            addMethod.Invoke(collection, new object[] { error });

            Type[] types = new Type[] { typeof(string), typeof(SqlErrorCollection) };

            object[] parameters = new object[] { errorMessage, collection };

            ConstructorInfo constructor = typeof(SqlException).

            GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, types, null);

            SqlException exception = (SqlException)constructor.Invoke(parameters);

            return exception;

        }

        private static SqlError GetError(int errorCode, string message)

        {

            object[] parameters = new object[] {

            errorCode, (byte)0, (byte)10, "server", message, "procedure", 0 };

            Type[] types = new Type[] {

            typeof(int), typeof(byte), typeof(byte), typeof(string), typeof(string),

            typeof(string), typeof(int) };

            ConstructorInfo constructor = typeof(SqlError).

             GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, types, null);

            SqlError error = (SqlError)constructor.Invoke(parameters);

            return error;

        }

 

        private static SqlErrorCollection GetErrorCollection()

        {

            ConstructorInfo constructor = typeof(SqlErrorCollection).

            GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { }, null);

            SqlErrorCollection collection = (SqlErrorCollection)constructor.Invoke(new object[] { });

            return collection;

        }

Silverlight chat application using WCF full duplex

25. February 2009  · Comments (7)

Last week I have been started to read about full duplex wcf and his implementation in silverlight applications.I have read tons of articles on MSDN and tons of blog and forum posts.

Recently I have found solution for chat application on this blog http://silverbling.blogspot.com/2009/01/using-pollingduplexhttpbinding-for.html 

so I have little modified that application but it is basically same application.This application is based on this blog post of Dan Wahlin :

http://weblogs.asp.net/dwahlin/archive/2008/06/16/pushing-data-to-a-silverlight-client-with-wcf-duplex-service-part-i.aspx

Everything that you need to know about building wcf full duplex application for silverlight you can find on msdn :

http://msdn.microsoft.com/en-us/library/cc645027(VS.95).aspx

and for accessing wcf duplex from silverlight client:

http://msdn.microsoft.com/en-us/library/cc645028(VS.95).aspx 

This application is also based on same articles above.This is simple chat application that has no chat rooms.Same concept you can use for developing multiplayer online silverlight games.

If you want to modify this application to add rooms you can look some useful pattern for that  List-Based Publish-Subscribe  :

http://msdn.microsoft.com/en-us/library/ms752254.aspx

What I have get when I have finished project looks like this :

Ielook1

Only thing that you need to do to make this project work for you is to change url of your wcf web service in page.xaml.cs .

this.pusher = new PushDataReceiver(

                this.processor,

                "http://localhost.:5433/SilverlightChatDuplexService.svc",

                "Silverlight/ISilverlightChatDuplexService/InitiateDuplex",    // The Wcf function or action.

                "");

 

Demo project : SilverlightChatSource

 

 

MVVM pattern in Silverlight using SLEextensions

15. February 2009  · Comments (6)

In this  article I will explain how to implement MVVM pattern in Silverlight.

I was very overjoyed when I was reading my friends article on Codeproject about MVVM pattern in WPF .

You can check this article by Miroslav Popovic on http://www.codeproject.com/KB/WPF/FirefoxLikeSearchWithMVVM.aspx

I have decided to try implement same pattern in Silverlight .After reading and researching about MVVM pattern and his implementation in Silverlight I have found that most used method for implementing something close to MVVM in Silverlight is done by using SLEextension libraries.

SLEextension libraries you can found on Codeplex at this adress : http://www.codeplex.com/SLExtensions

When we talk about MVVM pattern and his implementation we must mention concept of attached behaviors and what is main purpose of MVVM pattern.

The Attached Behavior pattern encapsulates "behavior" (usually user interactivity) into a class outside the visual heirarchy and allows it to be applied to a visual element by setting an attached property and hooking various events on the visual element.(definition by John Gossman)

Main concept of MVVM pattern is to shrink or completly remove logic from the presentation.More about these patterns you can read from links below.

There are many ways to implement attached behavior in Silverlight.You can use SilverlightFX libraries from Nikhil Kothari or something else but most easiest way to do this is to use SLEextensions library.

I will show you now how to implement this on small exemple project.I have created form with button, textbox and  datagrid control.Main purspose of this form is when you enter searh text and press search button grid is populated with filtered data.

Picture1

First we need to add reference to SLExtensions.dll.After that we need to create static class Commands where we need to put all our methods.We now create our method GetData.Whery important thing is to create static void Initialize to ensure later that static fields are initialized.

This class looks like this :

public static class Commands

    {

        static Commands()

        {

            GetData = new Command("GetData");

        }

 

        public static Command GetData

        {

            get;

            private set;

        }

        /// <summary>

        /// Ensure static fields are initialized

        /// </summary>

        public static void Initialize()

        {

        }

    }

After that we need to create base class for all ViewModels.It looks like this :

public abstract class ViewModel : INotifyPropertyChanged

    {

        public event PropertyChangedEventHandler PropertyChanged;

 

        protected virtual void OnPropertyChanged(string propertyName)

        {

            if (PropertyChanged != null)

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

        }

    }

and PersonViewModel class that has some important things.

In constructor PersonViewModel class we set what method will be responsible when GetData method executes.In setter or public property List we must set OnPropertyChanged method to occcur.

public class PersonViewModel:ViewModel

    {

        private List<Person> list;

        public string City { get; set; }

        public List<Person> List

        {

            get { return list; }

            set

            {

                list = value;

                OnPropertyChanged("List");

            }

        }

 

        public PersonViewModel(string CityP)

        {     

           City = CityP;

           Commands.GetData.Executed+=new System.EventHandler<ExecutedEventArgs>(GetData_Executed);

        }

 

        void GetData_Executed(object sender, ExecutedEventArgs e)

        {

            List = Model.Person.GetPeople();

            var query = from c in List

                        where c.City.ToLower().StartsWith(City.ToLower())

                        select c;

            List=query.ToList();

        }

 

    }

In xaml file we need to add reference to

xmlns:Input="clr-namespace:SLExtensions.Input;assembly=SLExtensions"

and set what method will be attached to button control.

Input:CommandService.Command="GetData"  
Input:CommandService.CommandParameter="{Binding}"

After that only two things more we must do.First we need to ensure that before proccesing xaml file static commands are created,and we need to on button click event pass PersonViewModel to Datacontext element to populate dataGrid control.

        public Page()

        {

            // Ensure static commands are created before xaml is processed

            Commands.Initialize();

            InitializeComponent();         

        }

 

        private void btnOK_Click(object sender, RoutedEventArgs e)

        {

            DataContext = new PersonViewModel(txtSearch.Text);

        }

I hope this will be good starting point for you to try to implement this pattern in your own projects. I think that reasons for implementing this pattern are obvious (testibility, separation from presentation layer…) but debbuging  is very big problem for everybody try to use this pattern in real Silverlight application.

Demo project: SilverlightMVVM.rar

 

Source :

Attached behavior pattern :

http://blogs.msdn.com/johngossman/archive/2008/05/07/the-attached-behavior-pattern.aspx

http://www.nikhilk.net/Silverlight-Behaviors.aspx

http://blogs.southworks.net/jdominguez/2008/08/icommand-for-silverlight-with-attached-behaviors/

MVVM pattern :

http://blogs.msdn.com/johngossman/archive/2005/10/08/478683.aspx

http://blogs.msdn.com/dancre/archive/2006/10/11/datamodel-view-viewmodel-pattern-series.aspx

http://www.nikhilk.net/Silverlight-ViewModel-Pattern.aspx

http://tozon.info/blog/post/2009/01/20/Silverlight-TreeView-MVVM-and-editing-1.aspx

http://mark-dot-net.blogspot.com/2008/11/model-view-view-model-mvvm-in.html

Styling silverlight datagrid control

8. February 2009  · Comments (4)

This article will explain you how to  modify default silverlight datagrid control look.

In this example I have created new silverlight application project and add datagrid control in xaml file.I have also created small class called Person to populate test data.

 public class Person

        {

            public int ID { get; set; }

            public string FirstName { get; set; }

            public string LastName { get; set; }

            public string City { get; set; }

        }

After I've set ItemsSource property of datagrid control to list of persons I have recived this error :

       Message: System.MethodAccessException: Person.get_ID()

       at System.Reflection.MethodBase.PerformSecurityCheck(Object obj, RuntimeMethodHandle method, IntPtr parent, UInt32 invocationFlags)

       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)

       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

       at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)

           at System.Reflection.RuntimePropertyInfo.GetValue(Object obj, Object[] index)

Resolution is to set my class Person to be public class.

Silverlight DataGrid control has default style for DataGrid,DataGridColumnHeader,DataGridRowHeader,DataGridRow,DataGridCell... We can examine this styles, modify them and create our own styles from this styles.

To examine this styles there is great tool called SilverlightDefaultStyleBrowser by Delay

http://blogs.msdn.com/delay/archive/2008/03/22/improved-access-to-silverlight-2-s-generic-xaml-resources-silverlightdefaultstylebrowser-available-via-clickonce.aspx

After I have modify some default styles I have got this look of silverlight datagrid :


First we need to add some new namespaces into xaml

        xmlns:primitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows"

        xmlns:localprimitives="clr-namespace:System.Windows.Controls.Primitives;assembly=System.Windows.Controls.Data"

        xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"

We must put all styles into

 <Grid.Resources>

of xaml file.Now I will explain some properties of DataGridStyle.All these properties we can also set directly into dataGrid control or we can create new style that we can easily reuse later in project.

<Style  x:Key="newDataGridStyle"  TargetType="data:DataGrid">
       <Setter Property="RowBackground" Value="#CFEEDE" />
       <Setter Property="AlternatingRowBackground" >
             <Setter.Value>
                  <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                  <GradientStop Color="#FFA3AEB9" Offset="0"/>
                  <GradientStop Color="#FF8399A9" Offset="0.375"/>
                  <GradientStop Color="#FF718597" Offset="0.375"/>
                  <GradientStop Color="#FF617584" Offset="1"/>
                  </LinearGradientBrush>
              </Setter.Value>
        </Setter>
        <Setter Property="Background" Value="#3E88B4" />
        <Setter Property="HeadersVisibility" Value="Column" />
        <Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
        <Setter Property="VerticalScrollBarVisibility" Value="Auto" />
        <Setter Property="SelectionMode" Value="Single" />
        <Setter Property="CanUserReorderColumns" Value="False" />
        <Setter Property="CanUserResizeColumns" Value="False" />
        <Setter Property="CanUserSortColumns" Value="True" />
        <Setter Property="AutoGenerateColumns" Value="True" />
        <Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected" />
        <Setter Property="BorderBrush">
             <Setter.Value>
                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                 <GradientStop Color="#FFA3AEB9" Offset="0"/>
                 <GradientStop Color="#FF8399A9" Offset="0.375"/>
                 <GradientStop Color="#FF718597" Offset="0.375"/>
                 <GradientStop Color="#FF617584" Offset="1"/>
                 </LinearGradientBrush>
             </Setter.Value>
         </Setter>
</Style >

As you can see on picture of dataGrid RowBackground property represent color of each row and AlternatingRowBackground property represent color every second row.If we set these two property to be equals all rows in grid will be same style.I have put code from borderBrush into AlternatingRowBackground property to get linearGradientBrush efect on every second row. SelectionMode property determine will multiselect support will be enabled or disabled.It has two values single and extended.

Very imortant is to set Grid.Resources above DataGrid control becouse if we try to set style of dataGrid and resources are bellow, dataGrid can't see those resources.After we have modified default style and put that style in  Grid.Resources we just add this code to apply style to dataGrid:

Style="{StaticResource newDataGridStyle}"

where newDataGridStyle is name of Style that we have define using x:Key .

Also many people ask how to change background color of DataGridHeader.It is easy to accomplish by modifing default DataGridColumnHeader style. We just need to change

  < vsm:VisualState x:Name="Normal" >

and put some code for getting efect like on picture above:

<vsm:VisualState x:Name="Normal" >
     <Storyboard>
        <ColorAnimationUsingKeyFrames BeginTime="0" Duration="0" Storyboard.TargetName="BackgroundRectangle" 
Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)">
             <SplineColorKeyFrame KeyTime="0" Value="#0A2752"/>
        </ColorAnimationUsingKeyFrames>
        <ColorAnimationUsingKeyFrames BeginTime="0" Duration="0" Storyboard.TargetName="BackgroundGradient" 
Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)">
             <SplineColorKeyFrame KeyTime="0" Value="#366AB5"/>
        </ColorAnimationUsingKeyFrames>
        <ColorAnimationUsingKeyFrames BeginTime="0" Duration="0" Storyboard.TargetName="BackgroundGradient" 
Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)">
             <SplineColorKeyFrame KeyTime="0" Value="#ACC4E7"/>
         </ColorAnimationUsingKeyFrames>
         <ColorAnimationUsingKeyFrames BeginTime="0" Duration="0" Storyboard.TargetName="BackgroundGradient" 
Storyboard.TargetProperty="(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
             <SplineColorKeyFrame KeyTime="0" Value="#ACC4E7"/>
         </ColorAnimationUsingKeyFrames>
      </Storyboard>
</vsm:VisualState>

I hope that this article will be good starting point for you to create your own styles for dataGrid control.

 

Demo project : DataGridStylesAndTemplates.rar (955.22 kb)

 

Source :

http://msdn.microsoft.com/en-us/library/cc278066(vs.95).aspx

http://msdn.microsoft.com/en-us/library/cc189093(VS.95).aspx

 

Silverlight supports fullscreen mode

5. February 2009  · Comments (0)

Silverlight 2 has build in support  for fullscreen mode.This feature can be very interested for building same online game solution or some cool video players or ...When we enter to this fullscreen mode everything is hidden including browser frame.

We can also resize our application to fit new changed screen size using two events in

Application.Current.Host.Content class.This class is located in C:\Program Files\Microsoft SDKs\Silverlight\v2.0\Reference Assemblies\System.Windows.dll and it looks like this :

        public sealed class Content

        {

            // Fields

 

            private EventHandler FullScreenChanged;

            private EventHandler Resized;

 

            // Events

 

            public event EventHandler FullScreenChanged;

            public event EventHandler Resized;

 

            // Methods

 

            public Content();

 

            internal void FireFullScreenChanged(object sender, EventArgs args);

            internal void FireResized(object sender, EventArgs args);

 

            // Properties

 

            public double ActualHeight { get; }

            public double ActualWidth { get; }

            public bool IsFullScreen { get; set; }

        }


You will see on little demo project how this works in real life.

First we need to add ScaleTransform in Page xaml code:

ScaleTransform ScaleX="1" ScaleY="1" x:Name="Root"

After that on Load method we need to save original width and height of application before entering fullscreen mode.We declare two variables to hold this values

private double width;

private double height;

and we set values:

 private void Load(object sender, RoutedEventArgs e)

        {

            height = this.Height;

            width = this.Width;

            Application.Current.Host.Content.Resized += new EventHandler(Resized);

            Application.Current.Host.Content.FullScreenChanged += new EventHandler(Resized);

        }

We add one method Resized to handle both our events 

Application.Current.Host.Content.Resized and  Application.Current.Host.Content.FullScreenChanged and inside this method we set how much application will resize on entering fullscreen mode by divide ActualWidth with old width and ActualHeight with old height.

 

private void Resized(object sender, EventArgs e)

        {

            if (Application.Current.Host.Content.IsFullScreen)

            {

                //if is fullscreen then zoom

                Root.ScaleX = (Application.Current.Host.Content.ActualWidth / width);

                Root.ScaleY = (Application.Current.Host.Content.ActualHeight / height);

            }

            else

            {

                //exit fullscreen remove zoom

                Root.ScaleX = 1;

                Root.ScaleY = 1;

            }

        }

And finally we add some button to change mode from fullscreen to normal on button click

private void Button_Click(object sender, RoutedEventArgs e)

        {

            Application.Current.Host.Content.IsFullScreen = !Application.Current.Host.Content.IsFullScreen;  

        }


 




 

Demo project : SilverlightFullscreenDemo.rar (1.59 mb)