Infragistics XamGantt Control - Using Entity Framework Code First to Persist Projects in a Database

[Infragistics] Mihail Mateev / Sunday, October 21, 2012

Infragistics Gantt Control helps to create own project planning applications, that can contain most of the Microsoft Project features. One of the most common scenario is to store project data in a database (Microsoft SQL Server or other).

Developers are always looking for the easiest way for the implementation of the solution. In this case, it is reasonable to use an ORM framework. This article will demonstrate how to use Entity Framework with Code First approach.

Entity Framework 4.x and 5.x contains both the Code First approach and the DbContext API. This API provides a more productive surface for working with the Entity Framework and can be used with the Code First, Database First, and Model First approaches.

 

 

Entity Framework Code First allows you to create data model classes prior to creating a database. When you run the application for the first time, a database is created for you on the fly based on the classes you created. Database initializers allow you to decide a strategy of database creation and seed data generation. Code First provides its own set of database initializer classes and also allows you to create your own. This approach is a very convenient when you need to distribute your  application. Customers need only to have a SQL Server installed.

 

Let’s create a simple WPF application and add XamGantt control instance inside it. You can start with the samples from my previous post: “How to Start Using Infragistics XamGantt Control” , "Data Exchange with Infragistics XamGantt Control - Using XML (Part 1)" or “Data Exchange with Infragistics XamGantt Control - Import/Export to Microsoft Project (Part 2)”. Add buttons to call import from and export to project. If you want to create a Silverlight simple you can use the same snippets, mentioned for WPF application.

 

 

What you need in advance:

 

Create the Model

Let’s define a very simple model using classes. You can start with the models samples from my previous post “Data Exchange with Infragistics XamGantt Control - Import/Export to Microsoft Project (Part 2)”

 

Create a Context

The simplest way to start using the classes for data access is to define a context that derives from System.Data.Entity.DbContext and exposes a typed DbSet<TEntity> for each class in my model.

 

You need to add a reference to the EF assembly

  • Project -> Add Reference…
  • Select the “.NET” tab
  • Select “EntityFramework” from the list
  • Click “OK”

You’ll also need a reference to the System.Data.Entity assembly:

 

  • Project -> Add Reference…
  • Select the “.NET” tab
  • Select “System.Data.Entity” from the list
  • Click “OK”

 

Let’s create a derived context:

Add a derived context below the existing classes that we’ve defined. You need to inherit System.Data.Entity.DbContext. By default when you run the application your database should be created if it doesn’t exist.

 

   1: public class DbProjectViewModel : DbContext, INotifyPropertyChanged
   2:  {
   3:  
   4:      #region SelectedTask
   5:      private TaskModel _selectedTask;
   6:      public TaskModel SelectedTask
   7:      {
   8:          get
   9:          {
  10:              return _selectedTask;
  11:          }
  12:          set
  13:          {
  14:              if (value != null)
  15:              {
  16:                  _selectedTask = value;
  17:              }
  18:  
  19:              NotifyPropertyChanged("SelectedTask");
  20:          }
  21:      }
  22:      #endregion SelectedTask
  23:  
  24:      #region Tasks
  25:      private DbSet<TaskModel> _tasks;
  26:      public DbSet<TaskModel> Tasks {get; set;}
  27:      
  28:      #endregion //Tasks
  29:  
  30:      #region NotifyPropertyChanged
  31:      public event PropertyChangedEventHandler PropertyChanged;
  32:      protected void NotifyPropertyChanged(String info)
  33:      {
  34:          if (PropertyChanged != null)
  35:          {
  36:              PropertyChanged(this, new PropertyChangedEventArgs(info));
  37:          }
  38:      }
  39:      #endregion NotifyPropertyChanged
  40:  
  41:  }

 

If you do not want to generate a database at all, as you already have a database,  you can add the following to the DbProjectViewModel constructor:

   1: #region Constructor
   2: public DbProjectViewModel()
   3:    : base()
   4: {
   5:    Database.SetInitializer<DbProjectViewModel>(null);
   6: }
   7:  
   8: public DbProjectViewModel(String connection)
   9:    : base(connection)
  10: {
  11:    Database.SetInitializer<DbProjectViewModel>(null);
  12: }
  13: #endregion Constructor

 

Your task model will be the same as in previous posts.

   1: public class TaskModel : ObservableModel
   2:  {
   3:      public TaskModel():base()
   4:      {
   5:      }
   6:  
   7:      public TaskModel(TaskModel oldTask)
   8:          : base()
   9:      {
  10:          ConstraintType = oldTask.ConstraintType;
  11:          ConstraintDate = oldTask.ConstraintDate;
  12:          Id = oldTask.Id;
  13:          DeadlineDate = oldTask.DeadlineDate;
  14:          TaskName = oldTask.TaskName;
  15:          Index = oldTask.Index;
  16:          Id = oldTask.Id;
  17:          DurationTicks = oldTask.DurationTicks;
  18:          Start = oldTask.Start;
  19:          IsMilestone = oldTask.IsMilestone;
  20:          IsInProgress = oldTask.IsInProgress;            
  21:      }
  22:  
  23:  
  24:      #region ConstraintType
  25:      private ProjectTaskConstraintType _constraintType;
  26:      public ProjectTaskConstraintType ConstraintType
  27:      {
  28:          get
  29:          {
  30:              return _constraintType;
  31:          }
  32:          set
  33:          {
  34:              if (_constraintType != value)
  35:              {
  36:                  _constraintType = value;
  37:                  this.NotifyPropertyChanged("ConstraintType");
  38:              }
  39:          }
  40:      }
  41:      #endregion ConstraintType
  42:  
  43:      #region ConstraintDate
  44:      private DateTime? _constraintDate;
  45:      public DateTime? ConstraintDate
  46:      {
  47:          get
  48:          {
  49:              return _constraintDate;
  50:          }
  51:          set
  52:          {
  53:              if (_constraintDate != value)
  54:              {
  55:                  _constraintDate = value;
  56:                  this.NotifyPropertyChanged("ConstraintDate");
  57:              }
  58:          }
  59:      }
  60:      #endregion ConstraintDate
  61:  
  62:      #region DeadlineDate
  63:      private DateTime? _deadlineDate;
  64:      public DateTime? DeadlineDate
  65:      {
  66:          get
  67:          {
  68:              return _deadlineDate;
  69:          }
  70:          set
  71:          {
  72:              if (_deadlineDate != value)
  73:              {
  74:                  _deadlineDate = value;
  75:                  this.NotifyPropertyChanged("DeadlineDate");
  76:              }
  77:          }
  78:      }
  79:      #endregion //DeadlineDate
  80:  
  81:      #region DurationFormat
  82:      private ProjectDurationFormat _durationFormat;
  83:      public ProjectDurationFormat DurationFormat
  84:      {
  85:          get
  86:          {
  87:              return _durationFormat;
  88:          }
  89:          set
  90:          {
  91:              if (_durationFormat != value)
  92:              {
  93:                  _durationFormat = value;
  94:                  this.NotifyPropertyChanged("DurationFormat");
  95:              }
  96:          }
  97:      }
  98:      #endregion DurationFormat
  99:  
 100:      #region Duration
 101:      private TimeSpan _duration;
 102:  
 103:      //[XmlIgnore]
 104:      [NotMapped]
 105:      public TimeSpan Duration
 106:      {
 107:          get
 108:          {
 109:              return _duration;
 110:          }
 111:          set
 112:          {
 113:              if (_duration != value)
 114:              {
 115:                  _duration = value;
 116:                  this.NotifyPropertyChanged("Duration");
 117:              }
 118:          }
 119:      }
 120:      #endregion Duration
 121:  
 122:      #region DurationTicks
 123:  
 124:      // Pretend property for serialization
 125:      [XmlElement("Duration")]
 126:      public long DurationTicks
 127:      {
 128:          get { return _duration.Ticks; }
 129:          set { _duration = new TimeSpan(value); }
 130:      }
 131:  
 132:      #endregion DurationTicks
 133:  
 134:      #region Id
 135:      private Int32 _id;
 136:  
 137:  
 138:      public Int32 Id
 139:      {
 140:          get
 141:          {
 142:              return _id;
 143:          }
 144:          set
 145:          {
 146:              if (_id != value)
 147:              {
 148:                  _id = value;
 149:                  this.NotifyPropertyChanged("Id");
 150:              }
 151:          }
 152:      }
 153:      #endregion Id
 154:  
 155:      #region Index
 156:      private TimeSpan _index;
 157:  
 158:  
 159:      public TimeSpan Index
 160:      {
 161:          get
 162:          {
 163:              return _index;
 164:          }
 165:          set
 166:          {
 167:              if (_index != value)
 168:              {
 169:                  _index = value;
 170:                  this.NotifyPropertyChanged("Index");
 171:              }
 172:          }
 173:      }
 174:      #endregion Index
 175:  
 176:      #region IsMilestone
 177:      private bool _isMilestone = false;
 178:      public bool IsMilestone
 179:      {
 180:          get
 181:          {
 182:              return _isMilestone;
 183:          }
 184:          set
 185:          {
 186:              if (_isMilestone != value)
 187:              {
 188:                  _isMilestone = value;
 189:                  this.NotifyPropertyChanged("IsMilestone");
 190:              }
 191:          }
 192:      }
 193:      #endregion IsMilestone
 194:  
 195:      #region IsInProgress
 196:      private bool _isInProgress = true;
 197:      public bool IsInProgress
 198:      {
 199:          get
 200:          {
 201:              return _isInProgress;
 202:          }
 203:          set
 204:          {
 205:              if (_isInProgress != value)
 206:              {
 207:                  _isInProgress = value;
 208:                  this.NotifyPropertyChanged("IsInProgress");
 209:              }
 210:          }
 211:      }
 212:      #endregion IsInProgress
 213:  
 214:      #region IsUndetermined
 215:      private bool _isUndetermined = false;
 216:      public bool IsUndetermined
 217:      {
 218:          get
 219:          {
 220:              return _isUndetermined;
 221:          }
 222:          set
 223:          {
 224:              if (_isUndetermined != value)
 225:              {
 226:                  _isUndetermined = value;
 227:                  this.NotifyPropertyChanged("IsUndetermined");
 228:              }
 229:          }
 230:      }
 231:      #endregion //IsUndetermined
 232:  
 233:      #region Predecessors
 234:      private string _predecesors;
 235:      public string Predecessors
 236:      {
 237:          get { return _predecesors; }
 238:          set
 239:          {
 240:              if (value != _predecesors)
 241:              {
 242:                  _predecesors = value;
 243:                  this.NotifyPropertyChanged("Predecessors");
 244:              }
 245:          }
 246:      }
 247:      #endregion //Predecessors
 248:  
 249:      #region PredecessorsIdText
 250:      private string _predecessorsIdText;
 251:      public string PredecessorsIdText
 252:      {
 253:          get { return _predecessorsIdText; }
 254:          set
 255:          {
 256:              if (value != _predecessorsIdText)
 257:              {
 258:                  _predecessorsIdText = value;
 259:                  this.NotifyPropertyChanged("PredecessorsIdText");
 260:              }
 261:          }
 262:      }
 263:      #endregion //PredecessorsIdText
 264:  
 265:      #region Successors
 266:      private string _successors;
 267:      public string Successors
 268:      {
 269:          get { return _successors; }
 270:          set
 271:          {
 272:              if (value != _successors)
 273:              {
 274:                  _successors = value;
 275:                  this.NotifyPropertyChanged("Successors");
 276:              }
 277:          }
 278:      }
 279:      #endregion //Successors
 280:  
 281:      #region Start
 282:      private DateTime? _start;
 283:      public DateTime? Start
 284:      {
 285:          get
 286:          {
 287:              return _start;
 288:          }
 289:          set
 290:          {
 291:              if (_start != value)
 292:              {
 293:                  _start = value;
 294:                  this.NotifyPropertyChanged("Start");
 295:              }
 296:          }
 297:      }
 298:      #endregion Start
 299:  
 300:      #region Finish
 301:      private DateTime? _finish;
 302:      public DateTime? Finish
 303:      {
 304:          get
 305:          {
 306:              return _finish;
 307:          }
 308:          set
 309:          {
 310:              if (_finish != value)
 311:              {
 312:                  _finish = value;
 313:                  this.NotifyPropertyChanged("Finish");
 314:              }
 315:          }
 316:      }
 317:      #endregion Finish
 318:  
 319:  
 320:      #region TaskID
 321:      private string _taskId;
 322:      public string TaskID
 323:      {
 324:          get
 325:          {
 326:              return _taskId;
 327:          }
 328:          set
 329:          {
 330:              if (_taskId != value)
 331:              {
 332:                  _taskId = value;
 333:                  this.NotifyPropertyChanged("TaskID");
 334:              }
 335:          }
 336:      }
 337:      #endregion TaskID
 338:  
 339:      #region Tasks
 340:      private string _tasks;
 341:      public string Tasks
 342:      {
 343:          get
 344:          {
 345:              return _tasks;
 346:          }
 347:          set
 348:          {
 349:              if (_tasks != value)
 350:              {
 351:                  _tasks = value;
 352:                  this.NotifyPropertyChanged("Tasks");
 353:              }
 354:          }
 355:      }
 356:      #endregion Tasks
 357:  
 358:      #region TaskName
 359:      private string _name;
 360:      [Key]
 361:      public string TaskName
 362:      {
 363:          get
 364:          {
 365:              return _name;
 366:          }
 367:          set
 368:          {
 369:              if (_name != value)
 370:              {
 371:                  _name = value;
 372:                  this.NotifyPropertyChanged("TaskName");
 373:              }
 374:          }
 375:      }
 376:      #endregion TaskName
 377:  
 378:      #region ResourceName
 379:      private string _resourceName;
 380:      public string ResourceName
 381:      {
 382:          get
 383:          {
 384:              return _resourceName;
 385:          }
 386:          set
 387:          {
 388:              if (_resourceName != value)
 389:              {
 390:                  _resourceName = value;
 391:                  this.NotifyPropertyChanged("ResourceName");
 392:              }
 393:          }
 394:      }
 395:      #endregion ResourceName
 396:  
 397:  }
 398: }

 

Add an instance of your derived context in the sample application. You can use it in the same way like you will bind XamGantt control to a model with custom tasks, demonstrated in the blog "How to Start Using Infragistics XamGantt Control".

   1: <Grid.Resources>
   2:     <models:DbProjectViewModel x:Key="viewmodel" />
   3: </Grid.Resources>

 

You need to cerate a ListBackedProject and a Gantt control instances.  ListBackedProject.TaskItemsSource is bound to Tasks.Local that returns ObservableCollection<TaskModel>.

   1: <ig:ListBackedProject x:Name="dataProvider" TaskItemsSource="{Binding Tasks.Local}">
   2:      <ig:ListBackedProject.TaskPropertyMappings>
   3:          <ig:ProjectTaskPropertyMappingCollection UseDefaultMappings="True">
   4:  
   5:              <!--Mandatory mappings -->
   6:              <ig:ProjectTaskPropertyMapping TaskProperty="DataItemId" 
   7:                                 DataObjectProperty="TaskID" />
   8:              <ig:ProjectTaskPropertyMapping TaskProperty="ConstraintType" 
   9:                                 DataObjectProperty="ConstraintType" />
  10:              <ig:ProjectTaskPropertyMapping TaskProperty="ConstraintDate" 
  11:                                 DataObjectProperty="ConstraintDate" />
  12:              <ig:ProjectTaskPropertyMapping TaskProperty="DurationFormat" 
  13:                                 DataObjectProperty="DurationFormat" />
  14:              <ig:ProjectTaskPropertyMapping TaskProperty="Tasks" 
  15:                                 DataObjectProperty="Tasks" />
  16:              <!--End of mandatory mappings -->
  17:  
  18:              <!--Manual tasks mapping -->
  19:  
  20:              <ig:ProjectTaskPropertyMapping TaskProperty="IsManual" 
  21:                                 DataObjectProperty="IsUndetermined" />
  22:  
  23:              <!--End of maual tasks mapping -->
  24:  
  25:              <!--Task link mapping -->
  26:  
  27:              <ig:ProjectTaskPropertyMapping TaskProperty="Predecessors" DataObjectProperty="Predecessors" />
  28:  
  29:              <!--End of task link mapping -->
  30:  
  31:              <!--Other tasks mapping -->
  32:  
  33:              <ig:ProjectTaskPropertyMapping TaskProperty="IsActive" 
  34:                                 DataObjectProperty="IsInProgress" />
  35:  
  36:              <ig:ProjectTaskPropertyMapping TaskProperty="Finish" 
  37:                                         DataObjectProperty="Finish" />
  38:  
  39:              <ig:ProjectTaskPropertyMapping TaskProperty="DurationFormat" 
  40:                                         DataObjectProperty="DurationFormat" />                               
  41:  
  42:  
  43:              <ig:ProjectTaskPropertyMapping TaskProperty="TaskName" 
  44:                                 DataObjectProperty="TaskName" />
  45:  
  46:              <ig:ProjectTaskPropertyMapping TaskProperty="Start" 
  47:                                 DataObjectProperty="Start" />
  48:  
  49:  
  50:              <ig:ProjectTaskPropertyMapping TaskProperty="Duration" 
  51:                                 DataObjectProperty="Duration" />
  52:  
  53:  
  54:              <ig:ProjectTaskPropertyMapping TaskProperty="IsMilestone" 
  55:                                 DataObjectProperty="IsMilestone" />
  56:  
  57:              <ig:ProjectTaskPropertyMapping TaskProperty="Deadline" 
  58:                                 DataObjectProperty="DeadlineDate" />
  59:  
  60:              <!--End of other tasks mapping -->
  61:  
  62:  
  63:          </ig:ProjectTaskPropertyMappingCollection>
  64:      </ig:ListBackedProject.TaskPropertyMappings>
  65:      <ig:ListBackedProject.TaskSettings>
  66:          <ig:ProjectTaskSettings AllowChangeIndentation="True" 
  67:                      AllowDelete="True"
  68:                      AllowDragDeadline="True"
  69:                      AllowDragMilestone="True"
  70:                      AllowDragPercentComplete="True"
  71:                      AllowDragSummary="Always"
  72:                      AllowDragTask="True"                              
  73:                      AllowInsert="True" 
  74:                      AllowResizeSummary="Always"                                        
  75:                      AllowResizeTask="True"/>
  76:      </ig:ListBackedProject.TaskSettings>
  77:  
  78:  </ig:ListBackedProject>

 

   1: <ig:XamGantt Margin="10" x:Name="xamGantt" VerticalAlignment="Top" ActiveCellChanged="xamGantt_ActiveCellChanged"  Project="{Binding ElementName=dataProvider}">
   2:                        <ig:XamGantt.DefaultColumnSettings>
   3:                            <ig:ProjectColumnSettings AllowHide="True"
   4:                                          AllowMove="True"
   5:                                          AllowResize="True"
   6:                                          AllowShow="True"  />
   7:                        </ig:XamGantt.DefaultColumnSettings>
   8:                    </ig:XamGantt>

 

 

Save project to database

 

Let’s first try to save Gantt project to Microsoft SQL Server database. Create a project with tasks in your XamGantt appellation.

 

 

To be possible to save tasks with correct data about links you need to add an additional code – here it is implemented in UpdatePredecessorsIdText method.

   1: #region btnSave_Click
   2: private void btnSave_Click(object sender, RoutedEventArgs e)
   3: {
   4:         var model = this.Root.Resources["viewmodel"] as DbProjectViewModel;
   5:         if (model != null)
   6:         {
   7:             UpdatePredecessorsIdText(xamGantt);
   8:             SaveGanttProject(model);
   9:         }
  10: }
  11: #endregion btnSave_Click

 

Unlike previous blogs here should keep not only information about PredecessorsIdText property but also information about the task   sequence number is stored in a property named id. Add this property in your custom task model.

When you save and read the model tasks could not be saved/read in the proper number. This property will keep the original task sequence in the Infragistics Gantt project.

 

   1: #region Id
   2: private Int32 _id;
   3:  
   4: public Int32 Id
   5: {
   6:     get
   7:     {
   8:         return _id;
   9:     }
  10:     set
  11:     {
  12:         if (_id != value)
  13:         {
  14:             _id = value;
  15:             this.NotifyPropertyChanged("Id");
  16:         }
  17:     }
  18: }
  19: #endregion Id

 

Add in the UpdatePredecessorsIdText method code that copy the value from the readonly property ProjectTask.Id to your custom tasks before to save the model.

   1: #region UpdatePredecessorsIdText
   2: private void UpdatePredecessorsIdText(XamGantt gantt)
   3: {
   4:     foreach (ProjectTask task in gantt.Project.RootTask.Tasks)
   5:     {
   6:         var customTask = task.DataItem as TaskModel;
   7:         customTask.PredecessorsIdText = task.PredecessorsIdText;
   8:         customTask.Id = task.Id;
   9:     }
  10: }
  11: #endregion UpdatePredecessorsIdText

 

The real save to your database is a very simple just call SaveChanges() method of your derived context.

   1: #region SaveGanttProject
   2: private void SaveGanttProject(DbProjectViewModel model)
   3: {
   4:     model.SaveChanges();
   5: }
   6: #endregion SaveGanttProject

 

Database settings

Connection strings

You can tell DbContext to use this connection by passing the connection string name to the DbContext constructor. For example:

   1: public class DbProjectViewModel : DbContext, INotifyPropertyChanged
   2: {
   3:         #region Constructor
   4:         public DbProjectViewModel()
   5:             : base("name=Northwind_Entities")
   6:         {
   7:             Database.SetInitializer<DbProjectViewModel>(null);
   8:         }
   9:         //implementation...
  10: }

 

Code First Default Connection Factory

Connection strings go in the standard connectionStrings element and do not require the new entityFramework section.

This configuration section allows you to specify a default connection factory that Code First should use to locate a database to use for a context. The default connection factory is only used when no connection string has been added to the configuration file for a context.

In the sample application is used the approach when you have no connection string and use default connection factory settings.

When you use default connection factory without changes you will use a .\SQLEXPRESS instance.

Where’s My Data?

DbContext by convention created a database for you on localhost\SQLEXPRESS. The database is named after the fully qualified name of your derived context, in our case that is “WPfIgGantImportExport.Models.DbProjectViewModel”. We’ll look at ways to change this later in the walkthrough.

 

  

 

   1: <entityFramework>
   2:   <defaultConnectionFactory 
   3:         type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
   4: </entityFramework>

 

If you don’t set a default connection factory, Code First uses the SqlConnectionFactory, pointing to .\SQLEXPRESS.SqlConnectionFactory also has a constructor that allows you to override parts of the connection string. If you want to use a SQL Server instance other than .\SQLEXPRESS you can use this constructor to set the server.

   1: <entityFramework>
   2:   <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
   3:     <parameters>
   4:       <parameter value="Data Source=(local);integrated security=True;multipleactiveresultsets=True"/>
   5:     </parameters>
   6:   </defaultConnectionFactory>
   7: </entityFramework>

 

Entity Framework Code First now uses local Microsoft SQL Server 2012 instance.

 

 

 

Read project from database

 

Write a LINQ expression that gets your tasks from data base. place this code in a separate method, named in this sample “ReadGanttProject”.

   1: #region btnRead_Click
   2:  private void btnRead_Click(object sender, RoutedEventArgs e)
   3:  {
   4:          var model = this.Root.Resources["viewmodel"] as DbProjectViewModel;
   5:          if (model != null)
   6:          {
   7:              ReadGanttProject(ref model);
   8:          }
   9:  }
  10:  #endregion btnRead_Click

 

Get all tasks in a separate list, ordered by Id. Clear the tasks from your model and add tasks from your list one by one from the smallest Id to the largest.

   1: #region ReadGanttProject
   2: private void ReadGanttProject(ref DbProjectViewModel model)
   3: {
   4:  
   5:     var mm = model.Tasks.Where(a => a.TaskName != "").OrderBy(a=>a.Id).ToList();
   6:     
   7:     foreach (var entity in model.Tasks)
   8:     {
   9:         model.Tasks.Remove(entity);
  10:     }
  11:  
  12:     //Clear the read tasks
  13:     model.SaveChanges();
  14:  
  15:     xamGantt.Project.RootTask.Tasks.Clear();
  16:  
  17:     for (int i = 0; i < mm.Count(); i++)
  18:     {
  19:         model.Tasks.Add(mm[i]);
  20:     }
  21:  
  22:     foreach (ProjectTask prTask in xamGantt.Project.RootTask.Tasks)
  23:     {
  24:         var taskModel = prTask.DataItem as TaskModel;
  25:         prTask.PredecessorsIdText = taskModel.PredecessorsIdText;
  26:     }
  27:  
  28:     //Update database again 
  29:     model.SaveChanges();
  30:  
  31: }
  32: #endregion ReadGanttProject

 

Now you have all tasks from database in your XamGantt application.

 

Try to delete a specified  task, add a new task and save the project. Database is updated without any issues.

  

 

The ListBackedProject.TaskItemsSource is not bound Tasks (Tasks is from DbSet type and doesn’t support property changed notifications) . It is bound to Tasks.Local that returns ObservableCollection<TaskModel>.

<ig:ListBackedProject x:Name="dataProvider" TaskItemsSource="{Binding Tasks.Local}">

 

In this case all property changed notifications updates the tasks inside the XamGantt control.

 

 

Conclusions

It is very easy to a database (Microsoft SQL Server or other to persist your project data from an application with an Infragistics XamGantt control. This scenario could be used for multi-user systems for project management and planning. The sample application could be modified with a few small changes to support many different projects as a real life application.

Using Infragistics NetAdvantage for XAML (WPF and Silverlight) Vol. 12.2 you can create software for project-management teams providing the benefit from the next NetAdvantage updates of and features .

Follow news from Infragistics for more information about new Infragistics products.

Source code is available here. Sample project is created with Visual Studio 2012, but you can use NetAdvantage 12.2 controls with both: Visual Studio 2012 and Visual Studio 2012. As always, you can follow us on Twitter @mihailmateev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!