Mar 05 2010

Binding a Converter Parameter

There is a lot of situations when you need to bind ConverterParameter value.

Imagine what you have Receipt class with two fields: amount and currency type. And you need to format amount string to something like $1,000.00 or ¥1,000.00 depending on currency type. So the good idea is to use converter to do formatting.

The good way is to have some something like AmountFormatter which takes amount and currency type and do the formatting.

Current version of the Silverlight disallow us to bind Converter Parameter value.  But we could pass whole Reciept object to the formatter and take amount and currency type from it directly. Such formatter could looks like this:

public class AmountConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var receipt = value as Receipt;
        if (receipt != null)
        {
            return String.Format("{1}{0:0,0.0}", receipt.Amount, receipt.CurrencyChar);
        }
		return value.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        ...
    }
}

This method is not good cause we tides converter and particular class (in our case it is Receipt). But what could we do?

Ok, we want to have reusable converter. But we need to pass several values to it at the same time. Then lets simply define an interface which converter is expecting to get:

public class AmountConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var data = value as IConverterData
        if (data != null)
        {
            return String.Format("{1}{0:0,0.0}", data.Value, data.CurrencySign);
        }
		return value.ToString();
    }

	...

	public interface IConverterData
    {
        string Value { get; set; }
        string CurrencySign { get; set; }
    }
}

Now to prepare Receipt class to be used in conjunction with AmountConverter we just need to implement AmountConverter.IConverterData interface.

And the usage will look like this:

<TextBlock Text="{Binding Converter={StaticResource AmountConverter}}"/>

Please not what we binds to the whole object here.

Feb 26 2010

Enable Silverlight 4 Tools in Visual Studio 2010 Release Candidate

Are you still waiting for Microsoft to release Silverlight 4 for Visual Studio 2010 RC?

I am not anymore :)

You could completely use Silverlight 4 bits for VS 2010 Beta 2 with VS 2010 RC. Alex Sorokoletov has found how to install Silverlight 4 on VS 2010 RC.

There is no magic and steps are simple as One, Two, Tree:

  1. Download Silverlight 4 Tools For VS 2010 Beta 2
  2. Run installation. The dialog window saying what Visual Studio 2010 Beta 2 should be installed will appear. Don’t hit the cancel button!
  3. Locate folder named like ‘bfb0032a835647b79718f26ba81d3392’ on root of some of your hard drives. And copy it’s content to some other location.
  4. Open ParameterInfo.xml and comment out <BlockIf DisplayText="Visual Studio 2010 Beta 2 ..."> section (it is placed on lines 13-41)
  5. Now you are ready to SPInstaller.exe to install Silverlight 4 Tools
  6. One additional step is required to force Visual Studio to use Silverlight 4 instead of Silverlight 3 for visual designers. To do it open regedit and search for DesignerPlatforms. Under this key go to Silverlight and change SilverlightHost value to ‘4.0’.

You should be happy now :)

If you need RIA services then load patched Microsoft.RiaServices.Tools.dll assembly and copy it to ‘c:\Program Files\Microsoft SDKs\RIA Services\v1.0\Libraries\Server’ folder.

And then open GAC management console by [Win+R] -> assembly ->[Enter]. Remove old one assembly and add new one.

Restart Visual Studio and you are done.

Thanks for Alex!

Feb 03 2010

Can’t attach Silverlight debugger when System.Windows.Browser.HtmlPage.Window.Navigate is used to open new window

If you can’t attach debugger to Silverlight application opened in new browser window using HtmlPage.Window.Navigate method then do the following:

  1. Go to Debug –> Attach to Process
  2. In Attach to Process dialog select process which host Silverlight application
  3. Click on “Select…” button under “Attach to:” section
  4. In Select Code Type window select “Debug this code type” option and uncheck all checkboxes except Silverlight
  5. Click OK –> Attach
Nov 26 2009

Silverlight 3 access DataContextChanged event

In WPF when DataContext changes the DataContextChanged event fires. We could attach event handler to this event to execute some custom code.

In Silverligth all is the same, except one small thing: DataContextCahnged is marked as internal… hmm... how then we could know what DataContext has changed?

We could do a simple trick. We can’t access to DataContextChanged event, but we could bind some our dependency property to DataContext and handle dependency changed event :)

Let’s build a small class which will do binding for us.

public static class DataContextChangedEventManager
{
    public delegate void DataContextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args);

    public static readonly DependencyProperty DataContextChangedProperty =
        DependencyProperty.Register("DataContextChangedProperty", typeof(object), typeof(FrameworkElement), new PropertyMetadata(null, OnDataContextChanged));

    public static readonly DependencyProperty DataContextChangedEventHandlersProperty =
        DependencyProperty.Register("DataContextChangedEventHandlersProperty", typeof(EventHandler<DataContextChangedEventArgs>), typeof(FrameworkElement), new PropertyMetadata(null));

    public static void OnDataContextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        var eventHandlers = obj.GetValue(DataContextChangedEventHandlersProperty) as EventHandler<DataContextChangedEventArgs>;
        if (eventHandlers != null)
        {
            eventHandlers.Invoke(obj, new DataContextChangedEventArgs(args.NewValue));
        }
    }

    ...
}

public class DataContextChangedEventArgs : EventArgs
{
    public DataContextChangedEventArgs(object value)
    {
        Value = value;
    }
    public object Value { get; private set; }
}

Here we are defining DataContextChangedProperty. This property will help us to react on DataContext changed event. When property has changed the OnDataContextChanged will be fired.

Also we have DataContextChangedEventHandlersProperty property defined which store event handlers.

Now let’s define method for attaching event handler for DataContextChanged event:

    ...

    public static void AddDataContextChangedEventHandler(this FrameworkElement control, EventHandler<DataContextChangedEventArgs> value)
    {
        if (control.GetValue(DataContextChangedEventHandlersProperty) == null)
        {
            control.SetValue(DataContextChangedEventHandlersProperty, value);
        }
        else
        {
            var eventHandler = Delegate.Combine((Delegate)control.GetValue(DataContextChangedEventHandlersProperty), value);
            control.SetValue(DataContextChangedEventHandlersProperty, eventHandler);
        }
        if (control.GetBindingExpression(DataContextChangedProperty) == null)
        {
            control.SetBinding(DataContextChangedProperty, new Binding());
        }
    }

    ...

And method for detaching event handlers:

    ...

    public static void RemoveDataContextChangedEventHandler(this FrameworkElement control, EventHandler<DataContextChangedEventArgs> value)
    {
        if (control.GetValue(DataContextChangedEventHandlersProperty) != null)
        {
            var eventHandler = Delegate.Remove((Delegate)control.GetValue(DataContextChangedEventHandlersProperty), value);
            control.SetValue(DataContextChangedEventHandlersProperty, eventHandler);
        }
        if (control.GetBindingExpression(DataContextChangedProperty) != null)
        {
            control.SetValue(DataContextChangedProperty, DependencyProperty.UnsetValue);
        }
    }

    ...

And the usage.

Lets say we have TextBlock and we want to show MessageBox when DataContext changes.

<TextBlock x:Name="TestBlock" DataContext="{Binding}"/>

We see here what there is binding defined on DataContext  propertyof the control. This binding means what we bind DataContext of the control to current control DataContext. Really this does nothing but we need it to get our DataContextChangedProperty binding to work.

And in code we need attach event handler for DataContext changed event.

public DataContextChanged()
{
    InitializeComponent();
    TestBlock.AddDataContextChangedEventHandler(OnDataContextChanged);
}

private void OnDataContextChanged(object sender, DataContextChangedEventArgs args)
{
    MessageBox.Show(args.Value.ToString());
}

And working example as usual:

The workarounds library with examples could be downloaded from here (VS2010): Andrew Veresov Workarounds library v1.0

Nov 21 2009

Silverlight 3 Validation Workaround

Silverlight 3 brings cool validation futures. But still it has some pitfalls. The main problem is the way how errors appear. Right now the only way we have to indicate error state of the control is to throw exception during data binding. Assume we have next data class:

public class DataClass
{
    private String _value;
    public String Value
    {
        get { return _value; }
        set { _value = value; }
    }
}

Lets add simple validation logic to it. We will checking for blank characters in a passed value. If blank characters are present then we will throw Exception with error description message.

public class DataClass
{
    private String _value;
    public String Value
    {
        get { return _value; }
        set
        {
            if (value.Length > 0 && value.IndexOf(' ') > 0)
            {
                throw new Exception("Value should not contain blank characters");
            }
            _value = value;
        }
    }
}

And if we have data binding defined:

<TextBox x:Name="exceptionInSetterBox" Text="{Binding Value, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>

then, if user enter a string with space characters the text box will take invalid state with error message in tooltip.

But what if you want manually set error state for particular control or perform async validation?

Ok, every control has Errors attached property. This property holds ReadOnlyObservableCollection<ValidationError> collection. Cool! We could create it by ourself and just set it throw SetValue method.

But... no :( we can't

The problem is in the ValidationError class. Simply it does not have public constructor available.

But, don't worry we will find workaround ;) (yes, ValidationError class also is marked as sealed, so we can't inherit from it)

If we can't instantiate ValidationError class then we could allow Silverlight to do it for us :)

The idea is to raise exception in some databound property of some Control (let's say ValidationErrors fabric) and grab ValidationErrors from there.

public class ValidationErrorBuilder: Control
{
    public static ReadOnlyObservableCollection<ValidationError> GetValidationErrors(String errorMessage)
    {
        ValidationErrorBuilder eb = new ValidationErrorBuilder();
        return GetErrors(eb, errorMessage);
    }

    private static readonly DependencyProperty ErrorProviderProperty =
        DependencyProperty.Register("ErrorProvider", typeof(ErrorProvider), typeof(ValidationErrorBuilder), new PropertyMetadata(null));

    private static ReadOnlyObservableCollection<ValidationError> GetErrors(Control control, string errorMessage)
    {
        var validationHelper = new ErrorProvider(errorMessage);

        control.SetBinding(ErrorProviderProperty, new Binding("ValidationError")
        {
            Mode = BindingMode.TwoWay,
            NotifyOnValidationError = false,
            ValidatesOnExceptions = true,
            UpdateSourceTrigger = UpdateSourceTrigger.Explicit,
            Source = validationHelper
        });

        control.GetBindingExpression(ErrorProviderProperty).UpdateSource();

        return (ReadOnlyObservableCollection<ValidationError>)control.GetValue(Validation.ErrorsProperty);
    }

    #region Nested type: ErrorProvider

    public class ErrorProvider
    {
        private readonly string _message;

        public ErrorProvider(string message)
        {
            _message = message;
            ThrowValidationError = true;
        }

        public bool ThrowValidationError { get; set; }

        [DebuggerNonUserCode]
        public object ValidationError
        {
            get { return null; }
            set
            {
                if (ThrowValidationError)
                {
                    throw new Exception(_message);
                }
            }
        }
    }

    #endregion
}

Now we could use ValidationErrorBuilder class for errors creation. To make picture complete let’s add extensions methods SetErrorState and ClearErrorState to all controls.

public static class ValidationExtender
{
    public static void SetErrorState(this Control control, string message)
    {
        control.SetValue(Validation.ErrorsProperty, ValidationErrorBuilder.GetValidationErrors(message));
        control.SetValue(Validation.HasErrorProperty, true);
        VisualStateManager.GoToState(control, "InvalidUnfocused", true);
        control.GotFocus += ControlGotFocus;
        control.LostFocus += ControlLostFocus;
    }

    static void ControlLostFocus(object sender, RoutedEventArgs e)
    {
        var control = sender as Control;
        if (control != null && (Boolean)control.GetValue(Validation.HasErrorProperty))
        {
            VisualStateManager.GoToState(control, "InvalidUnfocused", true);
        }
    }

    static void ControlGotFocus(object sender, RoutedEventArgs e)
    {
        var control = sender as Control;
        if (control != null && (Boolean)control.GetValue(Validation.HasErrorProperty))
        {
            VisualStateManager.GoToState(control, "InvalidFocused", true);
        }
    }

    public static void ClearErrorState(this Control control)
    {
        control.GotFocus -= ControlGotFocus;
        control.LostFocus -= ControlLostFocus;
        control.SetValue(Validation.ErrorsProperty, null);
        control.SetValue(Validation.HasErrorProperty, false);
        VisualStateManager.GoToState(control, "Valid", true);
    }
}

As result we will have:

Download validation library solution (VS 2010): AndrewVeresov.ValidationWorkaround v1.1

Oct 20 2009

Resharper 5.0 First Public Build is now available

Uhh, it's a hot day: first the visual studio 2010 beta 2 has came out and now JetBrains released first public available build of the ReSharper 5.0 :)

Here is download page: http://www.jetbrains.net/confluence/display/ReSharper/ReSharper+5.0+Nightly+Builds

What's New in ReSharper 5.0

  • External Sources
  • Structured Patterns
  • Project Refactorings
  • Call and Value Tracking
  • Internationalization
  • Visual Studio 2010 support (coming soon)
  • ASP.NET markup support
  • ASP.NET MVC support
  • Intellisense improvements
  • Bookmarks
  • Inspect project/folder
  • Upgrade-to-LINQ analysis
  • Native NUnit support
  • Xml Formatting

Here you could find more info about R# 5.0 futures: ReSharper 5.0 Overview

Oct 20 2009

Visual Studio 2010 beta 2 - Web Installer

Hi, now is just 20th of October... but VS 2010 beta 2 will be available only at 21th :(

Can't wait? Ok, I have time machine for you:

Microsoft Visual Studio 2010 Ultimate Beta 2 — ENU (Web Installer)
http://go.microsoft.com/fwlink/?LinkId=151233

You are welcome! :)

Important: Remove ASP.NET MVC 1.1 beforeinstallation. If it does not want to uninstall saying "You first need to uninstall Microsoft Visual Studio 2010 Tools for MVC 1.1, then uninstall ASP.NET MVC 1.1 (this is the runtime component)" then delete next registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET\MVCTools 1.1 (if you run 64bit OS then that key will be under HKLM\SOFTWARE\Wow6432Node node)

P.S.

If you have problems with TFSObjectModel during uninstalling VS 2010 beta 1 then just uninstall it from Programs and Futures manually ;)

 

It's a great day! :)

Oct 19 2009

Visual Studio 2010 beta 2 is out!!!

Hi all!

Greate news: visual studio 2010 beta 2 is ready for download!

Currently it is available only for MSDN subscribers. At October 21st Microsoft will will push it to public.

Great news!

Aug 20 2009

Web setup project: the installer was interrupted before installer_name could be installed

If you getting "the installer was interrupted before installer_name could be installed" during installation created by web setup project, then try to check that AspNet is properly installed on IIS server.

To do it run next command:

"%windir%\Microsoft.NET\Framework\{framework_version}\aspnet_regiis.exe" -i

After this installer should work :)

Jul 09 2009

ReSharper for Visual Studio 2010 (Preview) is out !!!

We was whaiting it so long and now it happened!

JetBrains published first public available builds of ReSharper for Visual Studio 2010. You could get it from EAP download page.

Note that it is a preview:

ReSharper for Visual Studio 2010 (preview) - is an early build for those who are trying out Visual Studio 2010 and cannot live without ReSharper. It is neither 4.5.1 for VS10, nor 5.0 for VS10. It is intermediate result of our attempts to integrate with Visual Studio 2010 extensibility model, plus preliminary state of some of ReSharper v5 features (you will find some of them enabled). Complete roadmap for ReSharper v5 will be published soon.

Of cource it is still 79 Blockers issues in Bug Tracking database. But you have a greate chance to be first public betta tester Smile