Tuesday, October 25, 2011

Setting up a Composite WPF Application Properly.

It's time to step back from working on bits and pieces of something large and remind myself how to create a MVVM project from scratch. There are probably templates available now but I'll start with an empty C# WPF project and call it MVVM for simplicity. Renamed MainWindow to Shell everywhere in the solution because Shell is the conventional name for a top-level window in the application built in the Composite Application Library.

Composite Application Library displays and hides controls through the use of Regions. Several controls can be displayed as regions, one of them is ItemsControl.

An attached property RegionManager.RegionName indicates which region is associated with the control. So, in the Shell.xaml I replaced the Grid element with the ItemsControl element:

<ItemsControl Name="MainRegion" cal:RegionManager.RegionName="MainRegion"/>

For the code to compile, the following line has to be added to the Window tag

xmlns:cal="http://www.codeplex.com/CompositeWPF"

and the Microsoft.Practices.Prism dll has to be referenced in the project which contains the definition of RegionManager.RegionName

The bootstrapper initializes the application build using the Composite Application Library. At this point the bootstrapper only returns the new instance of a Shell class. Added the Bootstrapper class to the solution:

class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return this.Container.Resolve();
}

protected override void InitializeShell()
{
base.InitializeShell();

App.Current.MainWindow = (Window)this.Shell;
App.Current.MainWindow.Show();
}

protected override void ConfigureModuleCatalog()
{
}
}

At this point, the reference to Microsoft.Practices.Prism.UnityExtensions is required (for UnityBootstrapper). And not to forget the

using Microsoft.Practices.Unity;

otherwise the container would not resolve! Basically, Container.Resolve constructs an instance of the concrete class, resolving any dependencies that it has.

Now the bootstrapper has to run when the application starts. To achieve this, the Startup event of the application is handled in the App.xaml.cs file.

public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
}

And since the instance of the Shell is manually created by the bootstrapper, the StartupUri attribute is not needed anymore in the App.xaml Application tag.

<Application x:Class="MVVM.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>

</Application.Resources>
</Application>

The application is fully functional now!

References:

WPF Hands-On Lab: Getting Started with the Composite Application Library

What does this mean in Prism/Unity: Container.Resolve()

UnityBootstrapper Class

Bootstrapper

by . Also posted on my website

Monday, October 24, 2011

Small Things Learned Today

Drop a Default Constraint on a Table Before Dropping the Table.

Trying to drop the colum from a database via a simple script

ALTER TABLE [dbo].MYTABLENAME DROP COLUMN MYCOLUMNNAME

I came across the error I did not quite expect.

Msg 5074, Level 16, State 1, Line 1
The object 'DF__MYTABLENAME__MYCOL__42ACE4D4' is dependent on column 'MYCOLUMNNAME'.
Msg 4922, Level 16, State 9, Line 1
ALTER TABLE DROP COLUMN MYCOLUMNNAME failed because one or more objects access this column.

Turns out it is not possible to drop the column this way because there is a default constraint set on it.
However, the constraint name is 'DF__MYTABLENAME__MYCOL__42ACE4D4', which is fine if I'm doing it in the database I have full access to. I could even use the visual tool like Management Studio to just right-click and drop.

That's not possible, however, in my case, because the database is in the remote location and I need to write a script that I can send over and ask the person in charge to run it. I don't want to ask him 'hey, could you please run sp_help on the table MYTABLENAME and let me know what it tells you'.
That's where this little script comes handy:

declare @default sysname, @sql nvarchar(max)

select @default = name
from sys.default_constraints
where parent_object_id = object_id('MYTABLENAME')
AND type = 'D'
AND parent_column_id = (
select column_id
from sys.columns
where object_id = object_id('MYTABLENAME')
and name = 'MYCOLUMNNAME'
)

set @sql = N'alter table MYTABLENAME drop constraint ' + @default
exec sp_executesql @sql

alter table MYTABLENAME drop column MYCOLUMNNAME

go

by . Also posted on my website

Sunday, October 16, 2011

WPF Commands Part 1

I'm working on deeper understanding on how commands work in WPF. How do RoutedCommands work? First, I add a class to my solution and call it Commands.

public static class Commands
{
public static readonly RoutedCommand MyClick = new RoutedCommand();
}

Next, I declare the command and add a binding in my User Control.

<Button 
Command="{x:Static my:Commands.MyClick}"
Grid.Column="2" Height="23" HorizontalAlignment="Left" Margin="0"
Name="button1" VerticalAlignment="Top" Width="75" Content="Button"/>

<UserControl.CommandBindings>
<CommandBinding
Command="{x:Static my:Commands.MyClick}"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed">
</CommandBinding>
</UserControl.CommandBindings>

Next, I add the handlers for the CommandBinding_CanExecute and CommandBinding_Executed.

private void CommandBinding_CanExecute(object sender, System.Windows.Input.CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}

private void CommandBinding_Executed(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
{
System.Windows.Forms.MessageBox.Show("Executed in DimensionsView");
}

So far, so good - the button is clicked, the message appears. However, that's too simple - next, I need to make sure my other user control will be notified when the button is clicked. This is the bit I was not able to do quickly on my first attempt. It appears that I need to implement a MVVM pattern to achieve this.

Fist I will implement the pattern in the simplest possible way. I'll create a ViewModel for my User Control. The ViewModel provides the mechanics of creating a command. The two functions serve the following purpose: CanClick() checks if the button click can happen. Some logic can be placed there and, if it returns false, the button will in fact be disabled. DoClick() actually runs the code that should happen on button click.

public class DimensionsViewModel
{
private DelegateCommand _clickCommand;

public ICommand ClickCommand
{
get
{
if (_clickCommand == null)
{
_clickCommand = new DelegateCommand(new Action(DoClick), new Func(CanClick));
}
return _clickCommand;
}
}

private bool CanClick()
{
/* code to check if the button can be clicked */
return true;
}

private void DoClick()
{
System.Windows.Forms.MessageBox.Show("Click in DimensionsView");
}
}

The Button in the xaml file needs to know which command to run.

<Button 
Command="{Binding Path=ClickCommand}"
Grid.Column="2" Height="23" HorizontalAlignment="Left" Margin="0"
Name="button1" VerticalAlignment="Top" Width="75" Content="Button"/></pre><p>And finally, the User Control needs to know which ViewModel to use:</p><pre class="brush:xaml"><UserControl x:Class="DynamicGrid.DimensionsView"

...
xmlns:my="clr-namespace:DynamicGrid">
<UserControl.DataContext>
<my:DimensionsViewModel/>
</UserControl.DataContext>
...
</UserControl>

And this is it! What I have achieved so far: there is absolutely no code in the xaml.cs file. Here's proof, the whole contents of the DimensionsView.xaml.cs:

public partial class DimensionsView : UserControl
{
public DimensionsView()
{
InitializeComponent();
}
}

Now someone can work on the UI and edit the xaml file, and someone can work on the business logic and work with the ViewModel, and they would not even need to check out the same file and then merge their changes or bump into one another in any way.

by . Also posted on my website

Thursday, October 13, 2011

Using the EventAggregator

The EventAggregator is the pattern that may be used in multiple scenarios, but in my case I have a WPF application that uses several User Control. When an action happens in a particular User Control (i.e. a user clicks the button), another User Controls or several User Controls react (i.e. draw an image). At first it looked like Routed Events and Commands are the way to go but I found out that could not easily build a working prototype so I decided to learn them at a later stage.

I found the explanation and a sample implementation here:

Simple message-based Event Aggregator

My prototype included two user controls, and I used the implementation of IEventAggregator and ISubscription from the link above.

That's the solution structure:

The user control that publishes the event does it in the following way:

public partial class DimensionsView : UserControl
{
private EventAggregator _eventAggregator = null;

private EventAggregator EventAggregator
{
get
{
if (_eventAggregator == null)
{
_eventAggregator = new EventAggregator();
}
return _eventAggregator;
}
}

public DimensionsView()
{
InitializeComponent();
}

private void button1_Click(object sender, RoutedEventArgs e)
{
//publish a message that will be subscribed to in another user control
EventAggregatorInstance.EAggregator.Publish(new BasicMessage("test"));
}

And the subscribing user control declares an action it's interested in explicitly and subscribes to it.

public partial class GridView : UserControl
{
private Action someAction;

private EventAggregator _eventAggregator = null;

private EventAggregator EvengAggregator
{
get
{
if (_eventAggregator == null)
{
_eventAggregator = new EventAggregator();
}
return _eventAggregator;
}
}

public GridView()
{
InitializeComponent();
someAction = message => Test(message);
var subscription = EventAggregatorInstance.singletonAggregator.Subscribe(someAction);
}

private void Test(BasicMessage msg)
{
PerformAction();
}

public void PerformAction()
{
System.Windows.Forms.MessageBox.Show("Action Performed");
}
}

That's pretty much all that's required to build a working prototype. Of course, an instance of the EventAggregator has to be created for the application to work. This can be done in multiple ways, I chose one of the simplest - a singleton.

public static class EventAggregatorInstance
{
public static EventAggregator EAggregator = null;

public static EventAggregator singletonAggregator
{
get
{
if (EAggregator == null)
{
EAggregator = new EventAggregator();
}
return EAggregator;
}
}
}

I haven't given up on the Routed Events and Commands, that's my next subject to explore!

by . Also posted on my website

Wednesday, October 12, 2011

First Encounter with Windows 7

I need to make sure that a couple of internal applications are working properly under Windows 7. That includes installing all required hardware drivers and documenting the process. It has been reasonably frustrating so far since not many drivers are installed without problems and some COM components are not registered properly and in general the process is very far from smooth. I don't think I've ever dealt with so many errors in one day, here's a subset. (There's nothing particularly interesting here except they all happened today under various circumstances).








by . Also posted on my website

Tuesday, October 11, 2011

Customising the app.config file during installation.

Since we have several environments - development, testing etc., and each environment uses a separate web service it is helpful to be able to specify which environment to use as an installation step. The alternative is to manually edit the app.config whenever needed.

Turns out it can be easily done (in just about 10 minutes if you know how to, plus some additional time to find the relevant information) using the Visual Studio Setup and Deployment project. Here's my walkthrough, I'm still using Visual Studio 2005:

- Under "Setup and Deployment project", in the "Solution Explorer", select "View->User Interface".

- Right-click "Start", select "Add Dialog", select the appropriate dialog. In my case, I chose "RadioButtons - 4 buttons".

- Set the properties for the dialog. These are mostly self-explanatory: ButtonXLabel is what the user will see, ButtonXValue is what the ButtonProperty will be set to if this option is selected.

- Move the "RadioButtons" element up in the tree to the position just after "Welcome"

- Under "Setup and Deployment project", in the "Solution Explorer", select "View->Custom Actions"

- Right-click "Custom Actions", select "Add Custom Action". Navigate to Application Folder and select "Add Output". Select Primary Output for the main project.

- Set the CustomActionData to the value similar to the one in the screenshot. TARGETENV is what ButtonPropery is called, and the custom action assigns it to the TargetEnv

That's it for the Setup and Deployment project.

Now to the code part. The section of my app.config file which I need looks like that:






I just need to read the appropriate value of the web service url and overwrite the "URL" setting with it. For that, I need the installer class.

- Right-click the main project and select "Add -> New Item". Add the Installer Class from the dialog window.

- In the installer class, I need to override the Install function. Again, mostly self-explanatory. Read the value of the target directory and the target value, open the app.config file, read the appropriate setting from it, assign the correct value to the setting and save the configuration file. Build and run.


[RunInstaller(true)]
public partial class MyInstaller : Installer
{
public MyInstaller()
{
InitializeComponent();
}

public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);

string targetDirectory = Context.Parameters["TARGETDIR"];
string buttonValue = Context.Parameters["TargetEnv"];
string exePath = string.Format("{0}SetupSampleProject.exe", targetDirectory);
Configuration config = ConfigurationManager.OpenExeConfiguration(exePath);

string url = string.Empty;

switch(buttonValue)
{
case "LIVE":
url = config.AppSettings.Settings["URLLive"].Value;
break;
case "TEST":
url = config.AppSettings.Settings["URLTest"].Value;
break;
case "SQA":
url = config.AppSettings.Settings["URLSQA"].Value;
break;
case "DEV":
url = config.AppSettings.Settings["URLDev"].Value;
break;
}

config.AppSettings.Settings["URL"].Value = url;

config.Save();
}
}

This is what the user will see during installation (well, obviously I missed the text labels on the dialog, but the rest is working).

References:

RadioButtons User Interface Dialog Box

- Does not really give any more information compared to what is already visible in the IDE.

Installer Class and Custom Actions

- Good, but spread over several pages and really gives too much detail for this simple task

Configure App.config Application Settings During MSI Install

- Brief and informative, exactly what I needed.

by . Also posted on my website

Monday, October 10, 2011

I broke things, so now I will jiggle things randomly until they unbreak

I came across this great quote from Linus Torwalds (not even a quote, just more of a random thing he said):

This kind of "I broke things, so now I will jiggle things randomly until they unbreak" is not acceptable.

Subject: Re: Linux 2.6.39-rc3

I feel a bit sad because that's what I'm doing a lot recently - "Oh, the API fails when I try to get visible image in JPG format? Well, how about infrared in BMP format? Oh, the API fails when I ask for a string? Well, I can live with a byte array, I'll do the conversion myself".

That's a bad practice and I'm trying not to do that - my only excuse is the poor documentation supplied with third-party API's and SDK's.

by . Also posted on my website

Tuesday, October 4, 2011

An Application Needs to Create An Event Source in the Event Log

Spent about 6 hours today resolving the issue about writing events into the custom Event Log. Usual deal - application happily writes everything I ask it to write on my PC, but of course the client installs the application and nothing is logged. Turns out that the issue is well researched and is happening when the application is running under the account that does not have permissions to the EventLog entry in the registry, which can be verified by checking the "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog" node permissions

The custom event log is not created and the events are not getting logged but there is no exception raised so it's not obvious at the first glance what is happening. And I had to obtain a separate test login to replicate the behaviour.

There are several approaches and details may vary.

  • The easiest and probably the most dangerous is to just give the users full control to the registry key.
  • Or you could just tell the user to only run the application when a member of Administrators group is logged on.
  • Or you could create the event log and source manually or using some tool for each installation.
  • A more proper and not so straightforward approach is to create the custom event log when the application is being installed. Generally I followed the steps described here

Create a New Event Source in the Event Log

  • Created a new project in my solution of the type Class Library
  • Added the code for the class that inherits from System.Configuration.Install.Installer and creates the event log
  • Built the project

Next, however, the author suggested to directly use the resulting dll to create the event log by running the InstallUtil. That's not what I want my clients to go through, so I switched to another solution from here:

Walkthrough: Installing an Event Log Component

  • Added the output from my Class Library to the Setup and Deployment project
  • Created a custom action and assigned the output to the custom action
  • Built the Setup and Deployment project

Early testing shows so far that now you have to be an Administrator to install the application. But the event log is created at the time of installation, while previously it was not created until the event had to be logged first time. And in the future any user can use that event log.

This page, by the way, suggested overriding the Install and Uninstall methods in the class that creates the event log, unlike the one I used, where the log is created right in the class constructor. But I didn't get deep enough to understand the benefit of this approach:

Building Custom Installer Classes in .NET

by . Also posted on my website