-1

I created an ImageColumn for a DataGrid object. However I have no cluehow to bind a relative image path to it. I need something like this, but in C#.

<Image Source="pack://application:,,,/Images/Folder-icon.png"/>

This is how I create my ImageColumn

public static DataGridTemplateColumn createImageColumn(string header, Binding b, Size s)
{
    DataTemplate dt = new DataTemplate();
    dt.DataType = typeof(Image);
    FrameworkElementFactory dtFactory = new FrameworkElementFactory(typeof(StackPanel));
    dtFactory.Name = "stack";
    dtFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);


    FrameworkElementFactory imageHolder = new FrameworkElementFactory(typeof(Image));
    imageHolder.Name = header;
    imageHolder.SetValue(Image.SourceProperty, b);
    imageHolder.SetValue(Image.WidthProperty, s.Width);
    imageHolder.SetValue(Image.HeightProperty, s.Height);
    imageHolder.SetValue(Image.HorizontalAlignmentProperty, HorizontalAlignment.Center);
    imageHolder.SetValue(Image.VerticalAlignmentProperty, VerticalAlignment.Center);
    dtFactory.AppendChild(imageHolder);

    dt.VisualTree = dtFactory;

    DataGridTemplateColumn imageColumn = new DataGridTemplateColumn();
    imageColumn.Header = header;
    imageColumn.CellTemplate = dt;
    return imageColumn;
}

And this is my current binding.

Binding b = new Binding();
b.Path = new PropertyPath("IMAGE_PATH");
b.RelativeSource = new RelativeSource(RelativeSourceMode.Self);

Edit: "IMAGE_PATH" is another column of the DataGrid. It holds the relative path to the image file (relative from the applications root). If I add an absolute path instead, it works. But not with a relative path.

More about the project: The datagrid holds item information (Part number, image, description, price). The user should be able to add images. The images will be copied into the application like:

APPLICATION_ROOT/Images/Item/file.png

This path will be saved in the database at the "IMAGE_PATH" column.

Images/Item/file.png

Later this path will be used to draw the image in the ImageColumn. However it's a relative path and it's only working with an absolute path yet.

Hope this makes it more clear. Thank you!

dbc
  • 104,963
  • 20
  • 228
  • 340
  • what is IMAGE_PATH? is it a filename or a property containing a filename or a property containing a URI or a property containing an Image? and what do you mean by relative path? do you mean relative URI or relative binding path? – Bizhan Jul 22 '18 at 10:56
  • you may want to read this page https://learn.microsoft.com/en-us/dotnet/framework/wpf/app-development/pack-uris-in-wpf – Bizhan Jul 22 '18 at 11:07
  • It's unclear why you think you need a Binding at all. Why don't you simply assign a BitmapImage to the Source property? Like `imageHolder.SetValue(Image.SourceProperty, new BitmapImage(new Uri(...)));` – Clemens Jul 22 '18 at 18:13
  • Hi, thanks for the answers. The "IMAGE_PATH" is diffrent column of the datagrid holding the relative path to the image "/folder/folder/file.png". – Andreas Reitberger Jul 23 '18 at 04:07
  • so it's not a resource anyway... and you want to display the path AND the Image? In this case you may look here: https://stackoverflow.com/questions/6111470/xaml-bitmap-urisource-absolute-path-works-relative-path-does-not-why/6111529#6111529 – dba Jul 23 '18 at 06:27
  • @AndreasReitberger *"The images will be copied into the application [folder]"* might not work due to limited file access privileges of the application. Better copy files to the `ApplicationData` directory. See here: https://msdn.microsoft.com/en-us/library/system.environment.specialfolder(v=vs.110).aspx. Then load them by a simply absolute path URI. – Clemens Jul 23 '18 at 07:46
  • @dba No need to show both, the image and the path. It would be fine if only the image is shown. So the plan is to convert the content of the "IMAGE_PATH" column to an image column. Thank you, I'll check out the link! – Andreas Reitberger Jul 24 '18 at 04:26
  • @Clemens Thanks for the link, I'll check it out :) Thanks a lot for the help! – Andreas Reitberger Jul 24 '18 at 04:27

2 Answers2

0

There are some Points to fix:

  1. The SourceProperty of an Image expects an ImageSource object, I guess in your case the DataContext-Property 'IMAGE_PATH' is of Type string
  2. Binding RelativePath.Self means looking up for an 'own' Property of the Control, you set the Binding, unless you have a Custom Image-Control with a DP 'IMAGE_PATH' as string internally converting it as a BitmapImage, this wont work.
  3. You didn't provide your ViewModel and the ItemsSource Binding of the Datagrid. You will need a ViewModel having an ObservableCollection< YourType > Property and Bind this to the DataGrid ItemsSource Property
  4. The Class 'YourType' will need a Property holding the BitmapImage to be used in the Binding in the DataGridTemplateColumn.
  5. I tend to build the Images as Resources (Sol.Explorer -> Image -> Build Action...) so the 'Pack'-Uri get's simplier and you don't have to copy the images in your outputfolder.

Here a working version (might not be perfect, but functional)

the Window xaml:

<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApp1"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
<Grid>
    <DataGrid AutoGenerateColumns="False"  
              Name="dg1" ItemsSource="{Binding Items}"
              CanUserAddRows="False" />
</Grid>

The CodeBehind for the Window (this is not too elegant, just for testing)

namespace WpfApp1
{
public partial class MainWindow : Window
 {
  public MainWindow()
    {
        InitializeComponent();
        var x = new VM();
        x.Items.Add(new Class2 { IMAGE_PATH = "dir_16p.png", value = "123" });
        x.Items.Add(new Class2 { IMAGE_PATH = "dir_blue_16p.png", value = "456" });
        DataContext = x;
        dg1.Columns.Add(Class1.createImageColumn("test", new Size(16, 16)));
        dg1.Columns.Add(new DataGridTextColumn { Binding = new Binding("value"), 
        Header = "Value" });
    }
  }
}

Class1.cs

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace WpfApp1
{
class Class1
{
    public static DataGridTemplateColumn createImageColumn(string header, Size s)
    {
        Binding b = new Binding("img");

        DataTemplate dt = new DataTemplate();
        dt.DataType = typeof(Class2);
        var dtFactory = new FrameworkElementFactory(typeof(StackPanel));
        dtFactory.Name = "stack";
        dtFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);


        FrameworkElementFactory imageHolder = new 
        FrameworkElementFactory(typeof(Image));
        imageHolder.SetValue(Image.SourceProperty, b);
        imageHolder.SetValue(Image.WidthProperty, s.Width);
        imageHolder.SetValue(Image.HeightProperty, s.Height);
        imageHolder.SetValue(Image.HorizontalAlignmentProperty, 
        HorizontalAlignment.Center);
        imageHolder.SetValue(Image.VerticalAlignmentProperty, 
        VerticalAlignment.Center);

        dtFactory.AppendChild(imageHolder);

        var  imageColumn = new DataGridTemplateColumn();
        imageColumn.Header = header;
        imageColumn.CellTemplate = dt;
        return imageColumn;
    }

}
}

Class2

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Media.Imaging;

namespace WpfApp1
{
class Class2 : INotifyPropertyChanged
{

    private string _IMAGE_PATH;
    public string IMAGE_PATH
    {
        get { return _IMAGE_PATH; }
        set
        {
            _IMAGE_PATH = value;
            NotifyPropertyChanged();
            img = new BitmapImage(
                new Uri("pack://application:,,,/Images/" + value));
        }
    }

    private BitmapImage _img;
    public BitmapImage img
    {
        get
        {

            return _img;
        }
        set
        {
            _img = value;
            NotifyPropertyChanged();
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }


    private string _value;
    public string value
    {
        get { return _value; }
        set { _value = value; NotifyPropertyChanged(); }
    }
}
}

And the ViewModel VM.cs

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace WpfApp1
{
class VM : INotifyPropertyChanged
{

    public VM()
    {
        Items = new ObservableCollection<Class2>();
    }

    private ObservableCollection<Class2> _Items;
    public ObservableCollection<Class2> Items
    {
        get { return _Items; }
        set { _Items = value; NotifyPropertyChanged(); }
    }


    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
}

Build Action for res-images

Clemens
  • 123,504
  • 12
  • 155
  • 268
dba
  • 1,159
  • 1
  • 14
  • 22
  • Two changes: 1. The Source property expects an ImageSource, not a BitmapImage (which is an ImageSource). 2. You have to use a full Pack URI in code behind, including the prefix. Such an URI is never Relative. – Clemens Jul 22 '18 at 18:05
  • Besides that, you do not need to explicitly create a BitmapImage. When an Image's Source property is bound, the Binding source property may as well be a string, an Uri, or a byte array. Built-in type conversion will automatically create an appropriate ImageSource. – Clemens Jul 22 '18 at 18:10
  • Hi! Thank you very much for your detailed help. But the thought is that the user can add Images to an item. So the image will be copied from the source to **APPLICATION_ROOT/Images/Item/file.png**. I'll update my question to make clear. Thank you! – Andreas Reitberger Jul 23 '18 at 06:04
  • @Clemens Hi, thank you for your response and your suggestions, learned something new :) The URI-Thing: if you set the build action of the resources to "Resource", you do not need the full "pack"-syntax. At least is this my expierience (don't now the mechanism in Background - it just works :) ) – dba Jul 23 '18 at 06:19
  • @AndreasReitberger in case of "dynamic resources" you may use the "siteoforigin"-syntax in the URI. Please google for details on this. Never used this, so may potentially incorrect :) – dba Jul 23 '18 at 06:20
  • @dba "you do not need the full pack-syntax" might be true if you bind to a string or Uri property, but not if you explicitly create a BitmapImage in code. – Clemens Jul 23 '18 at 07:42
  • @Clemens As I sad, not sure, how and why, but it does work.... at least for the scenario above :) I've tested it before posting :) Note: the images themselves are compiled as resources, therefore it doesn't match OP's requirements anymore :) – dba Jul 24 '18 at 12:53
-3

Try this

                             BitmapImage _image = new BitmapImage();
                            _image.BeginInit();
                            _image.CacheOption = BitmapCacheOption.None;
                            _image.UriCachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);
                            _image.CacheOption = BitmapCacheOption.OnLoad;
                            _image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
                            _image.UriSource = new Uri(imageSource, UriKind.RelativeOrAbsolute);
                            _image.EndInit();
                            ImageName.Source = _image;
jai pundir
  • 1
  • 1
  • 6
  • This is a horrible answer. We may assume that you're intending to tell us something about how to bypass image caching. How would this help in the context of the question? – Clemens Jul 24 '18 at 06:52