Thursday, July 22, 2010Collision detect of TimeItems in a Gantt chartThe GTP.NET will order the colliding time items in separate sub columns. The way this is done is fully automatic and the result will be something like this: But what if you would rather to have the Top time item at the bottom? One way is to re-order the SubColumn values after the collision detection is done. Go like this: 1: Private _CollisionDetectNeedUpdate As Boolean 2: 3: Private Sub OnCollionsDetect(ByVal aGantt As PlexityHide.GTP.Gantt, 4: ByVal e As PlexityHide.GTP.CollisionDetectEventArgs) 5: _CollisionDetectNeedUpdate = True 6: End Sub 7: 8: Private Sub Gantt1_OnTimeItemAreaPaintBackground(ByVal aOffscreenDraw As 9: PlexityHide.GTP.OffscreenDraw, ByVal e As PlexityHide.GTP.OffscreenDrawArgs) 10: Handles Gantt1.OnTimeItemAreaPaintBackground 11: If (_CollisionDetectNeedUpdate And 12: Gantt1.Grid.RootNodes.Count > 0 And Gantt1.MouseMoveKind = MouseMoveKind.none) Then 13: Dim gr As GanttRow = Gantt.GanttRowFromGridNode(Gantt1.Grid.RootNodes(0)) 14: 15: For i = 0 To gr.Layers(0).Count - 1 16: gr.Layers(0).Item(i).SubCol = 1 + gr.SubColumnsFromCollisionDetect - 17: gr.Layers(0).Item(i).SubCol 18: Next 19: _CollisionDetectNeedUpdate = False 20: End If 21: 22: End Sub And then you have a result that looks like this: Saturday, July 17, 2010Getting an Ajax Gantt chart set up properly with VisualStudio 2010You can use GTP.NET on the web and it supports Ajax to move around time items and scroll in time. Create a new ASP.NET project: The Gantt chart does not care from where your data comes, but since it needs data to display I need to go thru these steps in order to show anything at all. If you already have data you want to show, you might be better of just using that from the get-go. First we need to get hold of some data, so I add a new SqlServer database to the App_Data folder and create some tables and fields to hold my Gantt data: Start and Stop fields in the WorkItem table are of type DateTime. I then create a new typed DataSet (Do not like typed dataset? Any data is fine really, do it your way ) and drag on the tables and set up some relations: I added the Activity table twice, naming in SubActivity the second time, to hold a 2 level hierarchy in the gantt chart. On the Default.aspx page where I want to show my Gantt chart I drag on a ScriptManager and UpdatePanel onto the form. These are the standard components that provide the Ajax effect of partial postbacks. I also add the Gantt_ASP inside the UpdatePanel, and I add some buttons about the UpdatePanel. What also is important is to add the GTPImg.aspx page to the project – this page helps with Image rendering that the Gantt needs. You will find it under [program files]\plexityHide AB\GTP.NET 3.3.5.31\Assemblies\CLR35\asp.net. Make sure to add GTPImg.aspx and GTPImg.aspx.cs to the project.
If you run the project as it stands now you should be able to see the Gantt chart DateScaler and you should be able to panorate in time by grabbing and dragging it. You can also use the buttons to scale in or out to show less or more time: Now we will need to add some code to set up the databind: 1: private DataSet1 _appDataset; 2: protected void Page_Load(object sender, EventArgs e) 3: { 4: 5: 6: _appDataset = new DataSet1(); 7: 8: if (!Page.IsPostBack) 9: { 10: // Application load, Get values from database 11: FetchValuesFromStorage(); 12: 13: SaveThisSessionsDataInTheCacheForFasterRetrievelOnPostback(); 14: } 15: else 16: { 17: LoadDatasetFromCache(); 18: } 19: 20: 21: InitTheGantt(); 22: 23: 24: }
In this example I let the Gantt behave differently depending if this was a Postback or a new Page request. If it was a postback I get the data from the session cache to speed things up, and not from the database. The methods involved are : 1: private void FetchValuesFromStorage() 2: { 3: ConnectionStringSettingsCollection connections = 4: ConfigurationManager.ConnectionStrings; 5: using (SqlConnection connection = 6: new SqlConnection(connections["Database1ConnectionString"].ConnectionString)) 7: { 8: connection.Open(); 9: 10: SqlCommand command1 = new SqlCommand("SELECT * FROM Activity where parentActivity='{00000000-0000-0000-0000-000000000000}'", connection); 11: SqlDataReader reader = command1.ExecuteReader(); 12: _appDataset.Activity.Load(reader); 13: reader.Close(); 14: 15: SqlCommand command2 = new SqlCommand("SELECT * FROM Activity where parentActivity<>'{00000000-0000-0000-0000-000000000000}'", connection); 16: SqlDataReader reader2 = command2.ExecuteReader(); 17: _appDataset.SubActivity.Load(reader2); 18: reader2.Close(); 19: 20: SqlCommand command3 = new SqlCommand("SELECT * FROM WorkItem ", connection); 21: SqlDataReader reader3 = command3.ExecuteReader(); 22: _appDataset.WorkItem.Load(reader3); 23: reader3.Close(); 24: 25: } 26: } And also these two methods :
1: private void LoadDatasetFromCache() 2: { 3: string data=this.Session["ganttSessionData"] as string; 4: if (data != null) 5: { 6: StringReader stre = new StringReader(data); 7: _appDataset.ReadXml(stre); 8: } 9: else 10: FetchValuesFromStorage(); 11: } 12: 13: private void SaveThisSessionsDataInTheCacheForFasterRetrievelOnPostback() 14: { 15: StringWriter xwri = new StringWriter(); 16: _appDataset.WriteXml(xwri, XmlWriteMode.IgnoreSchema); 17: this.Session["ganttSessionData"] = xwri.ToString(); 18: } Minimum logic there really – just to show one way of many possible way to handle your data in an ASP.NET application. Moving on with what this post is really about: a Live Ajax Gantt with Data. To initialize the databind I have implemented this: 1: private void InitTheGantt() 2: { 3: Gantt_ASP1.Gantt.Grid.Columns.Clear(); 4: Gantt_ASP1.Gantt.Grid.Columns.Add(new GridColumn() { Title = "Activity", Tree=true, DataSourceColumn="Name" }); 5:
7: Gantt_ASP1.Gantt.OnEachListItemWhenDataBound_TimeItem += 8: 9: InitLayouts(); 10: 11: Gantt_ASP1.Gantt.GridProperties.RootNodes_DataSource = _appDataset.Activity; 12: 13: 14: 15: 16: if (!Page.IsPostBack) 17: { 18: // fold out tree nodes the first time we show 19: for (int i = 0; i < Gantt_ASP1.Gantt.Grid.RootNodes.Count; i++) 20: { 21: GridNode gn = Gantt_ASP1.Gantt.Grid.RootNodes[i]; 22: gn.Expanded = true; 23: Gantt_ASP1.SetExpandedStatusForGridNode(gn, true); 24: } 25: } 26: } As you see on row 11 the root activities are handed to the Gantt_ASP1.Gantt.GridProperties.RootNodes_DataSource and that is the key row in that code. But equally important is the event handlers that are set up for “OnEachListItemWhenDataBound_”, there are two of these one for GridNodes and one for TimeItem. Theses event handlers will allow us to set up further databind per item in the first databound collection (root activities). 1: void Gantt_OnEachListItemWhenDataBound_GridNode(object GTPComponent, EachListItemWhenDataBoundArgs e) 2: { 3: GridNode gn = (e.GTPObject as GridNode); 4: if (gn.SubLevel == 0) 5: { 6: // root item 7: DataSet1.ActivityRow row=(e.CurrencyManagerListItem as DataRowView).Row as DataSet1.ActivityRow; 8: gn.SubNodes_DataSource=row.GetChildRows(_appDataset.Relations["FK_Activity_SubActivity"]); 9: } 10: else if (gn.SubLevel == 1) 11: { 12: // sub item 13: DataSet1.SubActivityRow row = e.CurrencyManagerListItem as DataSet1.SubActivityRow; 14: Layer l = GanttRow.FromGridNode(gn).Layers[0]; 15: l.NameInDS_Identity = "id"; 16: l.NameInDS_Start = "Start"; 17: l.NameInDS_Stop = "Stop"; 18: l.TimeItemLayout = "MyTimeItemLayout"; 19: l.DataSource = row.GetChildRows(_appDataset.Relations["FK_SubActivity_WorkItem"]); 20: } 21: 22: } 23: 24: void Gantt_OnEachListItemWhenDataBound_TimeItem(object GTPComponent, EachListItemWhenDataBoundArgs e) 25: { 26: // This is a good place to apply data dependant styling per time item 27: } 28: In the code above you will see that I do different stuff depending on GridNode.SubLevel – this is a convenient way to know on what level I am getting this call. On Level zero – root level – I set up the databind of the Sub-activity-nodes. And on Level 1 the sub-activities – I set up databind for TimeItems in a TimeItemLayer. I also want to mention a few words about CellLayouts and TimeItemLayouts – these are objects that control the rendering look and feel. In this sample I have chosen to define them in code like this: 1: private void InitLayouts() 2: { 3: CellLayout cl=Gantt_ASP1.Gantt.Grid.CellLayouts.GetFromName("MyCellLayout"); 4: cl.Font=new System.Drawing.Font("Geneva",18); 5: cl.MinHeight = 30; 6: 7: Gantt_ASP1.Gantt.Grid.Columns[0].LayoutName = "MyCellLayout"; 8: 9: TimeItemLayout til=Gantt_ASP1.Gantt.TimeItemLayouts.GetFromName("MyTimeItemLayout"); 10: til.TimeItemStyle = TimeItemStyle.Glossy; 11: til.BrushKind = BrushKind.GradientHorizontal; 12: til.Color = Color.Pink; 13: til.GradientColor = Color.Red; 14: 15: 16: } Running the sample will give you this ; depending on your data of course: The implementation behind the buttons are all about data and not Gantt specific at all: 1: protected void ButtonAddRoot_Click(object sender, EventArgs e) 2: { 3: 4: _appDataset.Activity.AddActivityRow(Guid.NewGuid(), "Activity", "", Guid.Empty); 5: } 6: 7: protected void ButtonAddSub_Click(object sender, EventArgs e) 8: { 9: if (_appDataset.Activity.Count > 0) 10: { 11: _appDataset.SubActivity.AddSubActivityRow(Guid.NewGuid(), "SubAct", "", _appDataset.Activity[0]); 12: } 13: } 14: 15: protected void ButtonAddWork_Click(object sender, EventArgs e) 16: { 17: if (_appDataset.Activity.Count > 0 && _appDataset.Activity[0].GetSubActivityRows().Count()>0) 18: { 19: DataSet1.SubActivityRow row = _appDataset.Activity[0].GetSubActivityRows()[0];
21: } 22: } 23: 24: protected void ButtonSave_Click(object sender, EventArgs e) 25: { 26: DataSet1TableAdapters.ActivityTableAdapter ta1=new DataSet1TableAdapters.ActivityTableAdapter(); 27: DataSet1TableAdapters.SubActivityTableAdapter ta2 = new DataSet1TableAdapters.SubActivityTableAdapter(); 28: DataSet1TableAdapters.WorkItemTableAdapter ta3 = new DataSet1TableAdapters.WorkItemTableAdapter(); 29: 30: ta1.Update(_appDataset.Activity); 31: ta2.Update(_appDataset.SubActivity); 32: ta3.Update(_appDataset.WorkItem); 33: 34: _appDataset.AcceptChanges(); 35: } The One bit of important Gantt related code left is this event implementation: 1: 2: protected void Gantt_ASP1_OnClientSideChangesApplied(object sender, EventArgs e) 3: { 4: SaveThisSessionsDataInTheCacheForFasterRetrievelOnPostback(); 5: } 6: In the event above that is fired by the Gantt_ASP when it is done with applying the users change to the data, I save the data to the cache. This is the brilliant thing about databind – Load and save code need not be bothered with the components that aided in presenting or changing the data. Ok Done. Comments are appreciated. Sample can be downloaded from here http://www.plexityhide.com/pub/VS2010_Gantt_ASP_WebApplication1.zip Subscribe to Posts [Atom] |
|
