0

I have a problem with binding data in WPF. Task runed in constructor work perfect. But when i tried to run task in timer handler it didnt work -> the data in view didnt update...

public ObservableCollection<ushort> Test2{get; set;}

 public Settings()
    {
        InitializeComponent();
        Test2 = new ObservableCollection<ushort>();
        Test2.Add(666);
        Test2.Add(111);
        Task.Run(async () =>
        {
            int i = 0;
            while (true)
            {
                await Task.Delay(500);
                Test2[1] += (ushort)2;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(Test2)));
            }
        });

    }

  private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
    {
        Task.Run(async () =>
        {
            int i = 0;
            while (true)
            {
                await Task.Delay(500);
                Test2[1] += (ushort)2;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(Test2)));
            }
        });
    }

Timer

oTimer.Interval = 1000;               
oTimer.Elapsed += OnTimedEvent;
oTimer.AutoReset = true;
oTimer.Enabled = true;
oTimer.Start();             

Other class file

public UserControlHome()
    { 
        InitializeComponent();
       DataContext = new Settings();
    }

And the XML file

<TextBlock x:Name="Tob2Sensor1" Grid.Column="1" Text="{Binding Test2[1]}" HorizontalAlignment="Center"/>
r00ti
  • 13
  • 4

5 Answers5

0

Try removing the task from the timer event:

private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
{
    Test2[1] += (ushort)2;
    PropertyChanged(this, new PropertyChangedEventArgs(nameof(Test)));
}
Nick
  • 4,787
  • 2
  • 18
  • 24
0

Try wrap it up inside a Dispatcher

Application.Current.Dispatcher.Invoke(() => PropertyChanged(this, new PropertyChangedEventArgs(nameof(Test)));));

EDIT: This works for me (I'm using latest Prism.Core from Nuget):

using Prism.Mvvm;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;

namespace ArrayBinding
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow()
    {
      InitializeComponent();
      DataContext = new Settings();
    }
  }

  class Settings: BindableBase
  {
    public ObservableCollection<ushort> Test2 { get; set; }

    private Timer oTimer = new Timer();
    public Settings()
    {
      Test2 = new ObservableCollection<ushort>();
      Test2.Add(666);
      Test2.Add(111);

      oTimer.Interval = 1000;
      oTimer.Elapsed += OnTimedEvent;
      oTimer.AutoReset = true;
      oTimer.Enabled = true;
      oTimer.Start();

    }

    private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
    {
      Task.Run(async () =>
      {
        while (true)
        {
          await Task.Delay(500);
          Test2[1] += (ushort)2;
        }
      });
    }
  }
}
Chinh Nguyen
  • 583
  • 1
  • 7
  • 14
0

In the constructor version you don't need to call PropertyChanged - which is why it works

In the timer callback version, your PropertyChanged line is incorrect, try this:

PropertyChanged(this, new PropertyChangedEventArgs("Test2[1]"));
Dean Chalk
  • 20,076
  • 6
  • 59
  • 90
0

Might be a cross-threading problem. WPF does not allow changing bound collection from outside the UI thread. Try the following:

<!-- language: c# -->
private void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
{
    Task.Run(async () =>
    {
        int i = 0;
        while (true)
        {
            await Task.Delay(500);
            Dispatcher.Invoke(()=>
            {
                Test2[1] += (ushort)2;
                PropertyChanged(this, new PropertyChangedEventArgs(nameof(Test2)));
            });
        }
    }
}

IMO the 'async' part is obsolete. You can do as Nick has written, but inside the dispatcher invoke.

Klaus Gütter
  • 11,151
  • 6
  • 31
  • 36
0

Ok, i made few changes.

    public Settings()
    {
        InitializeComponent();

        Test2 = new ObservableCollection<ushort>();
        Test2.Add(100);
        Test2.Add(99);
        Test2.Add(98);


        dispatcherTimer.Tick += dispatcherTimer_Tick;
        dispatcherTimer.Interval = new TimeSpan(0, 0, 1);

    }
    private void dispatcherTimer_Tick(object sender, EventArgs e)
    {
            Task.Run(async () =>
            {
                int i = 0;
                while (true)
                {
                    await Task.Delay(500);
                    Dispatcher.Invoke(() =>
                    {
                        Test2[1] += (ushort)2;
                        PropertyChanged(this, new PropertyChangedEventArgs(nameof(Test2)));
                    });
                }
            });

    }
public void Button_Click(object sender, RoutedEventArgs e)
    {          
      dispatcherTimer.Start();
    }

And it doesnt work. But when I run timer "dispatcherTimer.Start();" in constructor everythink is ok....

r00ti
  • 13
  • 4