Freitag, 20. Dezember 2013

Use MEF to compose objects of types that are unknown at compile time / IoC implementation using MEF

With MEF it is quite easy to create a generic base class that composes objects of a type that is known at compile time.
This was explained in a previous post Generic base class for composing objects with MEF.
This works perfect when the Type is known a compile time. But what if we want to load objects that are defined in a config file? What if we want to create a IoC implementation with MEF where the proper instance is defined in a config file and can be set at runtime. This way the Type is not know at compile time even though the composition relies on knowing the type.
To achieve this there needs to be a ComposablePartCatalog that can compose objects depending on the type.

public class ComposeableTypeCatalog : ComposablePartCatalog
{
    ComposablePartCatalog _catalog;

    public ComposeableTypeCatalog(ComposablePartCatalog catalog)
    {
        _catalog = catalog;
    }

    public override IQueryable<ComposablePartDefinition> Parts
    {
        get { return _catalog.Parts; }
    }

    public override IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition importDef)
    {
        var result = new List<Tuple<ComposablePartDefinition, ExportDefinition>>();

        // check all parts in that catalog
        foreach (ComposablePartDefinition partDef in Parts)
        {
            // for each part, examine if any export definition matches the requested import definition.
            foreach (ExportDefinition exportDef in partDef.ExportDefinitions)
            {
                // the import has to be made on object
                if(Type.GetType(importDef.ContractName) == typeof(object))
                {
                    var matchingExport = new Tuple<ComposablePartDefinition, ExportDefinition>(partDef, exportDef);                        
                    result.Add(matchingExport);
                }
            }
        }
        return result;
    }
}

In the composition we need to use a TypeCatalog to define the type that should be composed. The big drawback with this approach ist the fact that the Imports have to be made on System.object or IEnumerable<System.object> and not on a concrete type.
public class TypeComposer
{
    private CompositionContainer _container;

    [ImportMany]
    public IEnumerable<object> Imports { get; set; }

    public object Compose(Type type)
    {
        // an aggregate catalog that combines multiple catalogs
        var agregatecatalog = new AggregateCatalog();

        // load only items of type
        agregatecatalog.Catalogs.Add(new TypeCatalog(type));

        // create a catalog that loads the given types into the IEnumerable<object>
        var typecatalog = new ComposeableTypeCatalog(agregatecatalog);

        // create the CompositionContainer with the parts in the catalog
        _container = new CompositionContainer(typecatalog);

        // fill the imports of this object
        try
        {
            _container.ComposeParts(this);
        }
        catch (CompositionException compositionException)
        {
            Console.WriteLine(compositionException.ToString());
        }

        if (Imports == null)
            return null;

        return Imports.FirstOrDefault();
    }
}
This class now imports all objects that are marked with an ExportAttribute containing the type that is passed with the parameter.
[Export(typeof(ComposeableClass))]
public class ComposeableClass
{
}
[TestMethod]
public void TypeComposerTest()
{
    var composer = new TypeComposer();
    var part = composer.Compose(typeof(ComposeableClass));
    Assert.IsNotNull(part as ComposeableClass);
}
As mentioned previously,the big drawback is the fact that the composition is made on System.object and not on the concrete type. This means the object has to be cast into the expected type which can lead to errors.

Donnerstag, 19. Dezember 2013

Generic base class for composing objects using MEF

If MEF is used extensively, it is not practicable to implement the composition for every class or type that has to be compsed. Why not create a generic base class that can load all of the objects that we need.
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

public class Composer<T>
{
    public T Compose<T>()
    {
        Compose();

        if (Imports == null)
            return default(T);

        return (T)(object)Imports.FirstOrDefault();
    }
        
    private static AggregateCatalog _catalog;

    private CompositionContainer _container;

    [ImportMany]
    public IEnumerable<T> Imports { get; set; }

    private void Compose()
    {
        // an aggregate catalog that combines multiple catalogs
        var catalog = new AggregateCatalog();

        // load all assemblies in the current directory
        catalog.Catalogs.Add(new DirectoryCatalog(".", "*.exe"));

        Compose(catalog);
    }

    private void Compose(AggregateCatalog catalog)
    {
        _catalog = catalog;

        // create the CompositionContainer with the parts in the catalog
        _container = new CompositionContainer(_catalog);

        // fill the imports of this object
        try
        {
            _container.ComposeParts(this);
        }
        catch (CompositionException compositionException)
        {
            Console.WriteLine(compositionException.ToString());
        }
    }
}

This class can now be used to Compose objects that are marked with the ExportAtribute.
[Export(typeof(ComposeableClass))]
public class ComposeableClass
{
}
[TestMethod]
public void GenericComposerComposeTest()
{
    var composer = new Composer<ComposeableClass>();
    var part = composer.Compose<ComposeableClass>();
    Assert.IsNotNull(part);
}

Dienstag, 11. Juni 2013

Changing the Image Source property with a trigger

To change the Source of an Image within a Trigger, the Source has to be set in a Style with the help of a Setter. This code won't work because the Trigger can't change/override a Property that has been set outside of the Style.
<Image Source="..\..\Images\OK.png">
    <Image.Style>
        <Style TargetType="{x:Type Image}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding HasError}" Value="True" >
                    <Setter Property="Source" Value="..\..\Images\Error.png"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Image.Style>
</Image>
Remove the Source attribute from the Image tag at the top and add it as a Setter in the Style. This will override whatever is applied by the style. You can move it to another DataTrigger or you can put it in a normal setter like so.
<Image>
    <Image.Style>
        <Style TargetType="{x:Type Image}">
            <Setter Property="Source" Value="..\..\Images\OK.png" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding HasError}" Value="True" >
                    <Setter Property="Source" Value="..\..\Images\Error.png"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Image.Style>
</Image>
This approach can also be used on a TextBlock if the text has to be changed with a Trigger.

Freitag, 11. Mai 2012

Seting Visibility on a GridViewColum of a ListView in WPF

There are two different ways how the Visibility of a GridViewColumn can be set.

1. With attached properties
2. Extending the GridViewColumn

In the attached property version, we save the original width of the column in another attached property, whenever the visibility is set to something else than Visible. After that we set the Width to 0.
This way the column disappears.

When resetting the Visibility to visible, we retrieve the original saved width and restore it to the Width property again.

public class GridViewColumnVisibilityManager
{
    public static Visibility GetVisibility(DependencyObject o)
    {
        return (Visibility)o.GetValue(VisibilityProperty);
    }

    public static void SetVisibility(DependencyObject obj, Visibility value)
    {
        obj.SetValue(VisibilityProperty, value);
    }

    public static readonly DependencyProperty VisibilityProperty =
        DependencyProperty.RegisterAttached("Visibility", typeof(Visibility),
        typeof(GridViewColumnVisibilityManager),
        new FrameworkPropertyMetadata(Visibility.Visible, 
        FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
        new PropertyChangedCallback(OnVisibilityPropertyChanged)));

    private static void OnVisibilityPropertyChanged(DependencyObject d, 
                                     DependencyPropertyChangedEventArgs e)
    {
        var column = d as GridViewColumn;
        if (column != null)
        {
            var visibility = GetVisibility(column);
            if (visibility == Visibility.Visible)
            {
                // set the with back to the original
                column.Width = GetVisibleWidth(column);
            }
            else
            {
                // store the original width
                SetVisibleWidth(column, column.Width);
                // set the column width to 0 to hide it
                column.Width = 0.0;
            }
        }
    }

    public static double GetVisibleWidth(DependencyObject obj)
    {
        return (double)obj.GetValue(VisibleWidthProperty);
    }

    public static void SetVisibleWidth(DependencyObject obj, double value)
    {
        obj.SetValue(VisibleWidthProperty, value);
    }

    /// <summary>
    /// dpenendency property that stores the last visible width
    /// whenever the visibility changes this propert is used to set or get the width
    /// </summary>
    public static readonly DependencyProperty VisibleWidthProperty =
        DependencyProperty.RegisterAttached("VisibleWidth", 
                typeof(double), 
                typeof(GridViewColumnVisibilityManager), 
                new UIPropertyMetadata(double.NaN));

}

<ListView ItemsSource="{Binding DataObjects}" Grid.Column="1" >
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" 
                     foo:GridViewColumnVisibilityManager.Visibility="{Binding IsVisible, Converter={StaticResource boolToVis}}"/>
                <GridViewColumn Header="Vorname" DisplayMemberBinding="{Binding Vorname}"/>
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>


The extension works the same way except that we can use a variable to store the original width instead of an attached property.

public class GridViewColumnExt : GridViewColumn
{
    public Visibility Visibility
    {
        get
        {
            return (Visibility)GetValue(VisibilityProperty);
        }
        set
        {
            SetValue(VisibilityProperty, value);
        }
    }

    public static readonly DependencyProperty VisibilityProperty =
        DependencyProperty.Register("Visibility", typeof(Visibility), 
        typeof(GridViewColumnExt),
        new FrameworkPropertyMetadata(Visibility.Visible, 
        FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
        new PropertyChangedCallback(OnVisibilityPropertyChanged)));

    private static void OnVisibilityPropertyChanged(DependencyObject d, 
                                  DependencyPropertyChangedEventArgs e)
    {
        var column = d as GridViewColumnExt;
        if (column != null)
        {
            column.OnVisibilityChanged((Visibility)e.NewValue);
        }
    }

    private void OnVisibilityChanged(Visibility visibility)
    {
        if (visibility == Visibility.Visible)
        {
            Width = _visibleWidth;
        }
        else
        {
            _visibleWidth = Width;
            Width = 0.0;
        }
    }

    double _visibleWidth;
}

<ListView ItemsSource="{Binding DataObjects}" Grid.Column="1" >
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <c:GridViewColumnExt Header="Name" DisplayMemberBinding="{Binding Name}" 
                     Visibility="{Binding IsVisible, Converter={StaticResource boolToVis}}"/>
                <GridViewColumn Header="Vorname" DisplayMemberBinding="{Binding Vorname}"/>
            </GridView.Columns>
        </GridView>
    </ListView.View>
</ListView>

Donnerstag, 12. April 2012

Binding multiple ComboBoxes to the same collection and using Filters

When multiple ComboBoxes bind to the same collection and the collection uses a Filtering, the items of all ComboBoxes get filtered. Not as expected only the one using the filter. If a ComboBox already has a value selected and another ComboBox uses a filter on the collection, the first ComboBox could lose its value because the SelectedItem is no longer contained in the displayed list.

This hapens because in WPF all Collections contain a default ICollectionView. This view can be retrieved with:

var view = CollectionViewSource.GetDefaultView(collection);


This can easily be handled if each ComboBox binds to a unique ICollectionView. It doesn't matter if the underlying Collection is the same as long as the ICollectionViews differ.


IList<ComboBoxExtDataObject> _dataObjects;
public IList<ComboBoxExtDataObject> DataObjects
{
    get
    {
        if (_dataObjects == null)
        {
            _dataObjects = new ObservableCollection<ComboBoxExtDataObject>();

            for (int i = 1; i <= 50; i++)
                _dataObjects.Add(new ComboBoxExtDataObject { Id = i, Name = "Name" + i });
        }
        return _dataObjects;
    }
}

ICollectionView _sources1;
public ICollectionView Sources1
{
    get
    {
        if (_sources1 == null)
        {
            var lst = new CollectionViewSource();
            lst.Source = _dataObjects;
            _sources1 = lst.View;
        }
        return _sources1;
    }
}

ICollectionView _sources2;
public ICollectionView Sources2
{
    get
    {
        if (_sources2 == null)
        {
            var lst = new CollectionViewSource();
            lst.Source = _dataObjects;
            _sources2 = lst.View;
        }
        return _sources2;
    }
}

In this case I bind the ComboBox1 to Sources1 and the ComboBox2 to Sources2. Now I can easily use the Filtering that I added to my ComboBox without changing the CollectionView of the other ComboBox.

Filering in ComboBoxes

To achieve filtering in a ComboBox I simply extended the ComboBox.

If I want to Filter the items in the DropDown list I simply bind to the FilterString property.
This example can even handle wildcards.

public class ComboBoxExt : ComboBox
{

#region Filter

#region Implementation

IList<object> _displayedItems;

/// <summary>
/// refreshes the filtering of the combobox according to the string in
/// <see cref="FilterString"/>
/// </summary>
private void RefreshFilter()
{
    var source = ItemsSource as ICollectionView;
    if (source == null)
        source = Items as ICollectionView;
            
    if (source != null)
    {
        if (_displayedItems == null)
            _displayedItems = new ObservableCollection<object>();
        _displayedItems.Clear();

        source.Filter = new Predicate<object>(Filter);

        if (_displayedItems.Count > 0)
        {
            var focusedElement = Keyboard.FocusedElement;

            SelectedItem = _displayedItems[0];

            if (focusedElement != null)
                focusedElement.Focus();
        }
    }
}

/// <summary>
/// method that gets called by the predicate in the ICollectionView.Filter that
/// filters the entries in the dropdown
/// according to the string in <see cref="FilterString"/>
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
private bool Filter(object obj)
{
    // no filter is set
    if (string.IsNullOrEmpty(FilterString))
        return true;

    // no filterpath is set
    if (string.IsNullOrEmpty(FilterMemberPath))
        return true;

    // get the value from the property out of the bound object
    var value = GetValueFromBindingPath(obj, FilterMemberPath);
    if (value == null)
        return false;

    //
    // matching filterstring and value using regex found on:
    // http://www.codeproject.com/Articles/11556/Converting-Wildcards-to-Regexes
    //

    // accept sql and dos Wildcards
    // convert all sql wildcards to dos wildcards
    var filter = FilterString.Replace("%", "*").Replace("_", "?");

    // convert all wildcards to regex wildcards and add a * to search all prepending chars
    filter = "^" + Regex.Escape(filter).Replace("\\*", ".*").Replace("\\?", ".") + ".*$";
            
    // match the value with the regex filter
    bool isFiltered = Regex.IsMatch(value.ToString(), filter, RegexOptions.IgnoreCase);
            
    if (isFiltered)
        _displayedItems.Add(obj);

    return isFiltered;
}

#endregion

#region DependcyProperties

#region FilterMemberPath

//
// the filtermemeberpath is used to get the property that the filter should be used on
// 

/// <summary>
/// gets or set the object path to the property that is filtered on the bound object
/// </summary>
public string FilterMemberPath
{
    get
    {
        return (string)GetValue(FilterMemberPathProperty);
    }
    set
    {
        SetValue(FilterMemberPathProperty, value);
    }
}

/// <summary>
/// the dependencyproperty for the <see cref="FilterMemberPath"/> property
/// </summary>
public static readonly DependencyProperty FilterMemberPathProperty =
    DependencyProperty.Register("FilterMemberPath", typeof(string), typeof(ComboBoxExt), 
      new FrameworkPropertyMetadata(null, 
           new PropertyChangedCallback(OnFilterMemberPathPropertyChanged)));

private static void OnFilterMemberPathPropertyChanged(DependencyObject sender, 
                                        DependencyPropertyChangedEventArgs args)
{
}

#endregion

#region FilterString

/// <summary>
/// gets or sets the string that is used for filtering the combobox
/// </summary>
public string FilterString
{
    get
    {
        return (string)GetValue(FilterStringProperty);
    }
    set
    {
        SetValue(FilterStringProperty, value);
    }
}
        
/// <summary>
/// dependencyproperty for the <see cref="FilterString"/> property
/// </summary>
public static readonly DependencyProperty FilterStringProperty =
    DependencyProperty.Register(
    "FilterString", 
    typeof(string), 
    typeof(ComboBoxExt), 
    new FrameworkPropertyMetadata(
        null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | 
              FrameworkPropertyMetadataOptions.AffectsRender | 
              FrameworkPropertyMetadataOptions.AffectsParentMeasure,
        new PropertyChangedCallback(OnFilterStringPropertyChanged)));


private static void OnFilterStringPropertyChanged(DependencyObject sender, 
                                    DependencyPropertyChangedEventArgs args)
{
    var dt = sender as ComboBoxExt;
    if (dt == null)
        return;
            
    dt.RefreshFilter();
}

#endregion
        
#endregion

#endregion

#region Value from Binding / Path

// 
// we create a virtual binding and bind to the property from the
// path that was set in the FiltermemberPath property
// with the binding we can easily retreave the object that is
// contained behind the property
//
// tests with over 5000 items have shown 
// that this has no impact on performance
//

/// <summary>
/// gets the object/value from a property from the object according to the path
/// </summary>
/// <param name="obj"></param>
/// <param name="propertyPath"></param>
/// <returns></returns>
public static object GetValueFromBindingPath(object obj, string propertyPath)
{
    Binding binding = new Binding(propertyPath);
    binding.Mode = BindingMode.OneTime;
    binding.Source = obj;
    BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, binding);
    return _dummy.GetValue(Dummy.ValueProperty);
}

// dummy object for getting the value from a path on a object
private static readonly Dummy _dummy = new Dummy();

private class Dummy : DependencyObject
{
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(object), typeof(Dummy), 
                                             new UIPropertyMetadata(null));
}

#endregion
}

Retrieving the value of a property from a DataBound object

In WPF it's easy to bind a object to a DependencyProperty and can be accessed just like any normal Property. You can even access the Properties of that object and get the values of these if you know what type they are.
But what is if the Binding is of a random type and you would like to access the value of a property on that unknown?

Let's say for example you want to add a Filtering to the ComboBox.
ComboBoxes can bind to any type as long as it implements IEnumerable. But for Filtering you need to know the value of a Property inside the objects in the IEnumerable.

To access the value of a Property on the Bound objects, you need to create a new Binding on the desired Property for each of the objects in the IEnumerable. The thing you need to tell the ComboBox is the name of the Property of which you would like to get the value of. In the example I simply created a DependencyProperty containing the name(path). You could also use the DisplayMemberPath or analyse the Template of the Bound objects.

public bool Filter(object obj)
{
    // get the value from the property out of the bound object
    var value = GetValueFromBindingPath(obj, FilterMemberPath);
    if(value != null)
    {
        if(value.ToString().Contains(FilterString))
           return true;
    }
    return false;
}

/// <summary>
/// the name of the property that the filtering should be made on in the bound objects
/// </summary>
public string FilterMemberPath
{
    get { return (string)GetValue(FilterMemberPathProperty); }
    set { SetValue(FilterMemberPathProperty, value); }
}

public static readonly DependencyProperty FilterMemberPathProperty =
    DependencyProperty.Register("FilterMemberPath", typeof(string), typeof(ComboBoxExt), 
                                new UIPropertyMetadata(null));

#region Value from Binding / Path

// 
// we create a virtual binding and bind to the property from the
// path that was set in the FiltermemberPath property
// with the binding we can easily retreave the object that is
// contained behind the property
//
// tests with over 5000 items have shown 
// that this has no impact on performance
//

/// <summary>
/// gets the object/value from a property from the object according to the path
/// </summary>
/// <param name="obj"></param>
/// <param name="propertyPath"></param>
/// <returns></returns>
public static object GetValueFromBindingPath(object obj, string propertyPath)
{
    Binding binding = new Binding(propertyPath);
    binding.Mode = BindingMode.OneTime;
    binding.Source = obj;
    BindingOperations.SetBinding(_dummy, Dummy.ValueProperty, binding);
    return _dummy.GetValue(Dummy.ValueProperty);
}

// dummy object for getting the value from a path on a object
private static readonly Dummy _dummy = new Dummy();

private class Dummy : DependencyObject
{
    public static readonly DependencyProperty ValueProperty =
        DependencyProperty.Register("Value", typeof(object), typeof(Dummy), 
                                    new UIPropertyMetadata(null));
}

#endregion


This looks like a hack and maybe (most probably) it is a big one.
But it works quite well and its quite fast.