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

INotifyPropertyChangedUIアイテムを使用している方法で更新するために必要なインターフェースの使用は見られません。現在のところ、UI コントロールは値が更新されたことを知る方法がありません。

于 2013-01-23T20:29:24.930 に答える