循环浏览c#/WPF中的图像文件夹

循环浏览c#/WPF中的图像文件夹,c#,wpf,C#,Wpf,所以我试着循环遍历一个文件夹,每2秒更改一次图像源 我认为我的代码是正确的,但我似乎遗漏了一些东西,因为我的图像不会更新,但我没有得到一个错误 这段代码填充了我的文件数组,这样它就可以找到图片,我只是做了一些错误的事情来设置图像源 XAML代码 <Grid> <Image x:Name="Picture" Source="{Binding ImageSource}" Width="980" Height="760" HorizontalAlignment="Le

所以我试着循环遍历一个文件夹,每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>
有人知道我做错了什么吗

更新代码

XAML


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

我看不到INotifyPropertyChanged界面的任何用途,它是按照您使用的方式更新UI项所必需的。现在,UI控件无法知道值是否已更新。

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

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

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

而且,如MSDN文档中所述,“将Enabled设置为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

您忘记通知绑定到
MainImageSource
的更新

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

而且,如MSDN文档中所述,“将Enabled设置为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

首先,在调用timer.Start()之前初始化文件、计数器和Imagecounter。您在哪里设置ImageSource?啊,是的,在这段代码中没有关系(因为计时器代码只在2秒后运行),但不是很好的做法。谢谢我正在定时器代码中设置:_MainImageSource=newURI(文件[counter-1],UriKind.Relative)@Spedax:
\u MainImageSource
不是
ImageSource
。您的XAML绑定了一个名为
ImageSource
的属性,该属性在您的代码中似乎不存在。首先,在调用timer.Start()之前初始化文件、计数器和Imagecounter。您在哪里设置ImageSource?啊,是的,这在这段代码中无关紧要(因为计时器代码只在2秒后运行),但不是很好的做法。谢谢我正在定时器代码中设置:_MainImageSource=newURI(文件[counter-1],UriKind.Relative)@Spedax:
\u MainImageSource
不是
ImageSource
。XAML绑定一个名为
ImageSource
的属性,该属性在代码中似乎不存在。设置该属性(而不是字段)也非常重要:
MainImageSource=new Uri(…)
,而不是
\u MainImageSource=new Uri(…)
。并且不要调用
timer.Enabled=true
timer.Start()
同时。两者都是一样的,一个就足够了。@MattBurland我认为从INotifyPropertyChanged开始比较容易。但是,是的,这是另一个(更多WPF)解决方案。您肯定忘记了编写我在代码末尾添加的OnPropertyChanged方法。我正在修复错误。但是有多个小错误需要修复。设置属性也非常重要,而不是字段:
mainmagesource=new Uri(…)
,而不是
\u mainmagesource=new Uri(…)
。并且不要同时调用
timer.Enabled=true
timer.Start()
。两者都是一样的,一个就足够了。@MattBurland我认为从INotifyPropertyChanged开始比较容易。但是,是的,这是另一个(更多WPF)解决方案。您肯定忘记了编写我在代码末尾添加的OnPropertyChanged方法。我正在修复错误。但是有许多小错误需要修复。
<Grid>
    <Image x:Name="Picture" Source="{Binding MainImageSource}"  Width="980" Height="760" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="350,50,0,0"></Image>
<Grid>