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

本文关键字:图像 文件夹 WPF 遍历 循环 | 更新日期: 2023-09-27 17:55:23

所以我正在尝试遍历一个文件夹并每 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;
            }
    }

我没有收到任何错误,但图像仍然无法切换。我想知道我的道路是否有效。有什么方法可以测试吗?

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

您忘记通知更新以MainImageSource绑定。

为此,您必须实现接口:INotifyPropertyChanged并定义DataContext

并且,如 MSDN 文档中所述,"将启用设置为 true 与调用"开始"相同,而将"启用"设置为 false 与调用"停止"相同。

喜欢这个:

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

我没有看到任何INotifyPropertyChanged界面的使用,这是按照您使用的方式更新 UI 项所必需的。 就像现在一样,UI 控件无法知道值是否已更新。