DATABASE

Silverlight : Data Binding - Receiving Change Notifications for Bound Data

11/17/2012 12:44:31 AM

1. Problem

You have data-bound elements in your UI, and you want to enable change notifications and automatic refresh of the UI when the bound application data changes.

2. Solution

You implement the System.ComponentModel.INotifyPropertyChanged interface in your data types and the System.Collections.Specialized.INotifyCollectionChanged interface in your collection types. You then raise the events defined in these interfaces from the implementing types to provide change notifications. You also ensure that the Mode property for each data binding is set to either BindingMode.OneWay or BindingMode.TwoWay to enable automatic UI refresh.

3. How It Works

The Silverlight binding infrastructure is aware of these two special interfaces and automatically subscribes to change notification events defined in the interfaces when implemented by the data source types.

3.1. Change Notification for Noncollection Types

The INotifyPropertyChanged interface has a single event named PropertyChanged. The event parameter is of type PropertyChangedEventArgs, which accepts the name of the changing property as a string parameter to the constructor and exposes it through the PropertyName property. The PropertyChangedEvntArgs class is shown here:

public class PropertyChangedEventArgs : EventArgs
{
    // Fields
    private readonly string propertyName;

    // Methods
    public PropertyChangedEventArgs(string propertyName);

    // Properties
    public string PropertyName { get; }
}

Once you implement the INotifyPropertyChanged interface in your data source type, you raise PropertyChanged whenever you need to raise change notifications for any of the bound source properties. You pass in the name of the property being changed through an instance of PropertyChangedEventArgs. Listing 1 shows a small but standard sample implementation.

Listing 1. Sample Implementation of INotifyPropertyChanged
public class Notifier : INotifyPropertyChanged
{
  //implementing INotifyPropertyChanged
  public event PropertyChangedEventHandler PropertyChanged;
  //utility method to raise PropertyChanged
  private void RaisePropertyChanged(PropertyChangedEventArgs e)
  {
    if (PropertyChanged != null)
      PropertyChanged(this, e);
  }

  private string _SomeBoundProperty;
  public string SomeBoundProperty
  {
    get { return _SomeBoundProperty; }
    set
    {
      //save old value
      string OldVal = _SomeBoundProperty;
      //compare with new value
      if (OldVal != value)
      {
        //if different, set property
        _SomeBoundProperty = value;
        //and raise PropertyChanged
        RaisePropertyChanged(new
          PropertyChangedEventArgs("SomeBoundProperty"));
      }

    }
  }
}

					  

3.2. Change Notification for Collection Types

The INotifyCollectionChanged interface also has a single event, named CollectionChanged, which can be raised by implementing collection types to provide change notifications. The change information that can be gained for collections is richer in comparison to INotifyPropertyChanged, as you can see in the NotifyCollectionChangedEventArgs type listed here:

public sealed class NotifyCollectionChangedEventArgs : EventArgs
{
  // Other members omitted for brevity

public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action,
  object newItem, object oldItem, int index);

  public NotifyCollectionChangedAction Action { get; }

  public IList NewItems { get; }

  public int NewStartingIndex { get; }

  public IList OldItems { get; }

  public int OldStartingIndex { get; }
}

					  

The code sample in the next section shows a custom collection that implements INotifyCollectionChanged.

4. The Code

The sample code for this recipe builds a simple data entry form over the data struc tures in Listing 2.

Listing 2. Application Data Classes
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;

namespace Recipe4_3
{
  public class Employee : INotifyPropertyChanged
  {

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(PropertyChangedEventArgs e)
    {
      if (PropertyChanged != null)
        PropertyChanged(this, e);
    }

    public Employee()
    {
    }

private string _FirstName;
    public string FirstName
    {
      get { return _FirstName; }
      set
      {
        string OldVal = _FirstName;
        if (OldVal != value)
        {
          _FirstName = value;
          RaisePropertyChanged(new PropertyChangedEventArgs("FirstName"));
        }
      }
    }
    private string _LastName;
    public string LastName
    {
      get { return _LastName; }
      set
      {
        string OldVal = _LastName;
        if (OldVal != value)
        {
          _LastName = value;
          RaisePropertyChanged(new PropertyChangedEventArgs("LastName"));
        }
      }
    }

    private long _PhoneNum;
    public long PhoneNum
    {
      get { return _PhoneNum; }
      set
      {
        long OldVal = _PhoneNum;
        if (OldVal != value)
        {
          _PhoneNum = value;
          RaisePropertyChanged(new PropertyChangedEventArgs("PhoneNum"));
        }
      }
    }

    private Address _Address;

					  

public Address Address
    {
      get { return _Address; }
      set
      {
        Address OldVal = _Address;
        if (OldVal != value)
        {
          _Address = value;
          RaisePropertyChanged(new PropertyChangedEventArgs("Address"));
        }
      }
    }
  }
  public class Address : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(PropertyChangedEventArgs e)
    {
      if (PropertyChanged != null)
        PropertyChanged(this, e);
    }

    private string _Street;
    public string Street
    {
      get { return _Street; }
      set
      {
        string OldVal = _Street;
        if (OldVal != value)
        {
          _Street = value;
          RaisePropertyChanged(new PropertyChangedEventArgs("Street"));
        }
      }
    }

    private string _City;
    public string City
    {
      get { return _City; }
      set
      {
        string OldVal = _City;

					  

if (OldVal != value)
        {
          _City = value;
          RaisePropertyChanged(new PropertyChangedEventArgs("City"));
        }
      }
    }

    private string _State;
    public string State
    {
      get { return _State; }
      set
      {
        string OldVal = _State;
        if (OldVal != value)
        {
          _State = value;
          RaisePropertyChanged(new PropertyChangedEventArgs("State"));
        }
      }
    }

    private int _ZipCode;
    public int ZipCode
    {
      get { return _ZipCode; }
      set
      {
        int OldVal = _ZipCode;
        if (OldVal != value)
        {
          _ZipCode = value;
          RaisePropertyChanged(new PropertyChangedEventArgs("ZipCode"));
        }
      }
    }
  }

  public class EmployeeCollection : ICollection<Employee>,
    IList<Employee>,
    INotifyCollectionChanged
  {
    private List<Employee> _internalList;

					  
public EmployeeCollection()
    {
      _internalList = new List<Employee>();
    }

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    private void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
      if (CollectionChanged != null)
      {
        CollectionChanged(this, e);
      }
    }
    //Methods/Properties that would possibly change the collection and its content
    //need to raise the CollectionChanged event
    public void Add(Employee item)
    {
      _internalList.Add(item);
      RaiseCollectionChanged(
        new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add,
          item, _internalList.Count − 1));
    }
    public void Clear()
    {
      _internalList.Clear();
      RaiseCollectionChanged(
        new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
    public bool Remove(Employee item)
    {
      int idx = _internalList.IndexOf(item);
      bool RetVal = _internalList.Remove(item);
      if (RetVal)
        RaiseCollectionChanged(
          new NotifyCollectionChangedEventArgs(
            NotifyCollectionChangedAction.Remove, item, idx));
      return RetVal;
    }
    public void RemoveAt(int index)
    {
      Employee item = null;
      if (index < _internalList.Count)
        item = _internalList[index];
      _internalList.RemoveAt(index);

					  

if (index < _internalList.Count)
        RaiseCollectionChanged(
          new NotifyCollectionChangedEventArgs(
            NotifyCollectionChangedAction.Remove, item, index));

    }
    public void Insert(int index, Employee item)
    {
      _internalList.Insert(index, item);
      RaiseCollectionChanged(
        new NotifyCollectionChangedEventArgs(
          NotifyCollectionChangedAction.Add, item, index));
    }
    public Employee this[int index]
    {
      get { return _internalList[index]; }
      set
      {
        _internalList[index] = value;
        RaiseCollectionChanged(
          new NotifyCollectionChangedEventArgs(
            NotifyCollectionChangedAction.Replace, value, index));

      }
    }

    public bool Contains(Employee item)
    {
      return _internalList.Contains(item);
    }
    public void CopyTo(Employee[] array, int arrayIndex)
    {
      _internalList.CopyTo(array, arrayIndex);
    }
    public int Count
    {
      get { return _internalList.Count; }
    }
    public bool IsReadOnly
    {
      get { return ((IList<Employee>)_internalList).IsReadOnly; }
    }
    public IEnumerator<Employee> GetEnumerator()
    {
      return _internalList.GetEnumerator();

					  

}
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
      return (System.Collections.IEnumerator)_internalList.GetEnumerator();
    }
    public int IndexOf(Employee item)
    {
      return _internalList.IndexOf(item);
    }
  }

}

					  

As shown in Listing 2, both the Employee and the Address types implement INotifyPropertyChanged to provide change notification. You also define a custom collection named EmployeeCollection for Employee instances and implement INotifyCollectionChanged on the collection type.

You can see the additional change information that can be accessed through the NotifyCollectionChangedEventArgs. Using the NotifyCollectionChangedAction enumeration, you can specify the type of change (Add, Remove, Replace, or Reset). You can also specify the item that changed and its index in the collection. This detail allows the binding infrastructure to optimize the binding so that the entire UI bound to the collection need not be refreshed for each change in the collection.

Also note that the System.Collections.ObjectModel contains a generic type named ObservableCollection<T> that already implements INotifyCollectionChanged. For all data binding scenarios, unless you have a specific reason to implement your own collection type, ObservableCollection<T> should meet your needs.

However, ObservableCollection<T> simply extends Collection<T>, which is a base collection class in the framework. If you choose to have change notification enabled for some of the other, more advanced, collections in the framework, such as List<T> or LinkedList<T>, or if you have implemented your own custom collection types with custom business logic, implementing INotifyCollectionChanged is the way to go.

Another scenario where you might choose to implement a custom collection is if you want to declare the collection as a resource in your XAML. This would necessitate creating a nongeneric collection class with a default constructor, and you would possibly want to initialize such a collection in the constructor. You can, however, extend ObservableCollection directly in such cases and do away with the need to implement any of the collection manipulation methods shown in the previous sample.

Listing 3 shows the simple data entry UI that you build on top of this collection.

Listing 3. Data Entry UI XAML
<UserControl x:Class="Recipe4_3.MainPage"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Recipe4_3"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             Width="400"
             Height="441">

					  

<UserControl.Resources>
        <!-- Employee collection Data source -->
        <local:EmployeeCollection x:Key="REF_EmployeeCollection" />
        <!-- Data template to be used for the Employee type -->
        <DataTemplate x:Key="dtEmployee">
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding FirstName}" />
                <TextBlock Text="{Binding LastName}"
                           Margin="5,0,0,0" />
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot"
          Background="White"
          Margin="10,10,10,10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0"
                 x:Name="lbx_Employees"
                 ItemsSource="{StaticResource REF_EmployeeCollection}"
                 ItemTemplate="{StaticResource dtEmployee}"
                 SelectionChanged="lbx_Employees_SelectionChanged" />
        <Grid x:Name="grid_NewButton"
              Margin="0,2,0,0"
              Grid.Row="1"
              HorizontalAlignment="Right">
            <Button  x:Name="btn_New"
                     Click="btn_New_Click"
                     Content="New Employee" />
        </Grid>
        <Border Grid.Row="2"
                Visibility="Collapsed"
                x:Name="border_EmployeeForm"
                Margin="0,2,0,0"
                BorderBrush="Black"
                BorderThickness="1"
                Padding="1,1,1,1">
            <Grid x:Name="grid_EmployeeForm">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="0.142*" />

					  

<ColumnDefinition Width="0.379*" />
                    <ColumnDefinition Width="0.1*" />
                    <ColumnDefinition Width="0.097*" />
                    <ColumnDefinition Width="0.082*" />
                    <ColumnDefinition Width="0.2*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="0.10*" />
                    <RowDefinition Height="0.15*" />
                    <RowDefinition Height="0.15*" />
                    <RowDefinition Height="0.15*" />
                    <RowDefinition Height="0.45*" />
                </Grid.RowDefinitions>

                <TextBox HorizontalAlignment="Stretch"
                         Margin="1,1,1,1"
                         x:Name="tbxFName"
                         VerticalAlignment="Stretch"
                         Text="{Binding FirstName, Mode=TwoWay}"
                         Grid.Row="1"
                         Width="Auto"
                         Grid.RowSpan="1"
                         Grid.ColumnSpan="2"
                         Grid.Column="1" />
                <TextBox HorizontalAlignment="Stretch"
                         Margin="1,1,1,1"
                         x:Name="tbxLName"
                         VerticalAlignment="Stretch"
                         Text="{Binding LastName, Mode=TwoWay}"
                         Grid.Row="1"
                         Grid.Column="3"
                         Width="Auto"
                         Grid.RowSpan="1"
                         Grid.ColumnSpan="3" />
                <TextBlock HorizontalAlignment="Stretch"
                           Margin="1,1,1,1"
                           VerticalAlignment="Stretch"
                           Text="Last"
                           TextWrapping="Wrap"
                           Grid.RowSpan="1"
                           Grid.Column="4"
                           Grid.ColumnSpan="2"
                           Height="Auto"
                           Width="Auto" />
                <TextBlock HorizontalAlignment="Center"

					  

Margin="1,1,1,1"
                           VerticalAlignment="Center"
                           Text="First"
                           TextWrapping="Wrap"
                           Grid.RowSpan="1"
                           Grid.Column="1"
                           Width="Auto"
                           Height="Auto" />
                <TextBlock HorizontalAlignment="Center"
                           Margin="1,1,1,1"
                           VerticalAlignment="Stretch"
                           Text="Name"
                           TextWrapping="Wrap"
                           Grid.RowSpan="1"
                           Grid.Row="1"
                           Height="Auto"
                           Width="Auto" />
                <TextBlock HorizontalAlignment="Center"
                           Margin="1,1,1,1"
                           VerticalAlignment="Stretch"
                           Text="Street"
                           TextWrapping="Wrap"
                           Grid.Row="2"
                           Width="Auto" />
                <TextBox HorizontalAlignment="Stretch"
                         x:Name="tbxStreet"
                         VerticalAlignment="Stretch"
                         Text="{Binding Address.Street, Mode=TwoWay}"
                         Grid.Row="2"
                         Margin="1,1,1,1"
                         Grid.Column="1"
                         Grid.ColumnSpan="5"
                         Width="Auto" />
                <TextBlock HorizontalAlignment="Center"
                           VerticalAlignment="Stretch"
                           Text="City"
                           TextWrapping="Wrap"
                           Margin="1,1,1,1"
                           Grid.Row="3" />
                <TextBlock Text="State"
                           Margin="1,1,1,1"
                           TextWrapping="Wrap"
                           Grid.Column="2"
                           Grid.Row="3"
                           HorizontalAlignment="Center" />

					  

<TextBlock Text="Zip"
                           Margin="1,1,1,1"
                           TextWrapping="Wrap"
                           Grid.Column="4"
                           Grid.Row="3"
                           HorizontalAlignment="Center" />
                <TextBox HorizontalAlignment="Stretch"
                         x:Name="tbxCity"
                         Margin="1,1,1,1"
                         VerticalAlignment="Stretch"
                         Text="{Binding Address.City, Mode=TwoWay}"
                         Grid.Row="3"
                         Grid.Column="1" />
                <TextBox Background="Transparent"
                         Grid.Column="3"
                         Margin="1,1,1,1"
                         Grid.Row="3"
                         Text="{Binding Address.State, Mode=TwoWay }"
                         x:Name="tbxState">
                </TextBox>
                <TextBox Background="Transparent"
                         Grid.Column="5"
                         Grid.Row="3"
                         Margin="1,1,1,1"
                         Text="{Binding Address.ZipCode, Mode=TwoWay }"
                         x:Name="tbxZipCode" />
                <TextBlock HorizontalAlignment="Center"
                           VerticalAlignment="Stretch"
                           Text="Phone"
                           Margin="1,1,1,1"
                           TextWrapping="Wrap"
                           Grid.Row="4" />
                <TextBox Grid.Column="1"
                         Grid.Row="4"
                         Margin="1,1,1,1"
                         Text="{Binding PhoneNum, Mode=TwoWay }"
                         x:Name="tbxPhoneNum" />
                <Button  Grid.Column="5"
                         Margin="1,1,1,1"
                         Grid.Row="4"
                         Height="30.911"
                         VerticalAlignment="Top"
                         Content="Close"
                         x:Name="btnClose"
                         Click="btnClose_Click" />

					  

</Grid>
        </Border>
    </Grid>
</UserControl>

You can see that, for the editable controls, you set the Mode property of the binding to BindingMode.TwoWay. The Mode property can be set to one of three values:

  • BindingMode.OneTime binds the value coming from the data source only once, when the element is initially displayed, and never again during the lifetime of the application. This is useful for static data that does not change for the lifetime of the application.

  • BindingMode.OneWay refreshes the bound value with any changes that happens to the data source but does not propagate changes made in the UI to the bound data source. This is useful for data that is read only to the user but that can change through other means in the application. This is the default setting for Binding.Mode if you do not specify any setting in your XAML or code.

  • BindingMode.TwoWay enables bidirectional propagation of changes and is the suitable mode for data-editing scenarios.

Running the sample produces the output shown in Figure 1.

Figure 1. Initial output from the application

Listing 4 shows the codebehind for the MainPage. As shown in the constructor, you initialize the bound EmployeeCollection instance with some initial Employee data. If you selected one of the records, you would see the output in Figure 2.

Listing 4. Codebehind for the Page
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;

namespace Recipe4_3
{
  public partial class MainPage : UserControl
  {
    public MainPage()
    {
      InitializeComponent();

      //initialize the employee collection with some sample data
      EmployeeCollection empColl = (EmployeeCollection)lbx_Employees.ItemsSource;

      empColl.Add(new Employee
      {
        FirstName = "Joe",
        LastName = "Duffin",
        PhoneNum = 2125551212,
        Address = new Address
        {
          Street = "2000 Mott Street",
          City = "New York",
          State = "NY",
          ZipCode = 10006
        }
      });

      empColl.Add(new Employee
      {
        FirstName = "Alex",
        LastName = "Bleeker",
        PhoneNum = 7185551212,
        Address = new Address
        {
          Street = "11000 Clover Street",
          City = "New York",
          State = "NY",
          ZipCode = 10007
        }
      });

					  

empColl.Add(new Employee
      {
        FirstName = "Nelly",
        LastName = "Myers",
        PhoneNum = 7325551212,
        Address = new Address
        {
          Street = "12000 Fay Road",
          City = "New York",
          State = "NY",
          ZipCode = 10016
        }
      });
    }

    private void btn_New_Click(object sender, RoutedEventArgs e)
    {
      //get the bound collection
      EmployeeCollection empColl = (EmployeeCollection)lbx_Employees.ItemsSource;
      //create and initialize a new Employee
      Employee newEmp = new Employee();
      newEmp.Address = new Address();
      //add it to the collection
      empColl.Add(newEmp);
      //set the current selection to the newly added employee.
      //This will cause selection change to fire, and set
      //the datacontext for the form appropriately
      lbx_Employees.SelectedItem = newEmp;

    }

    private void lbx_Employees_SelectionChanged(object sender,
      SelectionChangedEventArgs e)
    {
      //set the datacontext of the form to the selected Employee
      grid_EmployeeForm.DataContext = (Employee)lbx_Employees.SelectedItem;
      //show the form
      border_EmployeeForm.Visibility = Visibility.Visible;
      grid_NewButton.Visibility = Visibility.Collapsed;
    }

    private void btnClose_Click(object sender, RoutedEventArgs e)
    {
      //hide the form
      border_EmployeeForm.Visibility = Visibility.Collapsed;

					  

grid_NewButton.Visibility = Visibility.Visible;
    }
  }
}

Figure 2. Edit form for an existing employee

In the SelectionChanged handler for lbxEmployees, named lbx_Employees_SelectionChanged() in Listing 4, you set the DataContext of the containing Grid named grid_EmployeeForm to the selected Employee data item. This populates the contained fields with various properties of the Employee instance based on the bindings defined in Listing 3. You then make the Grid visible.

If you try editing the First Name field, you should see it changing in the selected item in the ListBox once you tab out of the field after the edit. As the data entry form propagates the change back to the appropriate Employee item in the collection as a result of the TwoWay binding, this action, in turn, causes the ListBox's binding to the collection to refresh the selected item.

If you click the New Employee button, you should get a blank data entry form, as shown in Figure 3, and see a blank item added to the ListBox. To achieve this, you handle the Click event of the button in btn_New_Click() shown in Listing 4. You create a new instance of the Employee type, initialize it, and add it to the collection. This takes care of displaying the blank item in the ListBox through the change notification mechanism of INotifyCollectionChanged. You also programmatically make that item the selected item in the ListBox, which in turns fires the SelectionChanged handler of the ListBox, and the data entry form is displayed again, as described in the previous paragraph.

Filling the fields in the data entry form should again cause change notifications to be propagated to the ListBox, as you tab out of fields.

Figure 3. Entering a new employee
Other  
  •  Silverlight : Binding Using a DataTemplate
  •  SQL Server 2005 : Advanced OLAP - Partitions, Aggregation Design, Storage Settings, and Proactive Caching
  •  SQL Server 2005 : Advanced OLAP - Actions
  •  SQL Server 2005 : Advanced OLAP - Key Performance Indicators (part 2) - KPI Queries in Management Studio
  •  SQL Server 2005 : Advanced OLAP - Key Performance Indicators (part 1) - A Concrete KPI, Testing KPIs in Browser View
  •  Oracle Database 11g : Database Fundamentals - Work with Object and System Privileges, Introduce Yourself to the Grid
  •  Oracle Database 11g : Database Fundamentals - Become Familiar with Other Important Items in Oracle Database 11g
  •  Microsoft ASP.NET 4 : Repeated-Value Data Binding (part 2) - Creating a Record Editor
  •  Microsoft ASP.NET 4 : Repeated-Value Data Binding (part 1)
  •  Microsoft ASP.NET 4 : Single-Value Data Binding
  •  
    Video
    Top 10
    SG50 Ferrari F12berlinetta : Prancing Horse for Lion City's 50th
    The latest Audi TT : New angles for TT
    Era of million-dollar luxury cars
    Game Review : Hearthstone - Blackrock Mountain
    Game Review : Battlefield Hardline
    Google Chromecast
    Keyboards for Apple iPad Air 2 (part 3) - Logitech Ultrathin Keyboard Cover for iPad Air 2
    Keyboards for Apple iPad Air 2 (part 2) - Zagg Slim Book for iPad Air 2
    Keyboards for Apple iPad Air 2 (part 1) - Belkin Qode Ultimate Pro Keyboard Case for iPad Air 2
    Michael Kors Designs Stylish Tech Products for Women
    REVIEW
    - First look: Apple Watch

    - 3 Tips for Maintaining Your Cell Phone Battery (part 1)

    - 3 Tips for Maintaining Your Cell Phone Battery (part 2)
    Popular Tags
    Video Tutorail Microsoft Access Microsoft Excel Microsoft OneNote Microsoft PowerPoint Microsoft Project Microsoft Visio Microsoft Word Active Directory Exchange Server Sharepoint Sql Server Windows Server 2008 Windows Server 2012 Windows 7 Windows 8 Adobe Flash Professional Dreamweaver Adobe Illustrator Adobe Photoshop CorelDRAW X5 CorelDraw 10 windows Phone 7 windows Phone 8 Iphone