Labels

Showing posts with label Silverlight. Show all posts
Showing posts with label Silverlight. Show all posts

Wednesday, October 13, 2010

Silverlight Threading

Threading in Silverlight is no more new than the regular Threading works.
However there are two new things to know here.

·         DispatcherTimer
·         BackgroundWorker

DispatcherTimer

Let’s understand the quick need of Dispatcher by below example.

Much like .NET client applications (e.g. WPF & WinForms applications), Silverlight supports a Single-Threaded Apartment Model. In this model, a single thread runs your entire application and owns all the objects that represent user-interface elements. The thread that creates them  owns them, and other threads can’t interact with them directly.
If this rule is violated – as shown in below example, by trying to access a user-interface object from a background thread–you’re certain  to cause an immediate exception, a lock-up, or a subtler problem.


Problem

protected void Unnamed1_Click(object sender, RoutedEventArgs e)
{       
   ThreadStart thst = new ThreadStart(ThreadRole);
   Thread th = new Thread(thst);
   th.Start();      
}


private void ThreadRole()
{
  try
  {
    Thread.Sleep(2000);
    Label1.Text = "Done"; // Does Not Affects
  }
  catch (Exception ex)
  {
    Throw ex// Invalid cross-thread access Exception
  }
}



This is where Dispatcher comes in picture. The dispatcher owns the main application thread and thus able to perform the above violated work.


Solution using Dispatacher

private void ThreadRole()
{
  Thread.Sleep(2000);
  this.Dispatcher.BeginInvoke(delegate()
  {
    Label1.Text = "Done"// Makes Affect
  });
}


The DispatcherTimer doesn’t offer true multithreaded execution. Instead, it triggers a periodic Tick event on the main application thread.
This advantage of having the tick event always executing on the main application thread, side steps synchronization problems and the other headaches. Refer above example.

This event interrupts whatever else is taking place in your application, giving you a chance to perform some work. I.e It halts/locks the main application / user interface.
This behavior on one hand side steps synchronization problems, but on the other hand it also introduces a number of limitations. I.e. This is fine if you need to  frequently perform small amounts of work,  but if it is a time-consuming task, then the user interface locks up until it’s finished. Thus, the DispatcherTimer doesn’t help you make a user interface more responsive.

So the case where time-consuming task is required, you need the real multithreading.

Here are examples of some small amounts of work that can be handled thru DispatcherTimer.
·         For example, it’s a great way to periodically check a web service for new data.  As we know all web service calls are asynchronous and are carried out on a background thread. Thus, we can use the   DispatcherTimer to just launch the web service call asynchronously and come back. This will enable the time-consuming download to take place on a background thread.

The BackgroundWorker (System.ComponentModel.BackgroundWorker)

The BackgroundWorker component, which was first introduced with .NET 2.0 to simplify threading considerations in WinForms applications.
Fortunately, the  BackgroundWorker is equally at home in Silverlight.

The BackgroundWorker component is one of the simplest and safest approaches to multithreading.
This gives you a nearly foolproof way to run a time-consuming task on a separate thread.
Silverlight recommends whenever possible, it’s better to use the straightforward BackgroundWorker component than the lower-level Thread class.
Reason being –

·         It supports event-based model to notify client application about Progress/Cancellation.
·         Secondly, to achieve this event-based model, it uses the dispatcher behind the scenes. Thus abstracts away the marshalling issues with an event-based model. (As discussed above).

The BackgroundWorker’s core events are DoWork, ProgressChanged, and RunWorkerCompleted.


Solution using Dispatacher

private BackgroundWorker backgroundWorker = new BackgroundWorker();
public BackgroundWorkerTest()
{
 InitializeComponent();
 backgroundWorker.WorkerReportsProgress = true;
 backgroundWorker.WorkerSupportsCancellation = true;

 backgroundWorker.DoWork += backgroundWorker_DoWork;
 backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
 backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
}


Note
·         BackgroundWorker is perfect if you have a single asynchronous task that runs in the background from start to finish (with optional support for progress reporting and cancellation).
·         If you have something else in mind—for example, an asynchronous task that runs throughout the entire life of your application or an asynchronous task that communicates with your application while it does its work—you must design a customized solution that uses the threading features you’ve already seen.


Hope this helps.

Regards,
Arun Manglick

Monday, September 6, 2010

MVVM - Silverlight


The Model View ViewModel (MVVM) is an architectural pattern used in software engineering that originated from Microsoft as a specialization of the Presentation Model design pattern introduced by Martin Fowler.

Why MVVM & Why not MVC or MVP:

In the MVC pattern, the Model is the data, the View is the user interface, and the Controller is the programmatic interface between the view, the model, and the user input.
This pattern, however, does not work well in declarative user interfaces like WPF or Silverlight. Reason, the XAML that these technologies uses can define some of the interface between the input and the view (Using Data Binding, Triggers, and states can be declared in XAML).

Model-View-Presenter (MVP) is another common pattern for layering applications.
In the MVP pattern, the presenter is responsible for setting and managing state for a view.
Like MVC, MVP does not quite fit the Silverlight model because the XAML might contain declarative Data Binding, Triggers, & State Management.

So where does that leave us?

Luckily for Silverlight , the WPF community has rallied behind a pattern called Model-View-ViewModel (MVVM).
This pattern is an adaptation of the MVC and MVP patterns in which:
·         The ViewModel provides a data model and behavior to the View. This allows the View to declaratively bind to the ViewModel.
·         The View becomes a mix of XAML and C#.
·         The Model represents the data available to the application, and
·         The ViewModel prepares the Model in order to bind it to the View.

Note: As the MVVM pattern is designed to support WPF and Silverlight, this pattern is only newly Available To The Public, as opposed to MVC or Model View Presenter (MVP).


MVVM:

Largely based on the MVC pattern, MVVM is targeted at modern UI development platforms (WPF & Silverlight).
MVVM pattern designed to gain two things.
·         The advantages of separation of functional development provided by MVC
·         And leveraging the advantages of XAML and the WPF, by binding data as close as possible to the Model while using the XAML. Thus minimizing the need for “code behind,” especially in the View


·         MVVM was designed to make use of specific functions in WPF & Silverlight, to better facilitate the separation of View layer development from the rest of the pattern.
·         This is done by removing virtually all “code behind” from the View layer.
·         Instead of requiring designers to write View code, they can use the native WPF markup language XAML and Create Bindings To The Viewmodel, which is written and maintained by application developers.
·         This separation of roles allows designers to focus on UX needs rather than programming or business logic, allowing for the layers of an application to be developed in multiple work streams.


Elements of the MVVM pattern are described as below - Also similartiies with MVC.


Model
As in the classic MVC pattern, the model refers to either

(a)   An object model that represents the real state content (an object-oriented approach), or
(b)   The data access layer that represents that content (a data-centric approach).

View
As in the classic MVC pattern, the view refers to all elements displayed by the GUI such as buttons, windows, graphics, and other controls.

ViewModel
The ViewModel is a “Model of the View” that serves in data binding between the View and the Model.
The View-Model of MVVM is responsible for exposing the data objects from the Model, in such a way that those objects are easily consumed in XAML.

It is similar to the Controller (in the MVC pattern) that acts as a Data Binder that changes Model information into View information and passes commands from the View into the Model.

The ViewModel exposes public properties, commands, and abstractions.

Controller
some references for MVVM also include a Controller layer or illustrate that the ViewModel is a specialized functional set in parallel with a Controller, while others do not.

This difference is an ongoing area of discussion regarding the standardization of the MVVM pattern.


Criticism

 

There are currently three main areas of criticism regarding the MVVM pattern.

1.    MVVM currently lacks standardization from Microsoft both in implementation and in toolsets
2.    The overhead in implementing MVVM is “overkill” for simple UI operations. Also for larger applications, generalizing the View layer becomes more difficult.
3.    Moreover, data binding, if not managed well, can result in considerable memory consumption in an application.
4.    Exercise in creating large numbers of data bindings to the ViewModel results in duplicate code and maintenance problems.

Implementation:




Model

namespace Arun.Manglick.Silverlight.Model
{
    public class Product : INotifyPropertyChanged
    {
        #region Private Variables

        private int productId;       
        private string modelNumber;
        private string modelName;
        private double unitCost;
        private string description;

        #endregion

        #region Constructor

        public Product()
        {
        }

        public Product(string modelNumber, string modelName,double unitCost, string description)
        {
            ModelNumber = modelNumber;
            ModelName = modelName;
            UnitCost = unitCost;
            Description = description;
        }

        #endregion

        #region Properties

        public int ProductId
        {
            get { return productId; }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentException("Product Id - Can't be less than 0.");
                }
                else
                {
                    productId = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("ProductId"));
                }
            }
        }       
        public string ModelNumber
        {
            get { return modelNumber; }
            set {
                modelNumber = value;
                OnPropertyChanged(new PropertyChangedEventArgs("ModelNumber"));
            }
        }
        public string ModelName
        {
            get { return modelName; }
            set {
                modelName = value;
            OnPropertyChanged(new PropertyChangedEventArgs("ModelName"));
            }
        }
        public double UnitCost
        {
            get { return unitCost; }
            set
            {
                if (value < 0)
                {
                    throw new ArgumentException("Can't be less than 0.");
                }
                else
                {
                    unitCost = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("UnitCost"));
                }
            }
        }
        public string Description
        {
            get { return description; }
            set {
                description = value;
                OnPropertyChanged(new PropertyChangedEventArgs("Description"));
            }
        }

        #endregion

        #region INotifyPropertyChanged Members

        ///
        ///
        ///
        public event PropertyChangedEventHandler PropertyChanged;

        ///
        ///
        ///
        ///



        public void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, e);
        }

        #endregion
    }
}



ViewModel

namespace Arun.Manglick.Silverlight.ViewModels
{
    public class ProductViewModel : INotifyPropertyChanged
    {
        #region Variables

        Product theProduct;
        ObservableCollection<Product> theProducts = new ObservableCollection<Product>();
        public event EventHandler LoadComplete;

        #endregion
       
        #region .ctor
        public ProductViewModel()
        {
        }
        #endregion

        #region Properties

        public ObservableCollection<Product> AllProducts
        {
            get
            {
                return theProducts;
            }
            set
            {
                theProducts = value;
                this.NotifyPropertyChanged("AllProducts");
            }
        }

        public Product SingleProduct
        {
            get
            {
                if (theProducts.Count > 0)
                    return theProducts[0] as Product;

                return null;
            }
            set
            {
                theProduct = value;
                this.NotifyPropertyChanged("SingleProduct");
            }
        }

        #endregion

        #region Methods

        public void Refresh()
        {
            ObservableCollection<Product> lst = GetData();
            AllProducts = lst;
            SingleProduct = theProducts[0] as Product;
            if (LoadComplete != null) LoadComplete(this, null);
        }

        private ObservableCollection<Product> GetData()
        {
            // This could be a Service Call.
            ObservableCollection<Product> lst = new ObservableCollection<Product> {
                new Product{ ProductId=1, ModelNumber="AA", ModelName="AAA", UnitCost=12, Description= "AA DESC"},
                new Product{ ProductId=1, ModelNumber="BB", ModelName="BBB", UnitCost=12, Description= "BB DESC"},
                new Product{ ProductId=1, ModelNumber="CC", ModelName="CCC", UnitCost=12, Description= "CC DESC"}
            };
            return lst;
        }

        #endregion

        #region Property Changed Implementation

        public event PropertyChangedEventHandler PropertyChanged;

        protected void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #endregion
    }
}




View

<navigation:Page x:Class="Arun.Manglick.Silverlight.Views.SimpleBindingProducts"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
                 xmlns:ViewModel="clr-namespace:Arun.Manglick.Silverlight.ViewModels"
                 xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
    <UserControl.Resources>
        <ViewModel:ProductViewModel x:Key="TheProductViewModel"
                                    LoadComplete="viewModel_LoadComplete"
                                    d:IsDataSource="True">ViewModel:ProductViewModel>
    UserControl.Resources>
    <StackPanel>
        <Grid Name="gridProductDetails"
              DataContext="{Binding Path=SingleProduct, Mode=TwoWay, Source={StaticResource TheProductViewModel}}">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto">ColumnDefinition>
                <ColumnDefinition>ColumnDefinition>
            Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto">RowDefinition>
                <RowDefinition Height="Auto">RowDefinition>
                <RowDefinition Height="Auto">RowDefinition>
                <RowDefinition Height="Auto">RowDefinition>
                <RowDefinition Height="Auto">RowDefinition>
                <RowDefinition Height="Auto">RowDefinition>
                <RowDefinition Height="Auto">RowDefinition>
                <RowDefinition Height="Auto">RowDefinition>
            Grid.RowDefinitions>
            <TextBlock Margin="7">Product ID:TextBlock>
            <TextBox Margin="5" Grid.Column="1" x:Name="txtProductId" Text="{Binding ProductId, Mode=TwoWay}">TextBox>
            <TextBlock Margin="7" Grid.Row="1">Model Number:TextBlock>
            <TextBox Margin="5"   Grid.Row="1" Grid.Column="1" Text="{Binding ModelNumber, Mode=OneWay}">TextBox>
            <TextBlock Margin="7" Grid.Row="2">Model Name:TextBlock>
            <TextBox Margin="5"   Grid.Row="2" Grid.Column="1" Text="{Binding ModelName,Mode=TwoWay}">TextBox>
            <TextBlock Margin="7" Grid.Row="3">Unit Cost:TextBlock>
            <TextBox Margin="5" Grid.Row="3" Grid.Column="1" Text="{Binding UnitCost, Mode=TwoWay}">TextBox>
            <TextBlock Margin="7,7,7,0" Grid.Row="4">Description:TextBlock>
            <TextBox Margin="7" Grid.Row="4" Grid.Column="1" Text="{Binding Description , Mode=TwoWay}">TextBox>           
            <StackPanel Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Orientation="Vertical">
                <ProgressBar Height="20" Visibility="Collapsed" IsIndeterminate="True" x:Name="loadingBar" />              
            StackPanel>
        Grid>
        <sdk:DataGrid AutoGenerateColumns="True" Name="dataGrid1"
                      ItemsSource="{Binding Path=AllProducts, Mode=TwoWay, Source={StaticResource TheProductViewModel}}" />
    StackPanel>
navigation:Page>



namespace Arun.Manglick.Silverlight.Views
{
    public partial class SimpleBindingProducts : Page
    {
        ProductViewModel viewModel = null;

        #region Constructor

        public SimpleBindingProducts()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(SimpleBindingProducts_Loaded);
        }

        #endregion

        #region Events




        private void SimpleBindingProducts_Loaded(object sender, RoutedEventArgs e)
        {
            loadingBar.Visibility = Visibility.Visible;
            viewModel = Resources["TheProductViewModel"] as ProductViewModel;
            viewModel.Refresh();
        }




        private void viewModel_LoadComplete(object sender, System.EventArgs e)
        {
            if (loadingBar != null) loadingBar.Visibility = Visibility.Collapsed;
        }

        #endregion
    }
}


Hope this helps.

Thanks & Regards,
Arun Manglick