Problems with Gantt_ASP Ajax

In IE10 and IE11 the coordinate system is all of a sudden in float numbers and not in integer as in all other browsers.

The AjaxControlToolkit has a GetElementPosition call deep down in it Common namespace that does some error checking.

The error checking it does is partly to verify that the coordinates it gets is of type integer – in IE10 and IE11 the will not be integers. Hence exceptions.

The error checking is only in effect when you have the scriptmanager in debug mode.

So to fix the errors you must do this:

          <asp:ToolkitScriptManager ID="ToolkitScriptManager2" runat="server" 
CombineScripts="False" ScriptMode="Release"> <Scripts> <asp:ScriptReference Name="Common.Common.js" Assembly="AjaxControlToolkit" /> <asp:ScriptReference Name="Compat.DragDrop.DragDropScripts.js" Assembly="AjaxControlToolkit" /> <asp:ScriptReference Name="PlexityHideAjax.Gantt_AjaxBehavior.js" Assembly="PlexityHideAjax" /> </Scripts> </asp:ToolkitScriptManager>

 

Once you have done this – the moving of time items from client side works just fine once again.

Unwanted MouseMode

This article was written for phGantTimePackage VCL (Delphi XE5)

We got the question and a repeating sample to support that showed a scenario where the developer wanted to bring up a modal dialog in double click on a TimeItem.

All that is straight forward:

procedure TForm1.phGant1DblClickGantArea(theGant: TphGant_ComBasic;
  theDataEntity: TphDataEntity_GantTime);
var FrmModal:TFrmModal;
begin
  FrmModal:=TFrmModal.create(nil);
  try
    FrmModal.ShowModal;
  finally
    FrmModal.Free;
  end;
end;

 

The problem was that when the user did this double click and was in a resize hot-zone for the time item – the mouse mode Resize was entered and was still set when the modal dialog was closed.

My first response was to call the method MouseMoveModeCancel. This method is useful for programmatically end any current mouse mode.

However it turned out to not work in this case.

The reason for it not working was that after the DblClick event returns – VCL diretly fires the MouseDown once more – and this is where we actually entered the mouse mode.

In phGantTimePackage there is a way do dispatch a call to the windows message queue and this comes in handy here. The mechanism is called TDoLater and the solution looks like this:

 

procedure TForm1.phGant1DblClickGantArea(theGant: TphGant_ComBasic;
  theDataEntity: TphDataEntity_GantTime);
var FrmModal:TFrmModal;
begin
  FrmModal:=TFrmModal.create(nil);
  try
    FrmModal.ShowModal;
    TDoLater.DoLater(LeaveAnyUnintentionalMouseMoveMode,nil);
  finally
    FrmModal.Free;
  end;
end;

procedure TForm1.LeaveAnyUnintentionalMouseMoveMode(a:TObject);
begin
    phGant1.MouseMoveModeCancel;
end;

 

This way the defer the MouseMoveCancel to a time to directly after all the current messages that windows has on user interaction.

To use the TDoLater you need a uses for phDoLater.

Speed of WPF Gantt

It is easy to make the WPF Gantt slow, just bog it down with lots of time items that has lots of details (complex internal markup).

This article is about how to make the WPF Gantt really fast.

There are two important things to optimize:

1. The Gantt rows

2. The Time Items

The Gantt rows

The Gantt rows can be optimized by limiting their rendering to only show the ones on screen. This is handled by standard WPF Virtualization implemented by VirtualizingStackPanel.

Example:

If you have your Gantt Rows in a Items control (if it is a tree view then it is really one items control per node):

      <ItemsControl x:Name="_ItemsControl"
                    VirtualizingStackPanel.IsVirtualizing="True"
                    VirtualizingStackPanel.VirtualizationMode="Standard"
    ScrollViewer.CanContentScroll="True"

                    ItemTemplate="{StaticResource _GanttRowTemplate}"
                    ItemsSource="{Binding Path=Resources}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                    <ItemsControl.Template>
          <ControlTemplate>
              
            <Border HorizontalAlignment="Stretch"
                    BorderThickness="{TemplateBinding Border.BorderThickness}"
                    Padding="{TemplateBinding Control.Padding}"
                    BorderBrush="{TemplateBinding Border.BorderBrush}"
                    Background="{TemplateBinding Panel.Background}">
              <ScrollViewer x:Name="_ScrollViewer"
                            HorizontalAlignment="Stretch"
                            HorizontalContentAlignment="Stretch"
                            VerticalAlignment="Stretch"
                            VerticalContentAlignment="Stretch"
                            IsDeferredScrollingEnabled="True"
                            CanContentScroll="True"
                            Padding="{TemplateBinding Control.Padding}"
                            ScrollChanged="ScrollViewer_ScrollChanged"
                            Focusable="False">
                <ItemsPresenter HorizontalAlignment="Stretch"
                                VerticalAlignment="Stretch" />
              </ScrollViewer>
            </Border>
          </ControlTemplate>
        </ItemsControl.Template>
      </ItemsControl>

Doing this will stop WPF from wasting time on stuff that is not currently on screen. And WPF will create the GanttRows when scrolled into view.

The TimeItems

The Time items are not in a StackPanel so we need to use a different technique. We need a way to decide if a should be on screen or not.

We do this by implementing the event OnVirtualizeTimeItemCheck. In this event we get information about the bound object – that YOU know how to interpret – and the ability to say if it should be visible or not. We inform the Gantt that it should not be visible by setting e.SuggestedVisibility to Collapsed.

    void _GanttControl_OnVirtualizeTimeItemCheck(object sender, OnVirtualizeTimeItemCheckArgs e)
    {
      if (e.TimeItemObject is WpfApplication10.Task)
      { 
        var t=e.TimeItemObject as WpfApplication10.Task;
        if (t.Begin < e.VisibleAreaStop && t.End > e.VisibleAreaStart)
          e.SuggestedVisibility = System.Windows.Visibility.Visible;
        else
          e.SuggestedVisibility = System.Windows.Visibility.Collapsed;
      }
    }

The easiest way to decide if it should be on screen is like above: If the start is smaller than stop of view AND the stop is bigger than start of view. This covers the cases when time items are partly on screen, and when time items span the whole screen and beyond.

Summary

If you have large datasets like 1000 rows and 1000 time items per row – you really have to follow these guidelines. But you see good effects long before that.

Below 1 million Time items – Gantt still renders fast.

image

GTP.NET.SL 2.12.13

Added DateScaler WeekSpanFirstFormat and WeekSpanSecondFormat to allow for better control of WeekSpan presentation

New wpf performance sample added

Many improvements for performance
Added loaded animation

OnVirtualizeTimeItemCheck event

issues with userdraw not always getting measured correctly fixed.

Speed of WPF Gantt

It is easy to make the WPF Gantt slow, just bog it down with lots of time items that has lots of details (complex internal markup).

This article is about how to make the WPF Gantt really fast.

There are two important things to optimize:

1. The Gantt rows

2. The Time Items

The Gantt rows

The Gantt rows can be optimized by limiting their rendering to only show the ones on screen. This is handled by standard WPF Virtualization implemented by VirtualizingStackPanel.

Example:

If you have your Gantt Rows in a Items control (if it is a tree view then it is really one items control per node):

      <ItemsControl x:Name="_ItemsControl"
                    VirtualizingStackPanel.IsVirtualizing="True"
                    VirtualizingStackPanel.VirtualizationMode="Standard"
    ScrollViewer.CanContentScroll="True"

                    ItemTemplate="{StaticResource _GanttRowTemplate}"
                    ItemsSource="{Binding Path=Resources}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                    <ItemsControl.Template>
          <ControlTemplate>
              
            <Border HorizontalAlignment="Stretch"
                    BorderThickness="{TemplateBinding Border.BorderThickness}"
                    Padding="{TemplateBinding Control.Padding}"
                    BorderBrush="{TemplateBinding Border.BorderBrush}"
                    Background="{TemplateBinding Panel.Background}">
              <ScrollViewer x:Name="_ScrollViewer"
                            HorizontalAlignment="Stretch"
                            HorizontalContentAlignment="Stretch"
                            VerticalAlignment="Stretch"
                            VerticalContentAlignment="Stretch"
                            IsDeferredScrollingEnabled="True"
                            CanContentScroll="True"
                            Padding="{TemplateBinding Control.Padding}"
                            ScrollChanged="ScrollViewer_ScrollChanged"
                            Focusable="False">
                <ItemsPresenter HorizontalAlignment="Stretch"
                                VerticalAlignment="Stretch" />
              </ScrollViewer>
            </Border>
          </ControlTemplate>
        </ItemsControl.Template>
      </ItemsControl>

Doing this will stop WPF from wasting time on stuff that is not currently on screen. And WPF will create the GanttRows when scrolled into view.

The TimeItems

The Time items are not in a StackPanel so we need to use a different technique. We need a way to decide if a should be on screen or not.

We do this by implementing the event OnVirtualizeTimeItemCheck. In this event we get information about the bound object – that YOU know how to interpret – and the ability to say if it should be visible or not. We inform the Gantt that it should not be visible by setting e.SuggestedVisibility to Collapsed.

    void _GanttControl_OnVirtualizeTimeItemCheck(object sender, OnVirtualizeTimeItemCheckArgs e)
    {
      if (e.TimeItemObject is WpfApplication10.Task)
      { 
        var t=e.TimeItemObject as WpfApplication10.Task;
        if (t.Begin < e.VisibleAreaStop && t.End > e.VisibleAreaStart)
          e.SuggestedVisibility = System.Windows.Visibility.Visible;
        else
          e.SuggestedVisibility = System.Windows.Visibility.Collapsed;
      }
    }

The easiest way to decide if it should be on screen is like above: If the start is smaller than stop of view AND the stop is bigger than start of view. This covers the cases when time items are partly on screen, and when time items span the whole screen and beyond.

Summary

If you have large datasets like 1000 rows and 1000 time items per row – you really have to follow these guidelines. But you see good effects long before that.

Below 1 million Time items – Gantt still renders fast.

image