2

I have a C# ListView with some entrys, a Methode to remove the first entry and a Timer to call this method. My problem is, the timer works well (I checked this with calling a MessageBox) and the remove method wors also well (I checket this by calling this method with a Button and not by the timer). But the timer is still not abel to remove items from my ListView.

My Code:

    void Button1Click(object sender, EventArgs e)
    {
        removeItems();
    }

    private void timer_Tick(object sender, System.Timers.ElapsedEventArgs e)
    {
        removeItems();      
    }

    void removeItems()
    {
        MessageBox.Show("Hello from the removeMethod");
        listViewTeam.Items.RemoveAt(0);
    }

Both calls of removeItems(); let the messageBox appear but only the Button let also delete the first Item of the listView.

Can someone help ne how I can remove the first Item by a timer?

Pascal
  • 23
  • 3
  • Which timer are you using? System.Timer or System.Windows.Forms.Timer? Have you called the Start() method on the timer? EDIT: Just saw you use the System timer. Use the Winforms timer as the others said and call Start() – Stelios Adamantidis Jun 13 '13 at 11:03

7 Answers7

2

The timer you're using is not thread safe. Instead of using System.Timer you should use System.Windows.Forms.Timer because it automatically runs on the UI thread. Then your code will work perfectly.

NibblyPig
  • 51,118
  • 72
  • 200
  • 356
  • "*The timer you're using is not thread safe*" - wrong, the one you are suggesting isn't. – James Jun 13 '13 at 11:12
  • 1
    http://msdn.microsoft.com/en-us/magazine/cc164015.aspx *System.Windows.Forms.Timer The timer events raised by this timer class are synchronous with respect to the rest of the code in your Windows Forms app. any code that resides inside a timer event handler (for this type of timer class) is executed using the application's UI thread.* It is completely thread safe with respect to a windows forms application. – NibblyPig Jun 13 '13 at 11:24
  • It's thread safe in a windows forms application when using a single UI thread, yes. `Forms.Timer` is designed *specifically* for a single-threaded environment therefore it's **not** thread safe. You couldn't, for example, use it in a multi-threaded environment. The [documentation](http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx) itself even states "*The Windows Forms Timer component is single-threaded, and is limited to an accuracy of 55 milliseconds. If you require a multithreaded timer with greater accuracy, use the Timer class in the System.Timers namespace.*". – James Jun 13 '13 at 11:38
  • visit this link http://stackoverflow.com/questions/4532850/windows-forms-timer-or-system-threading-timer – Banketeshvar Narayan Sep 11 '15 at 12:51
1

You appear to be using System.Timer which means your Elapsed callback may not necessarily be invoked on the UI thread. You can ensure it does by using Invoke i.e.

if (listViewTeam.InvokeRequired)
{
    listViewTeam.Invoke((MethodDelegate)delegate { listViewTeam.Items.RemoveAt(0); });
}

Or even easier set the SynchronizingObject property of your timer to be the form which contains your ListView and your code will just work.

James
  • 80,725
  • 18
  • 167
  • 237
  • 1
    It's far simpler to use the Forms timer as it runs on the UI thread, but this is another solution to call cross-thread. – NibblyPig Jun 13 '13 at 11:25
  • It's simpler, but less accurate - it all really depends on the purpose of the timer and what context it's being used in. – James Jun 13 '13 at 11:42
0

You need to use a delegate to safely interact with the UI control from a background thread (which is what you are really doing when you implement a Timer):

void Button1Click(object sender, EventArgs e)
{
    removeItems();
}

private void timer_Tick(object sender, System.Timers.ElapsedEventArgs e)
{
    removeItems();      
}

void removeItems()
{
    MessageBox.Show("Hello from the removeMethod");
    RemoveListViewItem(0);
}

public delegate void InvokeRemoveListViewItem(Int32 ItemIndex);
public void RemoveListViewItem(Int32 ItemIndex)
{
    if (InvokeRequired)
    {
        try { Invoke(new InvokeRemoveListViewItem(RemoveListViewItem), new Object[] { ItemIndex }); }
        catch (Exception)
        {
            //react to the exception you've caught
        }
    }

    listView.RemoveAt(ItemIndex);
}
DonBoitnott
  • 10,787
  • 6
  • 49
  • 68
0

Just like SLC said you need to use BeginInvoke. Personally I have solved it like this

  public AddListItem myDelegate;
8bitcat
  • 2,206
  • 5
  • 30
  • 59
0
                               Windows.Forms       System.Timers         System.Threading 
Timer event runs on what thread?    UI thread      UI or worker thread   Worker thread 
Instances are thread safe?          No             Yes                   No 
Familiar/intuitive object model?    Yes            Yes                   No 
Requires Windows Forms?             Yes            No                    No 
Metronome-quality beat?             No             Yes*                  Yes* 
Timer event supports state object?  No             No                    Yes 
Initial timer event schedulable?    No             No                    Yes 
Class supports inheritance?         Yes            Yes                   No

* Depending on the availability of system resources (for example, worker threads) 
Banketeshvar Narayan
  • 3,799
  • 4
  • 38
  • 46
-1

Start the timer first. BUT use the Windows Forms Timer.

Your designer class:

partial class Form1
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.components = new System.ComponentModel.Container();
        this.listBox1 = new System.Windows.Forms.ListBox();
        this.timer1 = new System.Windows.Forms.Timer(this.components);
        this.SuspendLayout();
        // 
        // listBox1
        // 
        this.listBox1.FormattingEnabled = true;
        this.listBox1.Items.AddRange(new object[] {
        "q",
        "w",
        "e",
        "r",
        "t",
        "y",
        "u",
        "sfdgsdf",
        "gf",
        "gsd",
        "fgs",
        "dfg"});
        this.listBox1.Location = new System.Drawing.Point(170, 200);
        this.listBox1.Name = "listBox1";
        this.listBox1.Size = new System.Drawing.Size(120, 95);
        this.listBox1.TabIndex = 0;
        // 
        // timer1
        // 
        this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(570, 502);
        this.Controls.Add(this.listBox1);
        this.Name = "Form1";
        this.Text = "Form1";
        this.Load += new System.EventHandler(this.Form1_Load);
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.ListBox listBox1;
    private System.Windows.Forms.Timer timer1;
}

Your form class:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        removeItems();
        if (listBox1.Items.Count == 0)
        {
            timer1.Stop();
        }
    }

    void removeItems()
    {
        MessageBox.Show("Hello from the removeMethod");
        listBox1.Items.RemoveAt(0);
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        timer1.Start();
    }
}
aiapatag
  • 3,355
  • 20
  • 24