Question

i have problem in my code that i don't even near to understand.

Here is my item interface;

internal interface IItem
{
    void Show();
    event EventHandler Completed;
    TimeSpan Duration { get; set; }
    string Name { get; set; }
}

internal class ItemImage : IItem
{

    public TimeSpan Duration { get; set; }
    public string Name { get; set; }
    public event EventHandler Completed;

    private DispatcherTimer _dt = new DispatcherTimer();

    public void Show()
    {

        _dt.Interval = this.Duration;
        _dt.Tick += (s, e) =>
        {
            _dt.Stop();
            Completed(this, new EventArgs());
        };
        _dt.Start();

    }
}

And here's my player:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    int _pIndex = 0;
    List<IItem> list = new List<IItem>();

    private void Button1_Click(object sender, RoutedEventArgs e)
    {
        list = new List<IItem>()
        {
            new ItemImage() { Duration = TimeSpan.FromSeconds(5), Name = "Image1" },
            new ItemImage() { Duration = TimeSpan.FromSeconds(3), Name = "Image2" },
            new ItemImage() { Duration = TimeSpan.FromSeconds(5), Name = "Image3" },
            new ItemImage() { Duration = TimeSpan.FromSeconds(7), Name = "Image4" }
        };
        Next();
    }

    void Next()
    {
        var tb = new TextBlock();
        tb.Text = ((IItem)list[_pIndex]).Name;
        StackPanel1.Children.Add(tb);

        list[_pIndex].Completed += (s, e) =>
        {
            Next();
        };
        list[_pIndex].Show();
        _pIndex++;
        _pIndex %= list.Count;

    }
}

First list plays with no problem but on second turn DispatcherTimer doesn't wait for my duration value, and immediately fires complete event. What do i do wrong? Thanks.

Was it helpful?

Solution

I don't know exactly what is happening (I didn't test it), but I see that every time you call Show(), another eventhandler is attached to the Tick in your ItemImage object. This could lead to some side effects you'll experiencing.

You might change it to:

internal class ItemImage : IItem 
{   

    public TimeSpan Duration { get; set; }
    public string Name { get; set; }
    public event EventHandler Completed;

    private DispatcherTimer _dt = new DispatcherTimer();

    // constructor
    public ItemImage()
    {
        _dt.Tick += (s, e) =>
        {
            _dt.Stop();
            Completed(this, new EventArgs());
        };
    }

    public void Show()
    {
        _dt.Interval = this.Duration;
        _dt.Start();

    }
}

You could recreate the DispatcherTimer or move the event attaching to the constructor. (like above)

This is also done in the Next() method with list[_pIndex].Completed. (it attaches to a class member, so every buttonclick new handlers are added to the list.)

You might reconcider the style of attaching events. Like moving them to constructors.

Like:

public partial class MainWindow : Window
{
    int _pIndex = 0;
    List<IItem> list = new List<IItem>();


    public MainWindow()
    {
        InitializeComponent();

        list[_pIndex].Completed += (s, e) =>
        {
            _pIndex++;
            _pIndex %= list.Count;

            Next();
        };

    }

    private void Button1_Click(object sender, RoutedEventArgs e)
    {
        list = new List<IItem>()
        {
            new ItemImage() { Duration = TimeSpan.FromSeconds(5), Name = "Image1" },
            new ItemImage() { Duration = TimeSpan.FromSeconds(3), Name = "Image2" },
            new ItemImage() { Duration = TimeSpan.FromSeconds(5), Name = "Image3" },
            new ItemImage() { Duration = TimeSpan.FromSeconds(7), Name = "Image4" }
        };
        Next();
    }

    void Next()
    {
        var tb = new TextBlock();
        tb.Text = ((IItem)list[_pIndex]).Name;
        StackPanel1.Children.Add(tb);
        list[_pIndex].Show();
    }
}

Good luck.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top