循环遍历c#/ WPF中的图像文件夹

时间:2013-01-23 20:22:14

标签: c# wpf

所以我试图循环浏览文件夹并每2秒更改一次图像源。

我认为我的代码是正确的,但我似乎遗漏了一些东西,因为我的图片不会更新,但我没有收到错误。

代码填充我的文件数组,以便找到图片,我只是做错了设置图像源。

XAML代码

<Grid>
       <Image x:Name="Picture" Source="{Binding ImageSource}"  Width="980" Height="760" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="350,50,0,0"></Image>
 <Grid>

C#代码

 private string[] files;
    private System.Timers.Timer timer;

    private int counter;
    private int Imagecounter;

    Uri _MainImageSource = null; 

    public Uri MainImageSource {
        get 
        {
            return _MainImageSource; 
        } 
        set 
        {
            _MainImageSource = value;
        } 
    }

    public IntroScreen()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(this.MainWindow_Loaded);
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {

        setupPics();
    }

    private void setupPics() 
    {
        timer = new System.Timers.Timer();
        timer.Elapsed += new ElapsedEventHandler(timer_Tick);
        timer.Interval = (2000);
        timer.Start();

        files = Directory.GetFiles("../../Resources/Taken/", "*.jpg", SearchOption.TopDirectoryOnly);
        Imagecounter = files.Length;
        MessageBox.Show(Imagecounter.ToString());
        counter = 0;
    }

    private void timer_Tick(object sender, EventArgs e)
    {
        counter++;
        _MainImageSource = new Uri(files[counter - 1], UriKind.Relative);
        if (counter == Imagecounter)
            {
                counter = 0;
            }
    }

任何人都知道我做错了什么?

更新了代码

XAML

 <Image x:Name="Picture" Source="{Binding MainImageSource}"  Width="980" Height="760" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="350,50,0,0"></Image>

C#

public partial class IntroScreen : UserControl, INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }

    private string[] files;
    private System.Timers.Timer timer;

    private int counter;
    private int Imagecounter;

    Uri _MainImageSource = null;

    public Uri MainImageSource
    {
        get 
        {
            return _MainImageSource; 
        } 
        set 
        {
            _MainImageSource = value;
            OnPropertyChanged("MainImageSource");
        } 
    }

    public IntroScreen()
    {
        InitializeComponent();
        this.Loaded += new RoutedEventHandler(this.MainWindow_Loaded);
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {

        setupPics();
    }

    private void setupPics() 
    {
        files = Directory.GetFiles("../../Resources/Taken/", "*.jpg", SearchOption.TopDirectoryOnly);
        Imagecounter = files.Length;


        counter = 0;
        timer = new System.Timers.Timer();
        timer.Elapsed += new ElapsedEventHandler(timer_Tick);
        timer.Interval = (2000);
        timer.Enabled = true;
        timer.Start();


    }

    private void timer_Tick(object sender, EventArgs e)
    {
        counter++;
        MainImageSource = new Uri(files[counter - 1], UriKind.Relative);
        if (counter == Imagecounter)
            {
                counter = 0;
            }
    }

我没有收到任何错误,但图片仍然没有切换。我想知道我的路径是否正常工作。有没有办法测试这个?

2 个答案:

答案 0 :(得分:4)

您忘记将MainImageSource的更新通知给绑定。

为此,您必须实现界面:INotifyPropertyChanged并定义DataContext

并且,如MSDN文档中所述“将Enabledd设置为true与调用Start相同,而将Enabled设置为false与调用Stop相同。”。

像这样:

public partial class IntroScreen : Window, INotifyPropertyChanged
{
    private string[] files;
    private Timer timer;

    private int counter;
    private int Imagecounter;

    BitmapImage _MainImageSource = null;
    public BitmapImage MainImageSource  // Using Uri in the binding was no possible because the Source property of an Image is of type ImageSource. (Yes it is possible to write directly the path in the XAML to define the source, but it is a feature of XAML (called a TypeConverter), not WPF)
    {
        get
        {
            return _MainImageSource;
        }
        set
        {
            _MainImageSource = value;
            OnPropertyChanged("MainImageSource");  // Don't forget this line to notify WPF the value has changed.
        }
    }

    public IntroScreen()
    {
        InitializeComponent();
        DataContext = this;  // The DataContext allow WPF to know the initial object the binding is applied on. Here, in the Binding, you have written "Path=MainImageSource", OK, the "MainImageSource" of which object? Of the object defined by the DataContext.

        Loaded += MainWindow_Loaded;
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        setupPics();
    }

    private void setupPics()
    {
        timer = new Timer();
        timer.Elapsed += timer_Tick;
        timer.Interval = 2000;

        // Initialize "files", "Imagecounter", "counter" before starting the timer because the timer is not working in the same thread and it accesses these fields.
        files = Directory.GetFiles(@"../../Resources/Taken/", "*.jpg", SearchOption.TopDirectoryOnly);
        Imagecounter = files.Length;
        MessageBox.Show(Imagecounter.ToString());
        counter = 0;

        timer.Start();  // timer.Start() and timer.Enabled are equivalent, only one is necessary
    }

    private void timer_Tick(object sender, EventArgs e)
    {
        // WPF requires all the function that modify (or even read sometimes) the visual interface to be called in a WPF dedicated thread.
        // IntroScreen() and MainWindow_Loaded(...) are executed by this thread
        // But, as I have said before, the Tick event of the Timer is called in another thread (a thread from the thread pool), then you can't directly modify the MainImageSource in this thread
        // Why? Because a modification of its value calls OnPropertyChanged that raise the event PropertyChanged that will try to update the Binding (that is directly linked with WPF)
        Dispatcher.Invoke(new Action(() =>  // Call a special portion of your code from the WPF thread (called dispatcher)
        {
            // Now that I have changed the type of MainImageSource, we have to load the bitmap ourselves.
            BitmapImage bitmapImage = new BitmapImage();
            bitmapImage.BeginInit();
            bitmapImage.UriSource = new Uri(files[counter], UriKind.Relative);
            bitmapImage.CacheOption = BitmapCacheOption.OnLoad;  // Don't know why. Found here (http://stackoverflow.com/questions/569561/dynamic-loading-of-images-in-wpf)
            bitmapImage.EndInit();
            MainImageSource = bitmapImage;  // Set the property (because if you set the field "_MainImageSource", there will be no call to OnPropertyChanged("MainImageSource"), then, no update of the binding.
        }));
        if (++counter == Imagecounter)
            counter = 0;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

您的XAML没有引用正确的属性:

<Grid>
    <Image x:Name="Picture" Source="{Binding MainImageSource}"  Width="980" Height="760" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="350,50,0,0"></Image>
<Grid>

为什么需要实施INotifyPropertyChanged

基本上,当您定义绑定时,WPF将检查包含相应属性的类是否定义INotifyPropertyChanged。如果是这样,它将订阅该类的事件PropertyChanged

答案 1 :(得分:2)

我没有看到INotifyPropertyChanged接口的使用,这是以您使用它的方式更新UI项目所必需的。就像现在一样,UI控件无法知道值已更新。