Monday, December 17, 2018

How to modify sub-report location using Crystal Reports libraries in C#

I have some reports with subreports that point to invalid paths (due to some subfolders renaming). I needed to modify these subreports paths in c#.  I browsed the internet; But all what I found is unresolved questions. like:
https://archive.sap.com/discussions/thread/3903898
and
https://social.msdn.microsoft.com/Forums/en-US/e116daac-7c2e-4fbc-a833-8414098a3cf3/cr-changing-subreport-path-at-runtime-or-to-relative-path-at-design-time?forum=vscrystalreports

Finally I came up with a solution. Unfortunately above threads were locked so I had to post it here .  Hopefully someone can benefit from it.

    var aReportDocument = new CrystalDecisions.CrystalReports.Engine.ReportDocument();
 
    // Fill the repot path
    aReportDocument.Load(rootPath);
 
    try
    {
        var aRCD = aReportDocument.ReportClientDocument;
 
        StringBuilder sb = new StringBuilder();
        var reportDefController = aRCD.ReportDefController;
        var rptObjs = reportDefController.ReportObjectController.GetAllReportObjects();
        foreach (CrystalDecisions.ReportAppServer.ReportDefModel.ReportObject rptObj in rptObjs)
        {
            // look for sub report object and display info
            if (rptObj.Kind == CrystalDecisions.ReportAppServer.ReportDefModel.CrReportObjectKindEnum.crReportObjectKindSubreport)
            {
                var subRptObjI = rptObj as CrystalDecisions.ReportAppServer.ReportDefModel.ISCRSubreportObject;
                var location = subRptObjI.SubreportLocation;
                if (location.Contains(@"d:\old invalid path\"))
                {
                    string newPortion = @"C:\new valid path\";
                    string newLocation = location.Replace(@"d:\old invalid path\"newPortion);
                                
                    sb.AppendLine("\tName : '" + subRptObjI.Name + "' location '" + subRptObjI.SubreportLocation + "'");
 
                    var newClone = (CrystalDecisions.ReportAppServer.ReportDefModel.ISCRSubreportObject)subRptObjI.Clone(true);
                    newClone.SubreportLocation = newLocation;
                    reportDefController.ReportObjectController.Modify(rptObjnewClone);
                }
            }
 
        }
        if (sb.Length > 0)
        {
            aReportDocument.SaveAs(newPath);
            sb.AppendLine("Saved successfully : " + newPath);
            Console.Write(sb.ToString());
        }
 
    }
    catch (Exception exc)
    {
        Console.WriteLine(exc.Message);
    }
 
    aReportDocument.Close();

Sunday, March 22, 2015

Panels behavior inside scrollable panels

In a previous post I talked about a problem that exist in WPF that happens when you display a control that can expand with its content needs and can have its own scrollbars inside a ScrollViewer.

My suggested design had a lot of limitations and restrictions. And today, I would like to revisit the problem analyze it, figure out generated issues and see if we can find a solution to resolve it:

In order to do this analysis we need first to really understand How do panels and their children interact and talk to each other to form the final view?

As we all know this happens in a two phase process that starts at the top of the tree and ripples to the bottom of the tree down to each child.

  1. Phase One: loop through all of the children of a parent/panel and give it the chance to dream of their desirable size according to suggested (by the parent) available space (set to infinity most of the time).
  2. Phase Two: loop through all of the children of a parent/panel and give them our decision regarding their wish.
Possible routes:
  1. measure phase:
    1. available space (equals to infinity most of the time) passed down to each child.
    2. a child (Measure) would calculate its dimensions and return it back to its parent by a property called DesiredSize.
    3. a panel will determine its own final size according to all of its children's sizes when arranged in a certain order.
  2. arrange phase:
    1. Top parent will pass down the final available space for this panel
    2. Panel will calculate each child's position and size and will pass it down to the child by calling Arrange


The problem with ScrollViewer is that it tills its children , I am going to make all of your wishes come true. Ironically this is not a favorable solution: Why? because when a child has the ability to display its own scrollbars, it becomes meaningless when they can grow as much as they want to display their content.

From a different point of view. A gui designer does not want to see a text box inside of a form that takes a whole lot of a space to display its content. We have other stuff to show, please behave your self and share with others available space and this is why you have scrollbars.

Semaphore : My solution is to ask your future employee what is your minimum acceptable salary and I will do my best to give you even more. And then you make a decision about it? This way, you can make better decision. Prevent your employee from going so far with their dreams. Of course this works in an ideal world like computers ! So this should be a good solution.

How it should ideally work according to my vision to achieve a better behavior?  It should go through three phase process:
  1. Phase One : loop through all the children and ask them what is the (Minimum) desirable size that is going to fit their content (recursively: without screwing its children's minimum sizes).
  2. Phase Two : loop through the children and tell them, this is the size that I can give to you, it can be less than what they wanted, same or even more.
  3. Phase three : the child will have to live with what is given to it , unless it was more than what it wants and can ignore given size and be as small as it wants.

Monday, April 28, 2014

WPF DataGrid RowDetails eats the first click.

I have this problem where I have a panel of buttons inside my DataGrid's RowDetails. When I first open the window , I see RowDetails of the first row shown as expected but not selected.

The symptoms : when I click on one of the buttons inside my current row's RowDetails , the DataGrid eats up the click and focuses the row instead of executing the click of the button. The second time I click on the button it works.

The complicated explanation: when you click on any button there is an algorithm that gets executed before determining that this operation is a successful click or not. This the highlights of the algorithm.
1- if mouse down is received on the button
2-   mouse is captured
3-   a set of flags are set to track the operation
4- when the mouse is up
5-   the flags need to be set.
6-   the most important mouse status should be Captured

now this contradicts/conflicts with some of RowDetails' implementation.  Which attempts to set the RowDetails' beholder row to selected.  Before doing this , it checks if the current row equals the RowDetails' beholding row. But the problem is that it is doing it the wrong way. It checks the CurrentItem instead of checking the SelectedItem.

So the simplest solution I came up with , is to sync SelectedItem with CurrentItem as follows:
       
        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            base.OnPropertyChanged(e);

            // to stop RowDetails from eating the first click.
            if (e.Property.Name == "SelectedItem" && CurrentItem == null) CurrentItem = SelectedItem;
        }

and no IsSynchronizedWithCurrentItem did not help resolving the problem.

Tuesday, March 25, 2014

Scrollable Expandable Control's problem

Scrollable-expandable-controls problem.
Scrollable-expandable-controls : are controls that can stretch as its content grows and will display scrollbars when their size is restricted.
Problem appears when they are located inside another scrollable control. Child scrollable-expandable-controls will keep expanding and will count on the outer scrollable control's scrollbars.
if you give it a maximum width or height problem will be resolved but you will need to know the size ahead and you don't have this privilege if you want a dynamic app that works well with all different screen sizes.
in order to achieve required behavior , we need a panel in between to allow its children (scrollable-expandable-control) to grow. Asking them to give the minimum required size and then give them the maximum size the parent provides without displaying scrollbars , currently there is no panel like this.

Here is a one that I developed to provide this functionality:
    class LimitChild : System.Windows.Controls.Panel
    {
        public LimitChild()
        {
        }

        protected override Size MeasureOverride(System.Windows.Size availableSize)
        {
            System.Diagnostics.Debug.Assert(InternalChildren.Count == 1);
            System.Windows.UIElement child = InternalChildren[0];

            Size panelDesiredSize = new Size();
            // panelDesiredSize.Width = availableSize.Width;
            panelDesiredSize.Width = (double)child.GetValue(FrameworkElement.MinWidthProperty);
            panelDesiredSize.Height = (double)child.GetValue(FrameworkElement.MinHeightProperty);

            child.Measure(panelDesiredSize);

            // IMPORTANT: do not allow PositiveInfinity to be returned, that will raise an exception in the caller! 
            // PositiveInfinity might be an availableSize input; this means that the parent does not care about sizing 
            return panelDesiredSize;
        }

        protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
        {
            System.Windows.UIElement child = InternalChildren[0];

            child.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
            if (finalSize.Width > child.RenderSize.Width)
                finalSize.Width = child.RenderSize.Width;
            if (finalSize.Height > child.RenderSize.Height)
                finalSize.Height = child.RenderSize.Height;

            return finalSize; // Returns the final Arranged size
        }
    }
and then inside your xaml surround your scrollable-expandable-control with a LimitChild panel.

Monday, March 24, 2014

SugarSync Review

I only tried to use it for one day, and I found all of these defects:

the client software that I install on my computer:
- I cannot log off and log in in a different user name. Once you log in you are stuck.
- some of the folders that are shared with me by other friends , their "sync to my computer" toggle button is completely disabled.
- The folder that I managed to turn its sync-to-my-computer toggle button on, SugarSync failed to sync it. It has a 3 GB file inside it. It says that sync is successful and complete, but actually the file is not in the folder. (P.S: I tried to increase the cache up to 8 GB but did not help.)
- Froze several times and became unresponsive. and I had to kill it every time.
- crashed once.


When I tried to access the Website directly , I faced the following Problems:
- it claims that download was successful and I end up with a broken file.
- if download is interrupted, there is no resume download feature.
- When I download a folder , I end up with 3 or 4 of the downloaded files with 0 KB (empty files). Knowing that they are at least few 3KBs in size but still I receive them with 0 KB and status is downloaded successfully.

Sunday, March 23, 2014

Unaligned Column Header with Data

I am very happy today that I solves a problem that I have been facing for a long time.
:->

When you have a custom control template, column headers will be shifted to the left unshowing the Select All button while the DataGrid cells manage to recognize the row header and shift correctly. Resulting in a column header that is not aligned with its corresponding cells.

CAUSE : some people would think it is the Visibility of the select all button but this is very untrue. The truth is that Width of this button is bound to an un-updated-properly property CellsPanelHorizontalOffset . Simply put , DataGrid fails to update this property accurately , ending up with value (most of the time) equals ZERO.

SOLUTION : bind the Select All button Width to RowHeaderActualWidth and enjoy the magic ;-)


Thursday, October 27, 2011

Don't derive from Selector

It is a base class that is used by ListBox ComboBox TabControl and others.. Why I cannot derive from it? because for some reason the designers decided to declare few critical functionality as internal . These methods were used in ListBox, ComboBox and TabControl in order to achieve their functionality but yet the designer determined to declare these methods as internal .. WHHHYYY?!!

I don't have the answer. I am deriving from ItemsControl instead.