Kiwi Ingenuity

Thoughts from another perspective

Temporary Storage Service for WPF

Sometimes there is a requirement to temporarily store an object that can be later retrieved.  My particular problem at the time was that I wanted to share a View Model with a second view, but alas neither view knows about one another.

My first thought was to store the view model object temporarily in a Unity Container.  That way I could easily retrieve the object at a later time. This solution was not satisfactory however because this was only suppose to be a temporary storage solution and Unity does not support any unregister operation.  Not having the ability to remove (unregister) the object was not an option.

My solution was to create a Temporary Storage Service. The service I am demonstrating here allows for the Deposit and the Withdrawal of data (objects).  Some people may want to store the data for a little bit longer than straight in and straight out.  This solution could easily be modified to suit.

My Temporary Storage Service looks like this.

public class TemporaryStorageService : ITemporaryStorageService
{
    public void Deposit<T>(Object o, string key)
    {
        System.Windows.Application.Current.Properties[key] = o;
    } 

    public T Withdraw<T>(string key)
    {   T o = (T)System.Windows.Application.Current.Properties[key];
        System.Windows.Application.Current.Properties.Remove(key);
        return o;
    }
} 

 

The Interface

publicinterface ITemporaryStorageService
{
   void Deposit<T>(Object o, string key);
   T Withdraw<T>(string key);
}

Register the service with Unity

// Temporary Storage Service
ITemporaryStorageService temporaryStorageService = new TemporaryStorageService();
unityContainer.RegisterInstance<ITemporaryStorageService>(temporaryStorageService);

 

Now I can call my Service whenever I need to store any Objects temporarily.  As I said above my immediate need was to store a view model temporarily.  So this is what I did.

My first view creates a new view model and immediately puts it in temporary storage

public class SomeWindow : Window
{
    ITemporaryStorageService _temporaryStorageService; 

    public SomeWindow(ITemporaryStorageService temporaryStorageService)
    {
        this._temporaryStorageService = temporaryStorageService;
        InitializeComponent();
    } 

    [Dependency]
    public SharedViewModel VM
    {
        set
        {
            this.DataContext = value;


            // We will store the view model in the Application Properites
            this._temporaryStorageService.Deposit<SharedViewModel>(value, "SharedViewModel");
        }
    }
} 

 

Then when I initialise my second view I call upon the stored viewmodel.

[Dependency]
public CashBook.MVVM.Services.ITemporaryStorageService TemporaryStorageService
{
   set
   {
      this.DataContext = value.Withdraw<SharedViewModel>("SharedViewModel");
   }
}

 

Works like a charm!

My First WPF Report

My first WPF application is near completion, but one of the last items on the list is report printing. After a couple of hours on the internet I really did not see an ideal solution. There were however some aspects of the solutions I saw that appealed so I decided that I would pick out the best bits of what I had found and script my own Report Writer.

One example that got my attention on the internet was Azam Sharp. I thought his Flow Document example was simple and clean solution. What I wanted to do was take Azam’s example to the next level and give myself a tool to quickly design a report for my new project.

My Requirement list went like this.

  1. Print Preview
  2. Page Header
  3. Page Footer
  4. Full control of the report layout
  5. Keep it simple.

I will demonstrate my solution using a similar Business Entity as Azam did.

    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        // I'll add another column as one should always try and improve a copied solution <g>.
        public string Phone { get; set; }
    }

My Customers Class will mange the Customer Collection

    public class Customers
    {
        List<Customer> _customers = new List<Customer>();

        public Customers()
        {
            for (int i = 1; i <= 80; i++)
            {
                Customer customer = new Customer();
                customer.FirstName = "FirstName " + i;
                customer.LastName = "LastName " + i;
                customer.Phone = "Phone " + i;
                _customers.Add(customer);
            }
        }

        public List<Customer> FullCustomerList { get { return _customers; } }

        public int CustomerCount { get { return _customers.Count(); } }

        public Customer[] GetReportLineItems(int skipCount, int takeCount)
        {
            var list = (from c in _customers
                        select c).Skip(skipCount).Take(takeCount);
            return list.ToArray(); ;
        }
    }

The operator will select and confirm the printing properties from the standard Print Dialog. I have designed a small class to record the values from the print dialog which we will use for formatting our report.

    internal class PageLayout
    {
        public PageLayout(double height, double width)
        {
            this.PageHeight = height;
            this.PageWidth = width;
            this.Margin = new Thickness(80, 50, 80, 50);
            this.ListViewItemsCount = 48;
        }

        public double PageHeight { get; private set; }
        public double PageWidth { get; private set; }
        public Thickness Margin { get; private set; }
        public double PrintAreaHeight { get { return PageHeight - Margin.Top - Margin.Bottom; } }
        public double PrintAreaWidth { get { return PageWidth - Margin.Left - Margin.Right; } }
        public int ListViewItemsCount{ get; private set; }
    }

The 'ListViewItemsCount' is most probably not the flashest coding mythology. I will elaborate in this later in this paper.

Next we want to design a Customer Report Template. We do this using XAML. The rules are no different, than if we where designing a form.

    <DockPanel x:Name="LayoutDockPanel" LastChildFill="True">
        <!-- Header -->
        <StackPanel DockPanel.Dock="Top" Height="80">
            <TextBlock FontSize="24" >Customer Report</TextBlock>
            <TextBlock FontSize="24" >Full Listing</TextBlock>
        </StackPanel>
        <!-- Report Footer -->
        <StackPanel DockPanel.Dock="Bottom" Height="20" >
            <TextBlock HorizontalAlignment="Center" Margin="0,5,0,0" >Printing via a Flow Document</TextBlock>
        </StackPanel>
        <!-- Report Body -->
        <ListView Name="ListView" HorizontalAlignment="Center" BorderThickness="0" >
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding FirstName}" Width="110"  />
                    <GridViewColumn Header="LastName" DisplayMemberBinding="{Binding LastName}" Width="110" />
                    <GridViewColumn Header="Phone" DisplayMemberBinding="{Binding Phone}" Width="110"  />
                </GridView>
            </ListView.View>
        </ListView>
    </DockPanel>

The Print Layout I have placed in a user control and it is here where I have total control over my report content. There is a page header, page body and a page footer section. If you think this through your layout options a virtually unlimited?

Now let put this altogether! I create a Print Preview window. The XAML looks like this.

    <DockPanel Width="Auto" Height="Auto" LastChildFill="True">
        <Button Width="100" Height="50" DockPanel.Dock="Top" Click="SendReportToPrinterButtonClick">Send to Printer</Button>
        <FlowDocumentReader >
            <FlowDocument x:Name="customerReport" Background="White" />
        </FlowDocumentReader>
    </DockPanel>

The Code behind looks like this.

    public partial class PrintPreview : Window
    {
        private Customers _vm;
        private PrintDialog _printDialog;

        public PrintPreview()
        {
            InitializeComponent();

            _vm = new Customers();

            _printDialog = new PrintDialog();
            if (_printDialog.ShowDialog() == true)
            {
                PageLayout pageLayout = new PageLayout(_printDialog.PrintableAreaHeight, _printDialog.PrintableAreaWidth);
                this.BuildReport(pageLayout);
            }
        }

        private void BuildReport(PageLayout pageLayout)
        {
            // Set the page Size and Margins
            this.customerReport.PagePadding = pageLayout.Margin;
            this.customerReport.PageHeight = pageLayout.PageHeight;
            this.customerReport.PageWidth = pageLayout.PageWidth;
            this.customerReport.ColumnWidth = pageLayout.PageWidth;
            this.customerReport.ColumnGap = 0;

            // Body Content
            for (int i = 0; i < _vm.CustomerCount; i += pageLayout.ListViewItemsCount)
            {
                CustomerReportTemplate layout = new CustomerReportTemplate();
                // The next two lines are important as it will place the 
                // header and footer in place and the rest of the page area
                // is for the page body content.
                layout.LayoutDockPanel.Height = pageLayout.PrintAreaHeight;
                layout.LayoutDockPanel.Width = pageLayout.PrintAreaWidth;

                layout.ListView.ItemsSource = _vm.GetReportLineItems(i, pageLayout.ListViewItemsCount);

                InlineUIContainer inlineContainer = new InlineUIContainer();
                inlineContainer.Child = layout;

                this.customerReport.Blocks.Add(new Paragraph(inlineContainer));

            }
        }

This is the code that drives the report content. Although you may see some evidence of MVVM in this snippet, it is not an objective in this paper to provide a true MVVM example.

The above snippet achieves:

  • Connecting to the data source.
  • Showing the Print Dialog, so that the user can select the desired printer.
  • Using the information from the print dialog set up report layout.
  • Calls on the data source for the report body content data.
  • After the operator has studied the print preview, send the report to the printer.

I want to bring your attention to how I defined the page layout (page size, margins) and also how I have completely avoided scripting a Document Paginator. Neil Knobbe has a blog where he deployed this this mythology, which I have borrowed. He emphasises how it is important to configure the flowdocument page, column and margin sizes.

List View Item Count

In my example I assigned a value to the ListViewItemCount. This property returns the number of rows I wish to print in the body of the my document. Now obviously this value depends on the paper orientation and the paper size in your printer. So really it needs to be calculated based on these other variables. The good news is that it can be calculated. At least it can be calculated if you are happy to agree with the following logic.

  1. This is a business report that accompanies your application. Once this report is designed it will not change much. We may want to give the operator the choice of Portrait or Landscape. The font and font size will be pretty much static. The paper size could possibly change, but in the majority of cases you will have designed your report to fit one paper size.
  2. If you study my Customer Report Template above you will see I have started with a DocPanel. Added a Header and a Footer and the rest of the space is for the Page Body. The height of the body area is dependent on the Print Dialog Printable area height, our margin and the space taken up by the header and footer.
  3. This suggests that the item count value could be as simple as providing a portrait orientation value or a Landscape orientation value. What are these values? If you go now and change the value for ListViewItemCount to a large number (say 130) and run the reports in both portrait and landscape the the answer will be quickly evident. Give it a go.
  4. If you do have several paper sizes that your operator has to choose from then it's a bit more complicated. However as I leaded to above, we can easily calculate the page body height and we can also easily calculate the height of each row. The rest I will leave up to you.

This example is a good base for printing Business Reports. If I was printing many reports on my project then a lot of the above code is reusable, so it would not take a long time to push out other reports as they are required.

Source Code

You can download the Source here.

WCSF - Sharing the Controller

While working on a Web Client Software Factory solution, I found myself in a situation where I wanted several presenters to share the same controller.

How this came about was that I had a page that deployed several User Controls that had their own presenters.  These same User Controls needed to communicate with one another, so sharing the same controller, was the solution I needed to deploy.

Mats Lycken had obviously needed to solve the same problem some months earlier and has posted a solution that certainly did the job for me.  He has published a [CreateShared] attribute which can be deployed to replace [CreateNew].

The [CreateShared] attribute enabled me to share the same Controller.  Mats has provided us with library which needs to be included in our project as well as a working example to see how to use the new attribute.

Further documentation can also be found here.  This link refers to a solution that Mats posted in the WCSF Contrib.  I also tried the Contrib solution and have to confess it left me rather confused?  My recommendation is to follow the first link above.

Thank-you Mats for a fine effort!

Adventures of setting up SQL 2008 February CPT

I downloaded the this new CPT, with the intention of replacing the SQL 2005, which currently resided on my Vista Ultimate machine.  The following is a brief summary of the issues I experienced.

 

Unable to find ‘Setup100.exe’

I was experiencing difficulties early in the piece as the install did not appear to be writing to my Program Files folder.  I tried “Run as Administrator” and whatever else I could think of to overcome the problem, all to no avail?  In the end I decided to reinstall Vista and presto the problem went away.

 

Version Support

Having successfully installed SQL 2008, I created a test database using the “Server Management Studio” – no problems.  When I loaded the “SQL Server Business Intelligence Development Studio” and tried to access the new database I received the error message “This server version is not supported. Only servers up to Microsoft SQL Server 2005 are supported.”  This was apparently a known issue and the solution can be found here.

 

Remote Access

There is no Surface Area Configuration tool. So I had to figure out a different way to allow Remote access.  The answer was found here . 

I enabled Named Pipes and TCP/IP and then I was able to access SQL from other machines on the network.

 

Implementing a Custom IProxyFactory using the DSA deployed by SCSF

When deploying WCF security, for a Smart Client Service Factory (SCSF) solution the documentation encourage the developer to deploy an EndPointCatalog.  I found this to be a lot of coding when all I wanted to do was simply pass a username and password to the proxy so that it would call the service with the correct credentials.

My workaround is to not create a “EndPoint” section in your client app.config but instead override the GetOnlineProxy Method in the Microsoft.Practices.SmartClient.DisconnectedAgent.WCFProxyFactory class.

Assuming you have a Service Agent (or similar) Module in your SCSF solution create a new class that inherits the WCFProxyFactory class mentioned above.

using System;
using System.Net;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Description;
using Microsoft.Practices.SmartClient.EndpointCatalog;
using Microsoft.Practices.SmartClient.DisconnectedAgent;
namespace MyNamespace
{
   public class WCFProxyFactory : Microsoft.Practices.SmartClient.DisconnectedAgent.WCFProxyFactory
               where TChannel : class
   {
      public override object GetOnlineProxy(Request request, string networkName)
      {
         Guard.ArgumentNotNull(request, "request");
         ClientBase proxy = (ClientBase)Activator.CreateInstance(request.OnlineProxyType);
         // Set the credentials
         ClientCredentials clientCredentials = proxy.ClientCredentials;
         clientCredentials.UserName.UserName = "myusername";
         clientCredentials.UserName.Password = "mypassword";
         return proxy;
      } 
   }
}

You will need to create a Guard Class which you can simply copy from the source code that comes with SCSF (i.e. Microsoft.Practices.SmartClient.DisconnectedAgent.Guard) and then your solution should successfully compile.

Lastly you need to change the default offlinebehavoir of your Agent which was generated by the Disconnected Service Agent. Look for the GetAgentDefaultBehavior method

public static OfflineBehavior GetAgentDefaultBehavior()
{
   OfflineBehavior behavior = new OfflineBehavior();
   behavior.MaxRetries = 3;
   behavior.Stamps = 1;
   behavior.Expiration = DateTime.Now + new TimeSpan(1, 0, 0, 0);
   // behavior.ProxyFactoryType = typeof(Microsoft.Practices.SmartClient.DisconnectedAgent.WCFProxyFactory);
   behavior.ProxyFactoryType = typeof(MyNamespace.WCFProxyFactory);
   return behavior;
}

Compile and start your application and it will now pass the credentials on to your service.

SCSF - View Removal and Disposal

There are instances where on closing a Work Item View, you would also like to dispose of the footprint.  Here is how it done.

Change the OnCloseView of the Presenter as follows:

#region Closing the View
/// <summary>
/// Close the view
/// </summary>
public void OnCloseView()
{
   base.CloseView();

   // If this presenter has created any workspaces
   // be sure to remove them first.
   // e.g. if (WorkItem.Workspaces.Contains(WorkSpaceName))
   //         WorkItem.Workspaces.Remove(WorkItem.Workspaces[WorkSpaceName]);

   // Remove the View from the SmartParts Collection
   this.WorkItem.SmartParts.Remove(View);

   // As we are closing and re-creating this view
   // several times, we need to Dispose the view.
   if (View is IDisposable)
      ((IDisposable)View).Dispose();

}
#endregion

Open the code behind the  that the presenter controls and add the following Code snippet.

#region Closing the View
void ParentForm_FormClosed(object sender, FormClosedEventArgs e)
{
   _presenter.OnCloseView();
}
#endregion

Finally on the same page change the OnLoad Method so it reads:

protected override void OnLoad(EventArgs e)
{
   _presenter.OnViewReady();
   base.OnLoad(e);

   // Capture the Form Close Event, because we want to destroy 
   // the view on Closing the form

   this.ParentForm.FormClosed += new FormClosedEventHandler(ParentForm_FormClosed);
}

 

And that’s it.   Each time you close the view it will be destroyed and the memory released.

About

My name is Allan Wissing.  I operate a small Software Development company, which has been keeping me busy now for over 2 decades.

For many years I have been telling myself that I do not require a Weblog!  There is no one out there that needs to know or who would want to know, what's on my mind.  Weblogs about what I had last Sunday for lunch or my holiday snaps, I have considered tedious and boring.

My work however has relied heavily on the Internet and search engines and there are some really neat blog sites out there, that have helped me resolve coding problems I was encountering.

Recently however, my work has been taking me to new horizons and there has been a desire to understand and master new technologies.  It came to my attention that not all the answers can be found using my favourite search engine?  This was because no one had yet scripted a solution.

The next logical step I decided was to script what I thought the solution should be and what better way but via a Weblog?  So here I am!  Most the content will be focused on how I resolved software development issues.  I welcome comments and feedback on the various subjects I might cover.

I hope some people get benefit from my perpective!

Caliber RM Firewall

Deploying the CaliberRM software so that I could access the CaliberRM Server/Database from both in-house and also from out on the internet became a overwhelming task for me this past week. I'm publishing this paper so that others may not go through the pains I endured.
Not all networks are setup the same, but I'm sure anyone reading this paper will pick up on some part that will enlighten them on how to achieve the desired connectivity.

Background

I have been evaluating CaliberRM for some months now and the application was beginning to grow on me. I haven't as yet proved the software usefulness through the entire SDLC (software development life cycle), but what I had achieved in Business Requirements and the User Requirements had me thinking this could be a useful tool.
I then thought I could achieve more use from the application if I could expose the CaliberRM server to the internet. I would then have the ability to access the software on and off site.
Finding documentation on how to achieve this was not easy. The documentation that came with the software had nothing to say and when I did a search on the internet I could only find one document on how to configure a firewall setup (see link below).
Trying to interpret what the instructions meant on this internet document became some what of a mission. Hoping Borland would come to my aid I put a question out on the news groups, but alas no response. My local Borland agent had to talk to me because I knew his phone number, but eventually that source dried up too. I even email the writer who published the document - Again not reply.
Anyway luckily I persisted and was intrigued in the end when I got the whole thing up and running that the original document was theoretically correct - It was just badly written.

It's all in the name

One piece of information I was able to extract from my local Borland agent was that he was accessing the CaliberRM application in-house and from the internet using the same IP address. He had exposed the public IP address to the in-house users. That way it was immaterial weather you were on or off site you used the same IP address to access the CaliberRM server
Another piece of information that deluded me for some time was the reference to "{fully qualified machine name}" and the statement "You must be able to access, or route, the fully qualified machine, from outside the firewall", which appeared on the Borland document. I spent a lot of time asking myself and other people what this meant?

The Solution

I discovered that CaliberRM wants to communicate with only one known server. The "{fully qualified machine name}" and the "Server name" you enter into the login screen have to be the same.
When the penny dropped on this the answer was easy!

CaliberRM Server Modifications

On the CaliberRM server machine you need to add the following property to the orb.properties file:
vbroker.se.iiop_tp.proxyHost={Your Domain Name}
e.g. MyDomain.com

Firewall

Open the TCP ports as described in the document allowing traffic to come both in and out.  Refer to your firewall instruction on how you do this.
Configure the NAT so that any traffic that comes in on the two ports is directed to the CaliberRM Server Machine

DNS Server

Configure your in-house DNS server machine so that any calls to your Domain Name (MyDomain.com) point to the CaliberRM Server.; Again the documentation on how to do this comes with the DNS software.

Testing

It should now be possible to execute the CaliberRM Client from both sides of the firewall. When you are off-site you enter your domain name as the server name when you login (e.g. MyDomain.com).
When you are on-site you again use your domain name (MyDomain.com), which will be interpreted by your local DNS server.

Conclusion

It was disappointing that the documentation was not up to scratch, but I hope this paper will enlighten those who encounter the same problems as I did.

Links