none
Accessing Extension Methods via Data Binding RRS feed

  • Question

  • I have a simple class that defines a bunch of properties. This class implements an interface called IAddressInfo. This interface defines a number of simple properties (such as AddressLine1, City, PostalCode, etc.). I then created a class that defines an extension method called FormatAddress that has the following signature:

    public static string[] FormatAddress(this IAddressInfo address)  
    {  

    This allows me to have a number of different classes implement this interface and then get the extension method applied to that class. Works great in code. However, I need to access this extension method via Xaml.

    I have a WPF user control that has a data context set to an instance of a class that implements IAddressInfo. I can bind to all of the properties directly but I'd rather bind to the FormatAddress extension method which returns a string array that I bind to an ItemsControl like this:

    <ItemsControl ItemsSource="{Binding FormatAddress}"/>

    This does not work and I get an exception in the Output window of Visual Studio:

    System.Windows.Data Error: 39 : BindingExpression path error: 'FormatAddress' property not found on 'object' ''PatientSearchResult' (HashCode=9393955)'. BindingExpression:Path=FormatAddress; DataItem='PatientSearchResult' (HashCode=9393955); target element is 'ItemsControl' (Name=''); target property is 'ItemsSource' (type 'IEnumerable')


    I tried adding a namespace reference to the top of the Xaml file but that has no effect. 

    Is there a way to access extension methods via data binding in Xaml? Perhaps some special property syntax? Possibly a custom IValueConverter?

    Thanks.




     
    • Edited by dkehring Thursday, October 23, 2008 1:09 PM
    Wednesday, October 22, 2008 3:55 PM

Answers

  • -> Therefore, I'm curious (and researching) what the IL looks like and whether that might give me a hint as to how I could construct a binding that would work.

    Well, extension method is a C# syntax sugar, it's actually a plain static method call using (call IL opcode). So there is nothing special about extension method in terms of runtime behavior. And you could call extension method using static method calling syntax something like the following in C#:

    public static class Extensions
    {
        public static Double Add(this Double baseValue, Double value)
        {
            return baseValue + value;
        }
    }

    Double value = 5.0d;
    //use extension method syntax here.
    value = value.Add(4.0d);

    //use plain static method calling syntax here.
    value = Extensions.Add(value, 4.0d);

    So the above two calls are exactly the same, extension method are purely syntax sugar:)
    And the compelling reason why extension method is introduced into C# 3.0 is to make calling LINQ query methods more expressive, consider the following:

    //without extension method, you need to write the ugly code as follows.
    var sequence = Enumerable.Range(0, 100);
    sequence = Enumerable.Where(sequence, (i) => i > 5);
    sequence = Enumerable.Select(sequence, (i) => i );

    //with extension method, you could write something like the follow:
    sequence = Enumerable.Range(0, 100).Where((i) => i > 5).Select(i => i);

    Hope this clears things up a little bit.
    • Marked as answer by dkehring Tuesday, December 9, 2008 1:36 PM
    Thursday, October 30, 2008 6:29 AM

All replies

  • Extension method is a language feature rather than CLR runtime feature, so basically speaking, this type of binding is not supported.

    Hope this clears things up a little bit.

    • Marked as answer by Marco Zhou Wednesday, October 29, 2008 10:10 AM
    • Unmarked as answer by Marco Zhou Thursday, October 30, 2008 6:19 AM
    Tuesday, October 28, 2008 7:37 AM
  • Marco,

    I appreciate the reply but I'm not sure why you marked this as an answer. (I thought that was my job). Assuming extension methods are a language feature, they have to compile to something that is accessible at runtime. Therefore, I'm curious (and researching) what the IL looks like and whether that might give me a hint as to how I could construct a binding that would work. If not, then so be it and I will mark this response as an answer.
    Wednesday, October 29, 2008 10:27 AM
  • -> Therefore, I'm curious (and researching) what the IL looks like and whether that might give me a hint as to how I could construct a binding that would work.

    Well, extension method is a C# syntax sugar, it's actually a plain static method call using (call IL opcode). So there is nothing special about extension method in terms of runtime behavior. And you could call extension method using static method calling syntax something like the following in C#:

    public static class Extensions
    {
        public static Double Add(this Double baseValue, Double value)
        {
            return baseValue + value;
        }
    }

    Double value = 5.0d;
    //use extension method syntax here.
    value = value.Add(4.0d);

    //use plain static method calling syntax here.
    value = Extensions.Add(value, 4.0d);

    So the above two calls are exactly the same, extension method are purely syntax sugar:)
    And the compelling reason why extension method is introduced into C# 3.0 is to make calling LINQ query methods more expressive, consider the following:

    //without extension method, you need to write the ugly code as follows.
    var sequence = Enumerable.Range(0, 100);
    sequence = Enumerable.Where(sequence, (i) => i > 5);
    sequence = Enumerable.Select(sequence, (i) => i );

    //with extension method, you could write something like the follow:
    sequence = Enumerable.Range(0, 100).Where((i) => i > 5).Select(i => i);

    Hope this clears things up a little bit.
    • Marked as answer by dkehring Tuesday, December 9, 2008 1:36 PM
    Thursday, October 30, 2008 6:29 AM
  • you mentioned that using an IValueConverter would be acceptable.  that would definitely work.

    IAddressInfo.cs

    namespace EMDemo

    {

        public interface IAddressInfo

        {

            string City { get; set; }

            string PostalCode { get; set; }

        }

    }



    Person.cs

    namespace EMDemo

    {

        public class Person: INotifyPropertyChanged, IAddressInfo

        {

            private string _name;

            private string _city;

            private string _postalCode;

            private int _age;



            public String Name

            {

                get { return _name; }

                set { _name = value; NotifyPropertyChanged("Name"); }

            }


            public int Age

            {

                get { return _age; }

                set { _age = value; NotifyPropertyChanged("Age"); }

            }



            private void NotifyPropertyChanged(string p)

            {

                if (PropertyChanged != null)

                    PropertyChanged(this, new PropertyChangedEventArgs(p));

            }


            #region INotifyPropertyChanged Members


            public event PropertyChangedEventHandler PropertyChanged;


            #endregion


            #region IAddressInfo Members


            public string City

            {

                get

                {

                    return _city;

                }

                set

                {

                    _city = value; NotifyPropertyChanged("City"); 

                }

            }


            public string PostalCode

            {

                get

                {

                    return _postalCode;

                }

                set

                {

                    _postalCode = value; NotifyPropertyChanged("PostalCode"); 

                }

            }


            #endregion


            public static ObservableCollection<Person> GetAll()

            {

                ObservableCollection<Person> people = new ObservableCollection<Person>() {

                    new Person { Name="Joe", Age=29, City="City A", PostalCode="PC1" },

                    new Person { Name="Jane", Age=23, City="City B", PostalCode="PC2" }

                };

                return people;

            }

        }

    }




    EMConverter.cs

    namespace EMDemo

    {

        public class EMConverter: IValueConverter

        {

            #region IValueConverter Members


            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

            {

                IAddressInfo ia = value as IAddressInfo;

                return ia.FormatAddress();

            }


            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

            {

                throw new NotImplementedException();

            }


            #endregion

        }

    }


    Window1.xaml

    <Window x:Class="EMDemo.Window1"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        xmlns:local="clr-namespace:EMDemo"

        Title="Window1" Height="300" Width="300">

        <Window.Resources>

            <local:EMConverter x:Key="emcv" />

            <DataTemplate DataType="{x:Type local:Person}">

                <StackPanel Margin="3">

                    <TextBlock Text="{Binding Name}" />

                    <TextBlock Text="{Binding Age}" />

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

                </StackPanel>

            </DataTemplate>

        </Window.Resources>

        <Grid>

            <ItemsControl x:Name="ic" Height="200" />

        </Grid>

    </Window>



    Window1.xaml.cs

    namespace EMDemo

    {

        /// <summary>

        /// Interaction logic for Window1.xaml

        /// </summary>

        public partial class Window1 : Window

        {

            public Window1()

            {

                InitializeComponent();

                ic.ItemsSource = Person.GetAll();

            }

        }

    }


    Evan Chua-Yap
    • Proposed as answer by Evan Chua-Yap Thursday, October 30, 2008 5:49 PM
    Thursday, October 30, 2008 5:49 PM