1

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

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

4

2 回答 2

4

您忘记通知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该类的事件。

于 2013-01-23T20:29:37.857 回答
2

我没有看到该INotifyPropertyChanged界面的任何使用,这需要以您使用它的方式更新 UI 项。就像现在一样,UI 控件无法知道值已更新。

于 2013-01-23T20:29:24.930 回答