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.

Thursday, September 29, 2011

Double-click on a cell in a DataGrid does not start editing the cell, while F2 works, and begins editing the cell!!!

Double-click on a cell in a DataGrid does not start editing the cell, while F2 works, and begins editing the cell!!!

I worked a lot to solve this problem.. In my case, the problem was that my implementation of IList was not working properly. Specifically, the index operator (this[]) , the IndexOf method and Contains method.. Fix them and then double-click will work again without problems.

Update: just more clarification. In my case, I tried to make a trick to deceive the DataGrid by passing different objects everytime the DataGrid calls the this[] operator on the collection (I return a new object having properties with equal values). But it turned out that DataGrid internally keeps a list of these objects and compare them by reference, ending up with unequal results.

Wednesday, September 28, 2011

A custom IEnumerable implementation does not work properly with DataGrid

I am developing a DataGrid that looks at a data store that does not have a public collection. I implemented an IEnumerable interface for this data-store BUT
the problem is: I am not able to edit cells; every time I try to edit a cell I get this exception saying, "'EditItem' is not allowed for this view.".

You may ask, why not to create a list for your data-store and pass it to the DataGrid. The answer is: Because my data-store might get very large.

Analysis: I did some digging and I found that DataGrid casts Items property (property of its grand base class: ItemsControl) it casts it to IEditableCollectionView. And here where it fails (we will talk about it later). But what is the type of Items Property ?
Items is a property of type ItemCollection (which is a CollectionView) that is created for the Enumerable class that you assigned to ItemsSource. The collection-view is created by calling CollectionViewSource.GetDefaultCollectionView on your Enumerable class.

The analysis' results: DataGrid fails to create an editable CollectionView for your Enumerable class. Why? because you did not implement the correct interface for your Enumerable class.

So, the question is what interfaces your collection (your Enumerable class) should implement, so that the DataGrid can create an editable collection-view for it?

OK , now , it started to make sense.. I dag deeper and deeper and I found this piece of comments inside ViewManager class in .Net framework that clarifies the problem: (ViewManager is the creator of the collection-view for your Enumerable class that eventually will be passed to and used by Items)
The comments say:
// Order of precendence in acquiring the View:
// 0) If collection is already a CollectionView, return it.
// 1) If the CollectionView for this collection has been cached, then
// return the cached instance.
// 2) If a CollectionView derived type has been passed in collectionViewType
// create an instance of that Type
// 3) If the collection is an ICollectionViewFactory use ICVF.CreateView()
// from the collection
// 4) If the collection is an IListSource call GetList() and perform 5),
// etc. on the returned list
// 5) If the collection is an IBindingList return a new BindingListCollectionView
// 6) If the collection is an IList return a new ListCollectionView
// 7) If the collection is an IEnumerable, return a new CollectionView
// (it uses the ListEnumerable wrapper)
// 8) return null
// An IListSource must share the view with its underlying list.

// if the view already exists, just return it
// Also, return null if it doesn't exist and we're called in "lazy" mode

So , all what we need to do now is to implement one of the following interfaces that would generate automatically one of the Collection Views that implements IEditableCollectionView. so that your grid will work correctly.

Finally, I implemented IList in my Enumerable class and it worked without problems :) great ..!!
Helpful Notes:
- The generic version of IList interface is not the one you should implement. You have to implement the non-gernic version of IList.
- You can try, implementing ICollectionViewFactory , but you have to know it is a little bit more complicated. Because you're going to end up implementing few more interfaces in the process.
- You can implement the IEditableObject interface for that items in your list to have more control over editing and validating each item in the list (each row in the DataGrid).

Problem solved.

Update: another complication I created .. read here about it

Friday, September 23, 2011

How to trace inside WPF source code !! FOUND IT..

How to trace inside WPF? Thank God I found the solution posted by Shawn Burke - MSFT
Thank you Shawn :)

And these links too:
http://referencesource.microsoft.com/serversetup.aspx
http://weblogs.asp.net/rajbk/archive/2010/04/21/setting-up-visual-studio-2010-to-step-into-microsoft-net-source-code.aspx

If non of the above worked (... just like what happened with me..), try this one. You can download .Net Framework source code :) .. isn't this neat. Here:
http://www.codeproject.com/Articles/93423/Step-Into-NET-Framework-4-0-Source-Code
Thanks Arik Poznanski :)

But no.. not even this worked .. I guess I still have DLLs that do not have source code of the same version.. But hey, look at the bright side.. I have the source code now.. And I can use the features and tricks provided by links the above. Especially Tracing into properties and filling the Call Stack frame with external assemblies' calls.. providing me with objects' names and methods' names. So I can look them up from the Source Code and figure out the problem that I am having.

Tabbing is misbehaving inside DataGrid with custom controls (GenerateElement)

Context: I created a DataGridBoundColumn class that creates my own control.
Problem: tabbing between cells (that were created using the GenerateElement), causes tabbing to go through DataGridCell (the container of your generated element) then your element. So it will require the user to press TAB twice to go to the next cell.
Solution: The elements that you generate using GenerateElement (not edit mode cells), need to update some of its properties: Focusable = false, IsHitTestVisible = false, and finally set its IsTabStop to false (use this line: noeditingElement.SetValue(KeyboardNavigation.IsTabStopProperty, false);)

Conclusion: Elements created using GenerateElement should be NOT focusable , NOT hit visible, or tab stop disabled.

Wednesday, September 21, 2011

Focus is lost when GenerateEditingElement is overridden

When deriving from DataGridBoundColumn to override control creation for your DataGrid, you are going to face a problem.. Where does my focus go when I double click the cell?

The cell control is created and activated but the focus just goes to the parent DataGridCell leaving the control that I just created in GenerateEditingElement unfocused. So I will have to click a third click to get the focus where I wanted it in the first place.

The solution is to override DataGridBoundColumn.PrepareCellForEdit and set the focus there. Voila.

Friday, August 19, 2011

GridColumn does not stop children from expanding and the column expands with the children!!

The problem: So you have DataGrid Grid with a TextBox in a column .. when the user start typing some text it will expand ignoring all parent controls and all restrictions!! What is the solution?

The solution is: use the * for the column width instead of "Auto" in ColumnDefinition..

Details:
If grid column definition is set to * then it stretches to take all available space provided by parent. Yet it prevents children (and itself) from growing beyond the available space provided by parent.

while using "Auto" for column width will grow as necessary (by children) regardless how much space its parent gives it and ignoring all restrictions.

StackPanel cannot stretch properly

Context: I created a TextBox element and a button; both inside a StackPanel. And I put the StackPanel inside a Grid. I gave the StackPanel HorizontalAlignement.Stretch. And I don't want to give width to my TextBox .

Problem: My StackPanel is not expanding to fill the available space!!! even though I gave it Stretch for HorizontalAlignment.

Solution: use DockPanel.

Reasoning: StackPanel is designed to shrink as much as its children accept.

Wednesday, April 20, 2011

UserControl closes when focus is lost

I have a UserControl that I want it to close when focus is lost from it completely. Knowing that it has few children elements: TextBox and few buttons.

Obstacles:
- if I click on button on this UserControl , it loses the focus immediately.
So any attempt to handle lost focus to close the control will cause the control to close every time you click any button on it.

SOLUTION: do not use IsFocusScope unless the control is toolbar or menu (that should not maintain focus. Otherwise focus will keep returning to the first control of a non-IsFocusScope-host when you press any button on this IsFocusScope-host.

Wednesday, March 16, 2011

I am not receiving Clicks in the blank area of a StackPanel

I created a panel with two controls in it: textblock and image. But the panel only accepts clicks or mouse over the text and the image but any blank area around them in the panel is ignored.

Solution: change your panel's background to Transparent. This way you will keep your panel transparent and force receiving mouse events on your panel.

WPF: define attached property or dependency property

When to define dependency property and when to define attached property.

- Define attached property, if you want to give your user the ability to default this property to a control and to all of its descendents as well.
- Define attached property, when you want all different kinds of controls to have the capability to carry this property with them. May be because they could be children of your control/panel so you give them the ability to assign this property so they can participate in deciding how to be laid in your panel.

WPF: User Control vs Custom Control

When to use User Control and when to use Custom Control. Simply:

Use Custom Control if:
- If you want to override a control to expand its functionality or restrict it, like TextBox or Button
- If you want your clients to be able to create different ControlTemplate for your control.

Use User Control if
- you want to create a composite of controls to give group of functionality all together.

Monday, February 14, 2011

Shared Resources

Shared resources are very useful and good saving strategy but it might cause you troubles if you don't pay attention. Changing a shared resource in one place will change all other places where it is used..

Ok this is easy and obvious but what is not obvious is that all of your resources are shared by default. Watch out. ;)

Thursday, February 10, 2011

ControlTemplate vs DataTemplate

When to use ControlTemplate and when to use DataTemplate/HierarchicalDataTemplate
Quick Answer:
- ControlTemplate is used to replace the whole visual tree of a control.
- DataTemplate/HierarchicalDataTemplate: is used only to replace the content of a control.
Example at the bottom will clarify more.

Features of each one of them:
ControlTemplate:
- you can use template-binding to bind some of the elements' properties defined in your template with the templated control's properties. Like to bind a rectangle's color in your template with the templated control's background color.
- you can use ContentPresenter to display the templated control's Content somewhere in your ControlTemplate (in case your templated control is ContentControl control).
- and you can use ItemsPresenter to display the templated control's items in case your templated control is ItemsControl control.

DataTemplate/HierarchicalDataTemplate:
- you can bind the elements you defined in your DataTemplate with properties of your corresponding Data (not control's property) classes.



Example:
if you have TreeView control and you have TreeViewItem(s) in it. The TreeViewItem is going to have some GUI in it to display the following:
- plus/minus button.
- border.
- place to display the header of the item, may be text block.
- place to display the child items of it, may be a stack.

if you use DataTemplate/HierarchicalDataTemplate, you only need to worry about displaying the text block (Content); so you don't have to worry about the plus/minus button nor to worry about the child items.

On the other hand, if you use ControlTemplate for this TreeViewItem, you will have to draw the plus/minus button and where to display the textblock (ContentPresenter) and where to display the child items (ItemsPresenter). and then you can add your own decorations around them.

I will keep updating this post as necessary. Please, let me know what you think and if you have any questions or concerns.