Wednesday, February 16, 2011

Link to existing source file

Got a nice tip from PDSA.NET

Reuse Class Files in Different Projects (Add as Link)

All of us have class libraries that we developed for use in our projects. When you create a .NET Class Library project with many classes, you can use that DLL in ASP.NET, Windows Forms and WPF applications. However, for Silverlight and Windows Phone, these .NET Class Libraries cannot be used. The reason is Silverlight and Windows Phone both use a scaled down version of .NET and thus do not have access to the full .NET framework class library. However, there are many classes and functionality that will work in the full .NET and in the scaled down versions that Silverlight and Windows Phone use.

Let's take an example of a class that you might want to use in all of the above mentioned projects. The code listing shown below might be something that you have in a Windows Form or an ASP.NET application.

public class StringCommon
{
public static bool IsAllLowerCase(string value)
{
return new Regex(@"^([^A-Z])+$").IsMatch(value);
}

public static bool IsAllUpperCase(string value)
{
return new Regex(@"^([^a-z])+$").IsMatch(value);
}
}

The StringCommon class is very simple with just two methods, but you know that the System.Text.RegularExpressions namespace is available in Silverlight and Windows Phone. Thus, you know that you may reuse this class in your Silverlight and Windows Phone projects. Here is the problem: if you create a Silverlight Class Library project and you right-click on that project in Solution Explorer and choose Add | Add Existing Item… from the menu, the class file StringCommon.cs will be copied from the original location and placed into the Silverlight Class Library project. You now have two files with the same code. If you want to change the code you will now need to change it in two places! This is a maintenance nightmare that you have just created. If you then add this to a Windows Phone Class Library project, you now have three places you need to modify the code!

Add as Link

Instead of creating three separate copies of the same class file, you want to leave the original class file in its original location and just create a link to that file from the Silverlight and Windows Phone class libraries. Visual Studio will allow you to do this, but you need to do one additional step in the Add Existing Item dialog. You will still right mouse click on the project and choose Add | Add Existing Item… from the menu. You will still highlight the file you want to add to your project, but DO NOT click on the Add button. Instead click on the drop down portion of the Add button and choose the "Add As Link" menu item. This will now create a link to the file on disk and will not copy the file into your new project.

When this linked file is added to your project, there will be a different icon next to that file in the Solution Explorer window. This icon signifies that this is a link to a file in another folder on your hard drive.

Of course, if you have code that will not work in Silverlight or Windows Phone -- because the code has dependencies on features of .NET that are not supported on those platforms – you can always wrap conditional compilation code around the offending code so it will be removed when compiled in those class libraries.

Download Other Tips and Tricks

I hope you found this short tip useful. You can get many other tips, tricks and articles at my website. http://www.pdsa.com/downloads.

Tuesday, February 8, 2011

MVVM Basic Binding - reflecting multiple targets

I have a ViewModel implementing INotifyPropertyChanged interface and have at least two properties Value and IsChanged. When I bind two Textboxes to the Value on source and one check box to IsChanged property of the source I noticed that user changes to Value does not reflect the other target controls!

This behavior is due to the fact that my ViewModel was not provided as a field in my container ViewModel and rather it was created when needed.


public PandViewModel PandViewModel
{
get
{
return listIndex < 0
?
null
:
new PandViewModel(panden[listIndex]);
}
}

Moving the PandViewModel to a local field in this container class, or even putting it into an ObservableCollection will sort out the problem:
public PandViewModel PandViewModel
{
get
{
return listIndex < 0
?
null
: pandenVM[listIndex];
}
}


See also a nice article over Binding on MSDN.

Wednesday, February 2, 2011

MVVM Basic Principals

The very basic principals so far I understand from M-V-VM practice is the following which is understood from this article : http://msdn.microsoft.com/en-us/magazine/dd419663.aspx#id0090025


View is a Control or Window

public partial class CustomerView : System.Windows.Controls.UserControl
{
public CustomerView()
{
InitializeComponent();
}
}

And this is as far as the view might be written in code. In the most cases we don't want to write more code in the View. The reason is that the View is supposed to be made loosely coupled from the data and the logic, probably done by creative designers.

The View gets DataContext


Some magic forces will get the data that is accessible through the ViewModel and set it to the DataContext property of the control:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);

var viewModel =
new MainWindowViewModel(path);
MainWindow window =
new MainWindow();
window.DataContext = viewModel;

window.Show();
}


In this example the magic happens in the Startup method of the application. Using MEF this magic is taken out to some sophisticated standard way.
Now the View and its elements are ready to consume the data.

View binds to ViewModel


Both data and actions are binded to public accessors of ViewModel:

 <TextBox
x:Name=
"firstNameTxt"
Grid.Row=
"2" Grid.Column="2"
Text=
"{Binding Path=FirstName, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate=
"{x:Null}"
/>


<Button
Command=
"{Binding Path=SaveCommand}"
Content=
"_Save"
/>



ViewModel is unaware of View


Unlike the Presenter in MVP, a ViewModel does not need a reference to a view. This makes it possible to attach any View at any time to consume the public data provided by ViewModel.

ViewModel exposes public properties to share data with any possible View:
public class CustomerViewModel : WorkspaceViewModel, IDataErrorInfo
{
public string FirstName
{
get { return _customer.FirstName; }
set
{
if (value == _customer.FirstName)
return;

_customer.FirstName = value;

base.OnPropertyChanged("FirstName");
}
}


ViewModel exposes public commands to share actions with any possible View:
/// <summary>
/// Returns a command that saves the customer.
/// </summary>
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand =
new RelayCommand(
param =>
this.Save(),
param =>
this.CanSave
);
}
return _saveCommand;
}
}


Some of these properties may be data coming from a model and some might be simply a state of the ViewModel to help out the View.

Data Manipulation


The ViewModel, never the View, performs all modifications made to the model data.
As I already mentioned the ViewModel is unaware of View but it is certainly aware of Model and can manipulate it when needed. When user interacts with the View, the binding manager will take care of both ways of data modifications in each binding.

The way a ViewModel is attached to a model, is an architectural decision and makes your application easier or hard to understand and maintain. But this is another important subject. For now, like MEF does the magic for coupling View and ViewModel, there needs to be some Factory or Controller mechanism to pull the data and assign it to the ViewModel.

Logging Properties of an instance object

Logging is an ongoing subject that I had some posts about it. In this post I am adding two methods to my beloved Logger class.
The first one shows the values of each property where the property is readable while the second method tries to show the signature of the method.
/// <summary>
/// Shows the public properties
/// </summary>
/// <param name="instance"></param>
public static void ShowPropertyValues(object instance)
{
var instanceType = instance.GetType();
Information(
">>> Showing Instance Properties: " + instanceType.Name);

var props = instanceType.GetProperties();
foreach (var property in props)
{
if (!property.CanRead) continue ; //'t read!
Information(
string .Format("Instance.{0} ='{1}'" , property.Name, property.GetValue(instance, null )));
}
}

/// <summary>
/// Shows the public properties
/// </summary>
/// <param name="instance"></param>
public static void ShowPropertySignature(object instance)
{
var instanceType = instance.GetType();
Information(
">>> Showing Type Properties: " + instanceType.Name);

var props = TypeDescriptor .GetProperties(instance);
foreach (PropertyDescriptor prop in props)
{
Information(
"----------------------------------------------------" );
Information(
string .Format("Name ='" , prop.Name));
Information(
string .Format("DisplayName ='" , prop.DisplayName));
Information(
string .Format("Description ='" , prop.Description));
Information(
string .Format("ComponentType ='" , prop.ComponentType.Name));
Information(
string .Format("Category ='" , prop.Category));
Information(
string .Format("IsBrowsable ='" , prop.IsBrowsable));
Information(
string .Format("IsLocalizable ='" , prop.IsLocalizable));
Information(
string .Format("IsReadOnly ='" , prop.IsReadOnly));
Information(
string .Format("PropertyType ='" , prop.PropertyType.Name));
Information(
string .Format("SerializationVisibility ='" , prop.SerializationVisibility));
Information(
string .Format("SupportsChangeEvents ='" , prop.SupportsChangeEvents));
}
}