Stimulsoft – Non-Modal Designer in WPF App, Part 2

In the previous post, we explored Supervising Controller Pattern to provide a solution to the issue we faced when using Stimulsoft Designer  Controller as an embedded control in WPF application. Even while we used the Supervising Controller Pattern, we still fiddled with the MVVM pattern by making the ViewModel “aware” of the View, even though it wasn’t depended. In this section, we would be exploring another method, through which could eliminate the ‘awareness’ factor, allowing us to work along the MVVM pattern with all its purity.
As in previous section, we will begin by adding the designer control in our XAML.
<wpfdesign:StiWpfDesignerControl></wpfdesign:StiWpfDesignerControl>
So how do we do resolve the issue this time around, without breaking the MVVM pattern ? Instead of Supervising Controller Pattern, we would instead on something which is native to the WPF. The Behaviours and Attached Properties
Behavior and Attached Property
Behavior allows us to Attach a new property the StiWpfDesignerControl, which is bindable.
public class ReportBehavior : Behavior
    {
        public static readonly DependencyProperty ReportSourceProperty = DependencyProperty.RegisterAttached("ReportSource", typeof(object), typeof(ReportBehavior), new PropertyMetadata(ReportSourceChanged));

        private static void ReportSourceChanged(DependencyObject DependencyObject, DependencyPropertyChangedEventArgs PropertyChangedEvent)
        {
            var stidesigner = DependencyObject as StiWpfDesignerControl;

            if (stidesigner != null)
                stidesigner.Report = PropertyChangedEvent.NewValue as Stimulsoft.Report.StiReport;
        }

        public static void SetReportSource(DependencyObject target, object value)
        {
            target.SetValue(ReportSourceProperty, value);
        }

        public static object GetReportSource(DependencyObject target)
        {
            return target.GetValue(ReportSourceProperty);
        }
As can be seen in the code above, we have defined a property called “ReportSource” which allows us to the set the Report Property of the Designer Controller.  We can now bind our property in our XAML
Putting it all together
      <wpfdesign:StiWpfDesignerControl local1:ReportBehavior.ReportSource="{Binding Path=ActiveReport}" x:Name="DesignerControl"  ></wpfdesign:StiWpfDesignerControl>
Don’t forget the namespace
xmlns:local1="clr-namespace:CM005.Behaviour"

 

Stimulsoft – Non-Modal Designer in WPF App

Stimulsoft Reports is one among the most powerful and easy to use Reporting Tools available. While this post is not focused on feature of Stimulsoft Reports, it is worth checking out the features in Stimulsoft Website.

 

Embedded Stimulsoft Designer Control and MVVM

The post would rather focus on displaying the powerful Stimulsoft Designer in a WPF application with Data Source being .Net Objects created at runtime. While this is pretty straightforward to do when you want to invoke the designer as a Modal Dialog, it becomes a little messier when you want the designer to be an embedded control, particularly when you want to stick to the MVVM Pattern.
Let’s first go ahead and place our designer control in the View first.
<wpfdesign:StiWpfDesignerControl></wpfdesign:StiWpfDesignerControl>

Don’t miss out adding the namespace

xmlns:wpfdesign="clr-namespace:Stimulsoft.Report.WpfDesign;assembly=Stimulsoft.Report.WpfDesign"

For sake of example, we would mock our business object rather than taking it out of Database

   public class Products
   {
       public string Name { get; set; }
       public decimal Price { get; set; }
   }

var   _ProductCollection = new List()
  {
  new ProductModel() { Name = "Product 1", Price = 100 },
  new ProductModel() { Name = "Product 2", Price = 200 }
  };

We do have a property ‘ActiveReport’ , which holds the report details and is responsible for synchronizing the dictionary. Let’s define the Property and synchronize our collection.


  private Stimulsoft.Report.StiReport _ActiveReport;

  public Stimulsoft.Report.StiReport ActiveReport
  {
  get { return _ActiveReport; }
  set
  {
  _ActiveReport = value;
  this.NotifyOfPropertyChange(nameof(ActiveReport));
  }
  }

And the code to register the collection with StiReport.

  ActiveReport = new Stimulsoft.Report.StiReport();
  ActiveReport.RegBusinessObject("Products", _ProductCollection);
  ActiveReport.Dictionary.Synchronize();

This is where our problems begin. We do not have a property (the Report Property doesn’t allow Binding) exposed by Stimulsoft Designer to which could bind the StiReport Instance. We can do it pragmatically by assigning the Report Property, but that would involve breaking the MVVM Structure. We would prefer not to do that, atleast not directly. So what would be our workaround ?

Supervising Controller Pattern

The workaround lies with having a Supervising Controlling Pattern. The Supervising Controller allows the ViewModel to be aware of the View, but not depended on it. Now this can be a tricky situation if someone questions whether this would be breaking the MVVM, considering as per MVVM, the ViewModel should be completely independent of View. Pretty much true, but when all doors are closed, there is nothing wrong in making the ViewModel ‘aware‘ of View, as long as it is not dependent on View and we do minimalist code in View. Remember, there is a significant difference in being aware and being dependent.

So how do we implement the Supervising Controlling Pattern and make the ViewModel aware of the View. We first being by creating the Contract interface through which the ViewModel would be interacting with View. Let’s call it ISupervisorController

  public interface ISupervisingController
  {
  void UpdateReportControl(Stimulsoft.Report.StiReport Report);
  }

As you can see, it has a single method called UpdateReportControl, accepting the StiReport instance. Let’s go ahead and implement it in the View. We will make that only minimalist work is done in the code behind class.

  public void UpdateReportControl(StiReport Report)
  {
  ReportDesigner.Report = Report;
  }

So with that done, we come to the most important part – how do we get an instance of View in the ViewModel ? Thanks to Caliburn.Micro, we do have a solution for that as well.

Screen and IViewAware

The Screen class in Caliburn.Micro utilizes an interface ‘IViewAware, which exposes a method called OnViewAttached which gives us reference to the attached View. All that left to do is override the method and use our Supervising Controller interface to call the method from View, which sets the Report property of Designer Control

  protected override void OnViewAttached(object view, object context)
  {
  base.OnViewAttached(view, context);
  (view as ISupervisingController).UpdateReportControl(ActiveReport);
  }

That’s it, you have your .Net Object Collection synchronized with the Designer. The entire source code described in this post can be found here