1

I have a text log File which i parse every 10 seconds to display it values on the WPF-Application, I am trying to use MVVM for the first time in WPF. The problem i am facing is I am unable to refresh the DataContext with the timer. The format of the text file is

log.txt

UserID|RP1|MS9|1.25

UserID|RP5|MS7|1.03

Code for the Application is given below

Code for Model-Class

 public class UserModel
 {
     public string userID{get; set;}
     public string RP{get; set;}
     public string MS{get; set;}
     public string Rate{get; set;}
 }

Code for ModelView-Class

 public class AppModelView
 {
     private ObservableCollection<UserModel> _userList;
     DispatcherTimer LogTimer;
    
     public AppModelView()
     {
          _userList = new ObservableCollection<UserModel>();
          LogTimer = new DispatcherTimer();
          LogTimer.Interval = TimeSpan.FromMilliseconds(10000);  
          
          LogTimer.Tick += (s, e) =>
          {
             foreach(DataRow row in LogManager.Record) //LogManager is class which parse the txt file and assign value into a DataTable Record
                   _userList.add(new UserModel
                   {
                       userID= row[0].toString();
                       RP = row[1].toString();
                       MS = row[2].toString();
                       rate = row[3].toString();

                    });
           };
           LogTimer.Start();
     }

     public ObservableCollection<UserModel> UserList
     {
          get { return _userList; }
          set { _userList = value;
                 NotifyPropertyChanged("UserList");}
     }

   public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
 }

MainWindows.xaml

 <Window x:Class="MonitoringSystem.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Monitoring Server"  WindowStartupLocation="CenterScreen" Height="768" WindowState="Maximized" >

  <Grid>
     <DockPanel>
            <Label Content="User Monitored" DockPanel.Dock="Top"/>
            <ListView Name="lstRpt" DockPanel.Dock="Bottom" ItemsSource="{Binding UserList}" >
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="UserID"  DisplayMemberBinding="{Binding userID}"/>
                        <GridViewColumn Header="RP" DisplayMemberBinding="{Binding RP}"/>
                        <GridViewColumn Header="MS"  DisplayMemberBinding="{Binding MS}"/>
                        <GridViewColumn Header="Rate"  DisplayMemberBinding="{Binding Rate}"/>
                    </GridView>
                </ListView.View>
            </ListView>
        </DockPanel>
  </Grid>
</Windows>

MainWindows.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        AppViewModel VM = new AppViewModel();
        this.DataContext = VM;
     }
 }

Now if I remove the DispatcherTimer it display the values which were parse for the first time and display it , but with timer it cannot display any values. Your Guidance is highly appreciated.

Community
  • 1
  • 1
WiXXeY
  • 981
  • 5
  • 19
  • 46
  • unable to refresh the DataContext - What does this mean? The code seems alright. The ListView will show changes for every 10 seconds, if THERE IS ANY CHANGE. – Jawahar Dec 08 '14 at 06:32
  • @XAMLLover I cannot get any values on the form, if i remove the dispatchertimer then it parse the values for first time and display on the form – WiXXeY Dec 08 '14 at 06:35
  • @WiXXeY: show the code, which populates new `UserModel` properties. And you don't want to change the data context, you want to update list of users, don't you? Also, why is `UserList` made read-write? Who should assign it, instead of `AppModelView` instance itself? – Dennis Dec 08 '14 at 06:39
  • @Dennis The user properties are populated perfectly I have debug it many times, but I will make edit in my code – WiXXeY Dec 08 '14 at 06:43
  • @Dennis Yap I want to update the list of users. and no one will assign values to it apart from AppmodelView – WiXXeY Dec 08 '14 at 06:45
  • you add the item to the private which dont have notification _userList instead of UserList. – Amin Budianto Dec 08 '14 at 07:09
  • @AminBudianto Didnt got what are you pointing at, kindly elaborate – WiXXeY Dec 08 '14 at 07:14
  • 1
    you will need to add the new UserModel to UserList instead of _userList. because UserList is the one implement NotifyPropertyChanged – Amin Budianto Dec 08 '14 at 07:23
  • 1
    @AminBudianto: what a nonsense. `ItemsSource` updates `Items` collection, when it receives `CollectionChanged` event from underlying collection. There's no need to change *collection* (don't confuse with *content* of collection) to update items, if the collection implements `INotifyCollectionChanged`, as `ObservableCollection` does. Moreover, `UserList` should be readonly. – Dennis Dec 08 '14 at 07:38
  • @WiXXeY: your code looks OK. Does `LogManager.Record` contains anything, when the timer fires? – Dennis Dec 08 '14 at 07:39
  • thanx i have figured it out by following http://stackoverflow.com/questions/8470101/liststring-inotifypropertychanged-event – WiXXeY Dec 08 '14 at 07:41

2 Answers2

1

Correction needed only in ViewModel

public class AppModelView
    {
        private ObservableCollection<UserModel> _userList;
        DispatcherTimer LogTimer;

        public AppModelView()
        {
            _userList = new ObservableCollection<UserModel>();
            LogTimer = new DispatcherTimer();
            LogTimer.Interval = TimeSpan.FromMilliseconds(10000);

            LogTimer.Tick += (s, e) =>
            {
                Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal,
                 new Action(
                     delegate()
                     {
                         UserList.Add(new UserModel
                         {
                             userID = "test"
                         });
                     }
                 )
                );
            };
            LogTimer.Start();
        }

        public ObservableCollection<UserModel> UserList
        {
            get { return _userList; }
            set
            {
                _userList = value;
                NotifyPropertyChanged("UserList");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void NotifyPropertyChanged(string propName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
Mahesh Malpani
  • 1,782
  • 16
  • 27
  • Why to run new delegate using dispatcher in `DispatcherTimer`'s callback, which, in turn, also uses dispatcher to fire? – Dennis Dec 08 '14 at 06:53
  • Hi haven't check. It will work without dispatcher also. Issue is with _userList. Use UserList collection instead – Mahesh Malpani Dec 08 '14 at 07:48
1

What I suspect is happening is that your UserModel is getting added to the collection before its properties have been set, and because your UserModel has no INPC the view never updates once they are set.

Try changing your code to:

LogTimer.Tick += (s, e) =>
{
    foreach(DataRow row in LogManager.Record) //LogManager is class which parse the txt file and assign value into a DataTable Record
    {
        var userModel = new UserModel
        {
            userID= row[0].toString();
            RP = row[1].toString();
            MS = row[2].toString();
            rate = row[3].toString();
        };

        _userList.Add(userModel);
    };

    LogTimer.Start();
 };
GazTheDestroyer
  • 20,722
  • 9
  • 70
  • 103