0

I've got a WPF Form (I am totally a beginner in WPF) which contains a Datagrid. This Datagrid gets its Content by a simple List<AudioFile>. Inside the class Mp3File which extends AudioFile (It's inside a PCL) is a method called GetCoverAsByteArray(), which returns the cover of a loaded AudioFile as a byte[]. Now i want to show the cover Image in the DataGrid, but i don't know how to do it. Can you please help me?

Here is the code i have so far:

<DataGrid x:Name="tvFiles" AutoGenerateColumns="False" MaxColumnWidth="1000" Margin="10,95,10,10" MinHeight="100">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="Cover" Width="*" MinWidth="64">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Header="Filename" Width="*" MinWidth="100" Binding="{Binding Filename}"/>
                        <DataGridTextColumn Header="Artist" Width="*" MinWidth="50" Binding="{Binding Artist}"/>
                        <DataGridTextColumn Header="Title" Width="*" MinWidth="50" Binding="{Binding Title}"/>
                        <DataGridTextColumn Header="Album" Width="*" MinWidth="50" Binding="{Binding Album}"/>
                        <DataGridTextColumn Header="BPM" Width="*" MinWidth="50" Binding="{Binding BPM}"/>
                        <DataGridTextColumn Header="Comment" Width="*" MinWidth="100" Binding="{Binding Comment}"/>
                        <DataGridTextColumn Header="Year" Width="*" MinWidth="40" Binding="{Binding Year}"/>
                        <DataGridTextColumn Header="Key" Width="*" MinWidth="40" Binding="{Binding Key}"/>
                        <DataGridTextColumn Header="Bitrate" Width="*" MinWidth="60" Binding="{Binding Bitrate}"/>
                        <DataGridTextColumn Header="Length" Width="*" MinWidth="50" Binding="{Binding Duration}"/>
                    </DataGrid.Columns>
                </DataGrid>

Thanks a lot for every help

EDIT 1

I implemented a Converted like Dennis has said and now my code looks like this:

class ByteArrayToImageConverter : IValueConverter {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {

        byte[] bytes = (byte[])value;

        if (bytes == null || bytes.Length == 0) return null;

        var image = new BitmapImage();
        using (var mem = new MemoryStream(bytes)) {
            mem.Position = 0;
            image.BeginInit();
            image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.UriSource = null;
            image.StreamSource = mem;
            image.EndInit();
        }
        image.Freeze();
        return image;
    }

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

<Window.Resources>
    <local:ByteArrayToImageConverter x:Key="converter" />
</Window.Resources>

<DataGrid x:Name="tvFiles" AutoGenerateColumns="False" MaxColumnWidth="1000" Margin="10,95,10,10" MinHeight="100">
                    <DataGrid.Columns>
                        <DataGridTemplateColumn Header="Cover" Width="*" MinWidth="64">
                            <DataGridTemplateColumn.CellTemplate>
                                <DataTemplate>
                                    <Image Source="{Binding GetCoverAsByteArray, Converter={StaticResource converter}}"/>
                                </DataTemplate>
                            </DataGridTemplateColumn.CellTemplate>
                        </DataGridTemplateColumn>
                        <DataGridTextColumn Header="Filename" Width="*" MinWidth="100" Binding="{Binding Filename}"/>
                        <DataGridTextColumn Header="Artist" Width="*" MinWidth="50" Binding="{Binding Artist}"/>
                        <DataGridTextColumn Header="Title" Width="*" MinWidth="50" Binding="{Binding Title}"/>
                        <DataGridTextColumn Header="Album" Width="*" MinWidth="50" Binding="{Binding Album}"/>
                        <DataGridTextColumn Header="BPM" Width="*" MinWidth="50" Binding="{Binding BPM}"/>
                        <DataGridTextColumn Header="Comment" Width="*" MinWidth="100" Binding="{Binding Comment}"/>
                        <DataGridTextColumn Header="Year" Width="*" MinWidth="40" Binding="{Binding Year}"/>
                        <DataGridTextColumn Header="Key" Width="*" MinWidth="40" Binding="{Binding Key}"/>
                        <DataGridTextColumn Header="Bitrate" Width="*" MinWidth="60" Binding="{Binding Bitrate}"/>
                        <DataGridTextColumn Header="Length" Width="*" MinWidth="50" Binding="{Binding Duration}"/>
                    </DataGrid.Columns>
                </DataGrid>

Now i get the following Message when loading a AudioFile into the Datagrid:

System.Windows.Data Error: 40 : BindingExpression path error: 'GetCoverAsByteArray()' property not found on 'object' ''Mp3File' (HashCode=54312533)'. BindingExpression:Path=GetCoverAsByteArray(); DataItem='Mp3File' (HashCode=54312533); target element is 'Image' (Name=''); target property is 'Source' (type 'ImageSource')

System.Windows.Data Error: 40 : BindingExpression path error: 'Key' property not found on 'object' ''Mp3File' (HashCode=54312533)'. BindingExpression:Path=Key; DataItem='Mp3File' (HashCode=54312533); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

Community
  • 1
  • 1
DirtyNative
  • 2,553
  • 2
  • 33
  • 58

2 Answers2

2

Data binding works for properties only. You have to add a property to AudioFile class to return cover data. If you don't want to change AudioFile for some reasons, then map it/wrap it into view model, and place property into that view model.

Then you'll have two options.

Option 1.

Instead of public byte[] CoverAsByteArray { get; } you can write a property, which returns ImageSource instance, something like public ImageSource CoverAsImageSource { get; }.

XAML will look like this:

<DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <Image Source="{Binding CoverAsImageSource}"/>
    </DataTemplate>
</DataGridTemplateColumn.CellTemplate>

Option 2.

You can write a value converter to convert byte[] value into ImageSource.

In this case, XAML will look like this:

<DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <Image Source="{Binding CoverAsByteArray, Converter={StaticResource YourConverterKey}}"/>
    </DataTemplate>
</DataGridTemplateColumn.CellTemplate>

Assuming, that byte[] represents a bitmap, you can use, e.g., this answer to convert it to appropriate image source.

Community
  • 1
  • 1
Dennis
  • 37,026
  • 10
  • 82
  • 150
  • Sorry, i forgot to mention that `AudioFile` is inside a PCL, so Option 1 will not work :/ In Option 2, what is `YourConverterKey` ? – DirtyNative Oct 19 '15 at 13:02
  • I've mentioned, that you can map `AudionFile` to view model (or wrap it into view model). That's why view models exist. `YourConverterKey` is a key of your `IValueConverter` implementation, declared somewhere above in resources. See this link, for example: http://www.wpftutorial.net/ValueConverters.html – Dennis Oct 19 '15 at 13:04
1

you can use Microsoft.WindowsAPICodePack.Shell to get image from your mp3 file

private string cacheLocalFile(string mp3fileName)
    {
        try
        {
            using (var shell = ShellFile.FromParsingName(mp3fileName))
            {
                Bitmap bmp = shell.Thumbnail.Bitmap;
                var cachedFileName = shell.Properties.System.FileName.Value;
                bmp.Save(Path.Combine(AppCacheDirectory, cachedFileName), ImageFormat.Jpeg);
                bmp.Dispose();
                return Path.Combine(AppCacheDirectory, cachedFileName);
            }
        }
        catch
        {
            return String.Empty;
        }

    }

you just got thumbnail, saves it to the file and returns file src to Image element, if you don't want to cache image you can just convert bitmap to bitmapImage and bind it to image element in view at all.

  • Alright, i made it with Dennis answeres. I implemented a new property called CoverByBytes and set the binding correctly. Thanks a lot all for your help – DirtyNative Oct 19 '15 at 14:17