Как получить доступ / изменить элементы управления в другом потоке, используя System.Timers.Timer? - PullRequest
3 голосов
/ 03 марта 2012

Так что я новичок в таймерах и потоках, и я не могу узнать, как создать таймер, который вызывает функцию, которая редактирует / изменяет элемент управления в моем MainWindow.

Код Xaml:

<Window x:Class="TimerTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox Height="287" HorizontalAlignment="Left" Margin="12,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="479" />
</Grid>
</Window>

C # код:

namespace TimerTest
{
    public partial class MainWindow : Window
    {
        //Declaring my aTimer as global
        public static System.Timers.Timer aTimer;
        //Function that is called
        public void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            textBox1.Text += "SomeText ";
        }
        public MainWindow()
        {
            InitializeComponent();
            aTimer = new Timer();
            aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            aTimer.Interval = 1000; // every 5 seconds
            aTimer.Enabled = true;
        }
    }
}

Ошибка, которую я получаю: «Вызывающий поток не может получить доступ к этому объекту, потому что другой поток владеет им». П.С .: Кроме того, я не очень хорош с делегатами, так что если вы думаете, что кто-то может помочь мне в этом случае, пожалуйста, опубликуйте пример кода.

Ответы [ 3 ]

13 голосов
/ 03 марта 2012

Просто добавьте:

aTimer.SynchronizingObject = this;

После создания таймера или используйте другой тип таймера из ToolBox-> Components, который является System.Windows.Forms.Timer.

Таймер, который вы используете в данный момент, запускает поток пула потоков, а не пользовательский интерфейс, поэтому его необходимо синхронизировать с потоком пользовательского интерфейса, чтобы он работал так, как вам нужно.

4 голосов
/ 03 марта 2012

Только поток, связанный с определенным элементом управления, может изменять этот элемент управления.

Чтобы изменить элемент управления из другого потока, вам нужно заключить код обновления в delegate и передать его textBox1.Dispatcher.BeginInvoke:

namespace TimerTest
{
    public partial class MainWindow : Window
    {
        private delegate void updateDelegate(string text);

        private void updateTextBox(string text)
        {
            textBox1.Text += text;
        }

        //Declaring my aTimer as global
        public static System.Timers.Timer aTimer;
        //Function that is called
        public void OnTimedEvent(object source, ElapsedEventArgs e)
        {
             textBox1.Dispatcher.BeginInvoke(
                   new updateDelegate(updateTextBox), "SomeText ");
        }
        public MainWindow()
        {
            InitializeComponent();
            aTimer = new Timer();
            aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
            aTimer.Interval = 1000; // every 5 seconds
            aTimer.Enabled = true;
        }
    }
}
1 голос
/ 03 марта 2012

Проблема в том, что вы пытаетесь обновить поток пользовательского интерфейса из другого потока.В WinForms вам придется перенаправлять вызов обратно в поток пользовательского интерфейса.

Следуя примеру из WPF .NET Лучший способ вызывать событие каждую минуту , в WPF вы можете использоватьТаймер Диспетчера:

    public static System.Windows.Threading.DispatcherTimer aTimer;


    public WindowUpdatedByTimer()
    {
        InitializeComponent();

        aTimer = new System.Windows.Threading.DispatcherTimer();
        aTimer.Tick += new EventHandler(OnTimedEvent);
        aTimer.Interval = TimeSpan.FromSeconds(5);
        aTimer.Start();

    }

    public void OnTimedEvent(object source, EventArgs e)
    {
        textBox1.Text += "SomeText ";
    }
...