Reputation: 55
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.
Upvotes: 2
Views: 1286
Reputation: 22073
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.
Upvotes: 2