Question

The lack of questions on the subject may be an indication of code smell here, but... Is it possible to write an extension method for a class and databind to that like you would a property?

The assumption is that I am provided with a class structure that I'm not able to fundamentally change, but I want to express a series of its boolean properties as a string for display purposes.

Simplified base class:

public class Transmission
{
    public int ID { get; set; }
    public bool Cancelled { get; set; }
    public bool Stored { get; set; }
    public bool Recorded { get; set; }
}

My extension method:

public static class Extensions
{
    public static string Status(this Transmission trans)
    {
        StringBuilder sb = new StringBuilder("|");
        if (trans.Cancelled)
            sb.Append("| Cancelled ");
        if (trans.Recorded)
            sb.Append("| Recorded ");
        if (trans.Stored)
            sb.Append("| Stored ");
        sb.Append("||");

        return sb.ToString();
    }
}

To add further complexity, I'm being passed a list of these things, and I'm trying to bind to a datagrid (with severely limited XAML experience).

<GroupBox Header="Here is an amazing list of results for you to violate horribly.">
    <DataGrid ItemsSource="{Binding Transmissions, Mode=OneWay}" AutoGenerateColumns="False">
         <DataGrid.Columns>
            <DataGridTextColumn Width="*" Header="Local ID" Binding="{Binding ID, Mode=OneWay}"/>
            <DataGridTextColumn Width="*" Header="Status" Binding="{Binding Status, Mode=OneWay}"/>
         </DataGrid.Columns>
    </DataGrid>
</GroupBox>

I've tested the code and was able to bind to ID without any difficulty. The 'Status' however, is not being picked up at all. Is there a trick to binding to an extension property? Or would it be more prudent to just write a decorator/facade class and bind to that?

Was it helpful?

Solution 2

When you are passed the list of Transmission objects you can use the Façade pattern and store them in a container designed...

public class TransmissionContainer : INotifyPropertyChanged
{
    private readonly Transmission _transmission;
    public TransmissionContainer(Transmission transmission)
    {
        _transmission = transmission;
    }
    private int _id;
    public int Id
    {
        [DebuggerStepThrough]
        get { return _transmission.ID; }
        [DebuggerStepThrough]
        set
        {
            if (value != _transmission.ID)
            {
                _transmission.ID = value;
                OnPropertyChanged("Id");
            }
        }
    }
    public bool Cancelled
    {
        [DebuggerStepThrough]
        get { return _transmission.Cancelled }
        [DebuggerStepThrough]
        set
        {
            if (value != _transmission.Cancelled)
            {
                _transmission.Cancelled = value;
                OnPropertyChanged("Cancelled");
                OnPropertyChanged("Status");
            }
        }
    }
    public string Status
    {
        [DebuggerStepThrough]
        get
        {
            StringBuilder sb = new StringBuilder("|");
            if (_transmission.Cancelled)
                sb.Append("| Cancelled ");
            if (_transmission.Recorded)
                sb.Append("| Recorded ");
            if (_transmission.Stored)
                sb.Append("| Stored ");
            sb.Append("||");
            return sb.ToString();
        }
    }
    //
    // code in other properties here
    //
    #region INotifyPropertyChanged Implementation
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string name)
    {
        var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
    #endregion
}

This is a container class that creates a façade to which your Xaml can bind transparently. As shown, each of the exposed properties simply echoes back the value in the private instance of Transmission. Changes are relayed to the WPF binding engine via the INotifyPropertyChanged interface.

To create an instance you can construct it with the original Transmission class. To bind a collection of these containers, you can declare an ObservableCollection of type TransmissionContainer. Doing that means the list is bound in addition to the various changes in the properties.

In this approach, your 'extension' property is just another exposed property that has no setter. Note that changes to the other members that affect Status call the notification on behalf of the 'extension' property. Coding in the remaining members of the Transmission class should take about 20 minutes...

OTHER TIPS

That is what you usually use a pattern like MVVM for. You add properties to the view-model which are based on the model and only relevant to the view. The view-model can contain a reference to the model to either bind directly to its properties or to mirror them on the view-model (for decoupling i would opt for the latter).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top