I am trying to implement an image slider with native WPF controls. The PropertyChanged
events for the SelectedIndex
and CurrentImage
properties aren't firing when I click Next or Back or Add a new one.
ViewModel:
namespace WpfApplication2.ImageSlider
{
public class ImageList : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<ImageItem> _Images = new ObservableCollection<ImageItem>();
public ObservableCollection<ImageItem> Images
{
get
{ return _Images; }
}
private int _SelectedIndex;
public static readonly PropertyChangedEventArgs SelectedIndexProperty = new PropertyChangedEventArgs("SelectedIndex");
public int SelectedIndex
{
get { return _SelectedIndex; }
set
{
_SelectedIndex = value;
var handler = PropertyChanged; // value is null
if (handler != null)
{
handler(this, SelectedIndexProperty);
handler(this, CurrentImageProperty);
}
}
}
public static readonly PropertyChangedEventArgs CurrentImageProperty = new PropertyChangedEventArgs("CurrentImage"); //Not Firing
private ImageItem _CurrentImage;
public ImageItem CurrentImage //Not Firing
{
get { return _CurrentImage; }
set
{
_CurrentImage = value;
if (Images.Count > 0)
{
CurrentImage =
Images[SelectedIndex];
}
}
}
public void Next()
{
if (SelectedIndex < Images.Count - 1)
SelectedIndex++;
else
SelectedIndex = 0;
}
public void Back()
{
if (SelectedIndex == 0)
SelectedIndex = Images.Count - 1;
else
SelectedIndex--;
}
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => AddNewImage(), _canExecute));
}
}
public ImageList()
{
_canExecute = true;
}
private bool _canExecute;
public void AddNewImage()
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "Image files (*.jpg, *.jpeg, *.jpe, *.gif, *.png, *.bmp, *.tif) | *.jpg; *.jpeg; *.jpe; *.gif; *.png, *.bmp, *.tif";
dlg.ShowDialog();
if (dlg.FileName != "")
{
Images.Add(new ImageItem() { URI = new Uri(dlg.FileName) });
var handler = PropertyChanged;
if (handler != null)
{
handler(this, CurrentImageProperty);
}
}
}
}
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
}
Model:
namespace WpfApplication2.ImageSlider
{
public class ImageItem
{
public Uri URI { get; set; }
private BitmapSource _Source;
public BitmapSource Source
{
get
{
try
{
if (_Source == null) _Source = new BitmapImage(URI);//lazy loading
}
catch (Exception)
{
_Source = null;
}
return _Source;
}
}
public void Save(string filename)
{
var img = BitmapFrame.Create(Source);
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(img);
using (var saveStream = System.IO.File.OpenWrite(filename))
encoder.Save(saveStream);
}
}
}
XAML:
<Window x:Class="WpfApplication2.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:WpfApplication2.ImageSlider"
mc:Ignorable ="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<BitmapImage x:Key="NotFound" UriSource="E:\..\NotFound.png"/>
</Window.Resources>
<Window.DataContext>
<local:ImageList/>
</Window.DataContext>
<DockPanel>
<Button Content="<" Click="Back_Click"/>
<Button DockPanel.Dock="Right" Content=">" Click="Next_Click"/>
<Image Source="{Binding CurrentImage.Source, Mode=OneWay,
TargetNullValue={StaticResource NotFound},
FallbackValue={StaticResource NotFound}}"/>
<Button Content="Add" Command="{Binding ClickCommand}"></Button>
</DockPanel>
</Window>
XAML.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private ImageList _list= new ImageList();
public ImageList list
{
get { return _list; }
set { _list = DataContext as ImageList; } //The count of the images is always 0;
}
private void Next_Click(object sender, RoutedEventArgs e)
{
list.Next();
}
private void Back_Click(object sender, RoutedEventArgs e)
{
list.Back();
}
}
You have made it wrong.... that's why it's not working Property changed DO NOT set value it only signal system to GET value from property.
try this as new ImageListClass
`
Delete all back code at you window and change window code to something like this:
The problem is that you are using a different
ImageList
object for the buttons than you are using for the display of the images. You've declared a new instance ofImageList
in the XAML as theDataContext
object. And your code-behind does have an attempt to assign this object to the_list
field, which backs thelist
property you're using to call theBack()
andNext()
methods.But this attempt to assign the field is bad code, and won't even work because no one ever calls the setter of the property. So the getter always returns the empty
ImageList
object you've assigned in the field initializer.You should instead just provide a getter and cast the
DataContext
value: