0

Inside my application I have grid that is using ObservableCollection as ImageSource
My Model looks like this:

    public class Student:INotifyPropertyChanged
    {
        public Student()
        {
        }

        public Student(int id, string firstName, string lastName, int points, bool active = true)
        {
            _id = id;
            _firstName = firstName;
            _lastName = lastName;
            _points = points;
            _active = active;
        }

        private int _id;
        private string _firstName;
        private string _lastName;
        private int _points;
        private bool _active;

        public int Id
        {
            get { return _id; }
            set
            {
                if (value == _id) return;
                _id = value;
                OnPropertyChanged();
            }
        }

        public string FirstName
        {
            get { return _firstName; }
            set
            {
                if (value == _firstName) return;
                _firstName = value;
                OnPropertyChanged();
            }
        }

        public string LastName
        {
            get { return _lastName; }
            set
            {
                if (value == _lastName) return;
                _lastName = value;
                OnPropertyChanged();
            }
        }

        public string Name
        {
            get { return string.Format("{0} {1}", _firstName, _lastName); }
        }

        public int Points
        {
            get { return _points; }
            set
            {
                if (value == _points) return;
                _points = value;
                OnPropertyChanged();
            }
        }

        public string Avatar
        {
            get
            {
                string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                if (appPath == null) return null;
                string imagesPath = Path.Combine(appPath, "Images","Students");
                if (!Directory.Exists(imagesPath)) return null;
                var imagePath = Path.Combine(imagesPath, string.Format("{0}.png", _id));
                if (File.Exists(imagePath)) return imagePath;
                imagePath = Path.Combine(imagesPath, string.Format("{0}.jpg", _id));
                if (File.Exists(imagePath)) return imagePath;
                imagePath = Path.Combine(imagesPath, string.Format("{0}.jpeg", _id));
                if (File.Exists(imagePath)) return imagePath;
                return Path.Combine(imagesPath, "Default.png");
            }
        }

        public bool Active
        {
            get { return _active; }
            set
            {
                if (value == _active) return;
                _active = value;
                OnPropertyChanged();
            }
        }


        public event PropertyChangedEventHandler PropertyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

I'm also storing selected student from grid in variable.
My XAML looks like this:

<DataGrid
    Grid.Column="0"
    Name="StudentsGrid"
    ItemsSource="{Binding Students}"
    HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
    CanUserAddRows="True"
    AutoGenerateColumns="False"
    RowEditEnding="StudentsGrid_OnRowEditEnding"
    SelectedItem="{Binding SelectedStudent, Converter={StaticResource SelectedStudentConverter}}">
    <DataGrid.Columns>
        <DataGridTextColumn Width="*" Binding="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" ClipboardContentBinding="{x:Null}" Header="Imię"/>
        <DataGridTextColumn Width="*" Binding="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" ClipboardContentBinding="{x:Null}" Header="Nazwisko"/>
        <DataGridCheckBoxColumn Width="60" Binding="{Binding Active, UpdateSourceTrigger=PropertyChanged}" ClipboardContentBinding="{x:Null}" Header="Aktywna"/>
    </DataGrid.Columns>
</DataGrid>
<Border HorizontalAlignment="Left" Height="120" Margin="10,78,0,0"
            VerticalAlignment="Top" Width="97" BorderBrush="Black" BorderThickness="1">
    <Image Name="Avatar" Source="{Binding SelectedStudent.Avatar, Converter={StaticResource AvatarConverter}}" />
    </Border>
<Button Click="ButtonBase_OnClick" Content="Change avatar" Grid.Column="1" HorizontalAlignment="Right" Margin="0,80,10,0" VerticalAlignment="Top" Width="107"/>

I've used converter for selected student because I got red border around grid when trying to add new row.

and code responsible for button click:

private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
    Microsoft.Win32.OpenFileDialog openfile = new Microsoft.Win32.OpenFileDialog
    {
        DefaultExt = "*.png",
        Filter = "JPG (*.jpg,*.jpeg)|*.jpg;*.jpeg|PNG (*.png)|*.png"
    };
    bool? result = openfile.ShowDialog();
    if (result == true)
    {
        var source = openfile.FileName;
        var ext = Path.GetExtension(source);
        string appPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
        if (appPath == null) return;
        string imagesPath = Path.Combine(appPath, "Images","Students");
        if (!Directory.Exists(imagesPath)) Directory.CreateDirectory(imagesPath);
        var destination = Path.Combine(imagesPath, string.Format("{0}{1}", _selectedStudent.Id, ext));

        Student old = _selectedStudent;
        SelectedStudent = null;
        File.Copy(source, destination,true);
        SelectedStudent = old;

        var bindingExpression = Avatar.GetBindingExpression(Image.SourceProperty);
        if (bindingExpression != null) bindingExpression.UpdateTarget();
    }
}

And AvatarConverter:

public class AvatarConverter:IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        try
        {
            BitmapImage image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.OnLoad;
            image.UriSource = new Uri((string)value);
            image.EndInit();

            return image;
            //I've used below code previously, but file was locked
            return new BitmapImage(new Uri((string)value));
        }
        catch
        {
            return new BitmapImage();
        }
    }

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

My idea was to display current avatar that is stored inside Images\Student. If file exists then Avatar property is set to that image path, else I'm displaying Default.png.

Next to currently selected avatar image I've added button that will allow me to update avatar.

I was't able to replace image file, because it was in use, but thanks to this answer I was able to fix this quickly.

But I'm unable to update image inside my application to display new source. I've tried null'ing SelectedStudent and restoring it, but without luck.

  1. How can I refresh image source for current student after I replace that image on disk.
  2. Should I move Avatar path resolving to converter or can I leave it inside model?
Community
  • 1
  • 1
Misiu
  • 4,738
  • 21
  • 94
  • 198

0 Answers0