PropertyChanged event of my Image Slider code isn&

2019-07-30 05:30发布

问题:

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="&lt;" Click="Back_Click"/>
        <Button DockPanel.Dock="Right" Content="&gt;" 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();
    }
}

回答1:

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

`

public class ImageListFixed : INotifyPropertyChanged
    {
        #region Fields
    private ObservableCollection<ImageItem> images = new ObservableCollection<ImageItem>();
    private int selectedIndex;
    private ImageItem currentImage;

    #endregion Fields

    #region Properties

    public ObservableCollection<ImageItem> Images
    {
        get { return images; }
        set { images = value; }
    }

    public int SelectedIndex
    {
        get { return selectedIndex; }
        set
        {
            if(value < Images.Count && value > -1)
            {
                selectedIndex = value; OnPropertyChanged();
                CurrentImage = Images[selectedIndex];
            }
        }
    }

    public ImageItem CurrentImage
    {
        get { return currentImage; }
        set { currentImage = value; OnPropertyChanged(); }
    }

    #endregion Properties

    #region Public Methods

    public void Next()
    {
        SelectedIndex ++;
    }

    public void Back()
    {
        SelectedIndex--;
    }

    #endregion Public Methods

    #region Methods

    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) });
            SelectedIndex = Images.Count - 1;
        }
    }

    #endregion Methods

    #region Constructors

    public ImageListFixed()
    {
        _canExecute = true;
    }

    #endregion Constructors

    #region Commands

    private ICommand _clickCommand;
    public ICommand ClickCommand
    {
        get
        {
            return _clickCommand ?? (_clickCommand = new CommandHandler(() => AddNewImage(), _canExecute));
        }
    }

    private bool _canExecute;

    private ICommand nextCommand;

    public ICommand NextCommand
    {
        get
        {
            if (nextCommand == null)
            {
                nextCommand = new CommandHandler(()=> OnNextCommand(), true);
            }
            return nextCommand;
        }
        set { nextCommand = value; }
    }

    private void OnNextCommand()
    {
        Next();
    }
    private ICommand backCommand;

    public ICommand BackCommand
    {
        get
        {
            if(backCommand == null)
            {
                backCommand = new CommandHandler(() => OnBackCommand(), true);
            }
            return backCommand;
        }
        set { backCommand = value; }
    }

    private void OnBackCommand()
    {
        Back();
    }

    #endregion Commands




    #region INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion INotifyPropertyChanged

}`

Delete all back code at you window and change window code to something like this:

<DockPanel>
    <Button Content="&lt;" Command="{Binding BackCommand}"/>
    <Button DockPanel.Dock="Right" Content="&gt;" Command="{Binding NextCommand}"/>
    <Image Source="{Binding CurrentImage.Source, Mode=OneWay}"/>
    <Button Content="Add" Command="{Binding ClickCommand}"></Button>
</DockPanel>


回答2:

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 of ImageList in the XAML as the DataContext object. And your code-behind does have an attempt to assign this object to the _list field, which backs the list property you're using to call the Back() and Next() 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:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public ImageList list 
    {
        get { return (ImageList)DataContext; }
    }

    private void Next_Click(object sender, RoutedEventArgs e)
    {
        list.Next();
    }

    private void Back_Click(object sender, RoutedEventArgs e)
    {
        list.Back();
    }
}


标签: c# wpf xaml mvvm