2016-07-27 23 views
1

我想实现图像滑块本机WPF控件。 SelectedIndexCurrentImage属性的PropertyChanged事件在单击“下一步”或“后退”或添加一个新属性时未触发。我的图像滑块代码PropertyChanged事件没有被提出

视图模型:

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(); 
     } 
    } 
} 

型号:

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(); 
    } 
} 
+0

我想可能是因为您的DataContext没有设置为包含处理程序主窗口的一个实例。 –

+0

你是什么意思,具体由“不发射”?您发布的代码将会调用例如单击第一个按钮时的'Back_Click()'方法,该方法将调用'list.Back()'方法。您发布的代码中没有任何内容会阻止该内容。但是你的问题中没有任何东西表明你可能会问别的东西。无论如何,当然如果你的问题是关于事件的,你不需要发布多个按钮或实际图像来重现“不发射”行为。请提供一个好的[mcve],并解释到底是什么问题。 –

+0

@PeterDuniho我在代码中提到了确切问题的地方。 'PropertyChanged'为null,'CurrentImageProperty'没有被触发。 – iamCR

回答

1

你犯了错误......这就是为什么它不能正常工作 属性改变不设置值它只是信号系统从属性获取值。

尝试这种新的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 

}` 

删除所有后面的代码在你的窗口,改变窗口的代码是这样的:

<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> 
+0

我正在使用C#版本<6.您可以优化这个吗? 'PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(propertyName));' – iamCR

+0

自己完成它。非常感谢。运作良好。 – iamCR

+2

大部分这只是一个偏好而不是函数的问题,用命令绑定来代替事件处理,两者都是工作,两者都比另一个更为正确。这个修正它的唯一原因是它删除了一个ImageList的错误创建,它应该是对数据上下文的引用 – MikeT

2

的问题是,你是使用与您用于显示图像的按钮不同的ImageList对象。您已将XAML中的ImageList的新实例声明为DataContext对象。并且您的代码隐藏方法试图将此对象分配给_list字段,该字段支持您用于调用Back()Next()方法的list属性。

但是,这种尝试分配字段是错误的代码,甚至不会工作,因为没有人曾经调用属性的setter。因此,getter总是返回您在字段初始值设定项中分配的空对象ImageList

而应该只是提供一个getter和铸DataContext值:

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(); 
    } 
} 
+0

准确识别和为什么发生的好解释+1 – MikeT