1

I have a list of view models which I am binding to a TreeView, however these view models are representing a 'file system' like data structure with 'files' and 'folders'. So in the item template on my view for the tree view I have an image which should represent either a folder or a file.

Here is My XAML:

<StackPanel Orientation="Horizontal">
                                <!-- Folder Icon -->
                                <Image Width="15" Height="15" Stretch="Fill" Source="\Resources\Folder.png"></Image>

                                <Grid>
                                    <!-- Folder Name -->
                                    <Label Content="{Binding Path=FolderName}">
                                        <!-- Force Selection on Right Click -->
                                        <ACB:CommandBehaviourCollection.Behaviours>
                                            <ACB:BehaviourBinding Event="PreviewMouseRightButtonDown" Command="{Binding Path=MainModel.SelectTreeViewItem}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}}}"></ACB:BehaviourBinding>
                                        </ACB:CommandBehaviourCollection.Behaviours>
                                    </Label>

                                    <!-- Folder Name Editor -->
                                    <StackPanel Name="FolderEditor" Orientation="Horizontal" Visibility="Collapsed">
                                        <TextBox Text="{Binding Path=FolderName}" Width="130"></TextBox>
                                        <Button Content="Ok" Command="{Binding Path=RenameFolder}" CommandParameter="{Binding ElementName=FolderEditor}"></Button>
                                    </StackPanel>
                                </Grid>
                            </StackPanel>

So basically I want to know how to bind the source of the image object to my view models.

Thanks, Alex.

H.B.
  • 166,899
  • 29
  • 327
  • 400
Alex Hope O'Connor
  • 9,354
  • 22
  • 69
  • 112
  • possible duplicate of [WPF Image UriSource and Data Binding](http://stackoverflow.com/questions/20586/wpf-image-urisource-and-data-binding) – Alastair Pitts Oct 24 '11 at 03:05

3 Answers3

2

I think that the best way to do this is using a converter like this:

    public class EnumToResource : IValueConverter
    {
        public List<object> EnumMapping { get; set; }

        public EnumToResource()
        {
             EnumMapping = new List<object>();
        }

        public virtual object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            int adjustment = 0;
            if (parameter != null && !Int32.TryParse(parameter.ToString(), out adjustment))
            {
                adjustment = 0;
            }
            if (value == null) return this.EnumMapping.ElementAtOrDefault(0);
            else if (value is bool)
                return this.EnumMapping.ElementAtOrDefault(System.Convert.ToByte(value) + adjustment);
            else if (value is byte)
                return this.EnumMapping.ElementAtOrDefault(System.Convert.ToByte(value) + adjustment);
            else if (value is short)
                return this.EnumMapping.ElementAtOrDefault(System.Convert.ToInt16(value) + adjustment);
            else if (value is int)
                return this.EnumMapping.ElementAtOrDefault(System.Convert.ToInt32(value) + adjustment);
            else if (value is long)
                return this.EnumMapping.ElementAtOrDefault(System.Convert.ToInt32(value) + adjustment);
            else if (value is Enum)
                return this.EnumMapping.ElementAtOrDefault(System.Convert.ToInt32(value) + adjustment);

            return this.EnumMapping.ElementAtOrDefault(0);
        }

        public virtual object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

Then declare an enumeration called NodeType:

enum NodeType
{
    Folder,
    File,
}

In your view model you declare an INotifyPropertyChanged property called NodeType of the enumeration type.

Then in your XAML you declare the converter resource like this:

 <Converters:EnumToResource x:Key="IconConverter">
  <Converters:EnumToResource.EnumMapping>
   <BitmapImage UriSource="\Resources\Folder.png"/>
   <BitmapImage UriSource="\Resources\File.png"/>
  </Converters:EnumToResource.EnumMapping>
 </Converters:EnumToResource>

Finally you bind your property like this:

     <Image Source="{Binding Path=NodeType, Converter={StaticResource ResourceKey=IconConverter}}"/>

This way you do not need to deal with BitmapImage declarations and loading in your view model and you can still make it fully bindable.

Murven
  • 2,377
  • 17
  • 23
0

I found this solution very interesting. In studying it, I came up with a few modest improvements to the original code, which I submit in hopes someone else may benefit.

public class ResourceIndexer : IValueConverter
{
    // initialized via xaml
    public List<object> Resources { get; set; }

    public ResourceIndexer()
    {
        Resources = new List<object>();
    }

    public virtual object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
    {
        int index = ConvertToInt( value );

        if( parameter != null )
        {
            index += ConvertToInt( parameter );
        }

        var selected = this.Resources.ElementAtOrDefault( index );
        return selected;
    }

    public static int ConvertToInt( object value )
    {
        if( value == null )
        {
            throw new ArgumentNullException();
        }

        int index = 0;

        if( value is bool || value is Enum
        || value is byte || value is sbyte
        || value is short || value is ushort
        || value is int || value is uint
        || value is long || value is ulong
        || value is char
        )
        {
            index = System.Convert.ToInt32( value );
        }
        else if( value is string )
        {
            if( !int.TryParse( (string)value, out index ) )
                throw new ArgumentOutOfRangeException( "" );
            // else the conversion went into index
        }
        else
        {
            throw new NotSupportedException( $"cannot index non-integral type ({value.GetType().Name}" );
        }

        return index;
    }

    public virtual object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
    {
        throw new NotImplementedException();
    }
}
0

In your view models add new property Icon, i.e.

public BitmapImage Icon
    {
        get
        {
            return this._icon;
        }

        set
        {
            this._icon = value;
            this.NotifyPropertyChanged("Icon");
        }
    } 

change Image's source in DataTemplate to: Source="{Binding Path=Icon}" and load your icons in view model accordingly, i.e. for folder view model use something like this:

this.Icon = new BitmapImage(new Uri("/Your_assembly;component/Images/folder.png", UriKind.Relative));
dodsky
  • 504
  • 4
  • 11