简体   繁体   中英

Access method in ViewModel from another ViewModel

Previously I had one ViewModel and it was becoming quite big so I have decided to spread it into two separate ViewModels. Now I came up with problem of accessing methods between ViewModels. I need to run for example UpdateDataGridView(); that is now located in DataLogModel.cs and I need to run it from ViewModel.cs every time window has been changed (inside WinEventProc method in ViewModel.cs ).

What is the correct way of doing it? I have tried with:

var DL = new DataLogModel();
DL.UpdateDataGridView();

No errors, but also method is not accessed (= DataGrid is not updated). However accessing the same method from DataLogModel.cs works just fine (= DataGrid is updated as expected)

ViewModel.cs:

using Tracker.Models;
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;

namespace Tracker
{
    public class ChModel
    {
        private string DBconnectionString = ConfigurationManager.AppSettings["DBConnectionString"];

        /// <summary>
        /// We are interested to display only Today data in Chart
        /// /// <summary>
        public DataTable GetDataForChart()
        {
            DataTable ndt = new DataTable();
            SqlConnection sqlcon = new SqlConnection(DBconnectionString);
            ...
            return ndt;
        }
    }

    class ViewModel : BaseViewModel
    {
        private GetActiveWindowTitle.WinEventDelegate dele = null;

        long milliSeconds;
        TimeSpan timeSpan;
        DateTime CurrentDate;

        public static string WindowTitle;
        public static Stopwatch stopwatch = new Stopwatch();
        public static Stopwatch ManualStopwatch = new Stopwatch();

        public ViewModel()
        {
            // Let's start tracking windows
            StartWindowTracking();

        }

        /// <summary>
        /// Track windows
        /// <summary>
        private void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,
            int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
        {
            WindowTitle = GetActiveWindowTitle.GetActiveWindowTitleMethod();

            if (!string.IsNullOrEmpty(WindowTitle))
            {
                stopwatch.Stop();

                milliSeconds = stopwatch.ElapsedMilliseconds;
                timeSpan = stopwatch.Elapsed;
                CurrentDate = DateTime.Now;

                MainProcess.AddRecordToDatatable(WindowTitle,
                            (int)(milliSeconds / 1000), DateTime.Now.Date, MainProcess.AdminHoursCode, MainProcess.userName);

                UpdateDataGridView();

                stopwatch.Start();
            }

        }

        public void StartWindowTracking()
        {
            WindowTitle = GetActiveWindowTitle.GetActiveWindowTitleMethod();

            dele = new GetActiveWindowTitle.WinEventDelegate(WinEventProc);
            IntPtr m_hhook = GetActiveWindowTitle.SetWinEventHook(GetActiveWindowTitle.EVENT_OBJECT_FOCUS,
                GetActiveWindowTitle.EVENT_OBJECT_FOCUS, IntPtr.Zero, dele, 0, 0, GetActiveWindowTitle.WINEVENT_OUTOFCONTEXT);
        }

        public string DBconnectionString { get; internal set; }
    }
}

DataLogModel.cs:

using Tracker.Models;
using System;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;

namespace Tracker
{
    public class Model
    {
        private string DBconnectionString1 = ConfigurationManager.AppSettings["DBConnectionString"];

        /// <summary>
        /// Get data according to StartDate value
        /// <summary>
        public DataTable GetData(DateTime MyDate)
        {
            DataTable ndt = new DataTable();
            SqlConnection sqlcon = new SqlConnection(DBconnectionString1);
            ...
            return ndt;
        }
    }
    class DataLogModel : BaseViewModel
    {

        public DataLogModel()
        {
            // Update DataGrid
            UpdateDataGridView();
        }

        Model _myModel = new Model();
        private ObservableCollection<ActivityLogModel> _activityLogData = new ObservableCollection<ActivityLogModel>();
        public ObservableCollection<ActivityLogModel> ActivityLogData
        {
            get { return _activityLogData; }
            set
            {
                _activityLogData = value;
                OnPropertyChanged();
            }
        }

        public void UpdateDataGridView()
        {
            DataTable table = _myModel.GetData(StartDate);

            ActivityLogData.Clear();

            for (int i = 0; i < table.Rows.Count; ++i)
                ActivityLogData.Add(new ActivityLogModel
                {
                    WindowTitle = table.Rows[i][0].ToString(),
                    TimeSpent = (int)table.Rows[i][1],
                    DateToday = Convert.ToDateTime(table.Rows[i][2]),
                    Project = table.Rows[i][3].ToString(),
                    UserName = table.Rows[i][4].ToString(),
                });
        }
    }
}

MainViewModel.cs:

using Tracker.Models;
using System.Windows;
using System.Windows.Input;

namespace Tracker
{
    class MainViewModel
    {
        public WindowViewModel WindowViewModel { get; set; }
        public ViewModel ViewModel { get; set; }
        public SettingsViewModel SettingsViewModel { get; set; }
        public DataLogModel DataLogModel { get; set; }
    }
}
var DL = new DataLogModel();
DL.UpdateDataGridView();

The problem with this code is that DL is a 'new' DataLogModel... it is not the same instance of DataLogModel that your DataGrid exists on so it cannot refresh it in the way that you want.

What you need if you are trying to call that method on the 'correct' DataLogModel is a reference to that DataLogModel. When you create your ViewModel and DataLogModel, for example, you would pass that specific instance of the DataLogModel to the ViewModel (usually in the constructor itself) to be able to call it.

You must always take care that you are operating on the proper instances:

var instanceA = new MyClass() { Value = 5 };
var instanceB = new MyClass() { Value = 5 };

// instanceA and instanceB are two different instances 
// i.e. referencing two different memory addresses
ReferenceEquals(instanceA, instanceB); // false

// Does not modify the Value of instanceB
instanceA.Value = 10; 

// instanceA.Value is 10 while instanceB.Value is still 5
instanceA.Value == instanceB.Value // false

When you assign an instance of MainViewModel to the DataContext of your view, you must use this and only this instance (and its aggregated instances) to modify the view.

Currently you are using two instances of DataLogModel , one that is referenced by the view and one that is disconnected:

// This instance is disconnected from the view. 
// It is not the same instance that is referenced inside MainViewModel
var DL = new DataLogModel();
DL.UpdateDataGridView();

You must make sure that all properties properly initialized with the proper references. If ViewModel is supposed to access DataLogModel then ViewModel needs to aggregate a (shared) reference to an instance of this type:

ViewModel.cs

class ViewModel : BaseViewModel
{
  private DataLogModel DataLogModel { get; }

  public ViewModel(DataLogModel dataLogModel)
  {
    // Aggregate an instance of DataLogModel.
    // This enables the instantiating class to inject a *shared* instance.
    this.DataLogModel = dataLogModel;
  }

  private void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,
            int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
  {
    ...
    
    this.DataLogModel.UpdateDataGridView();

    ...
  }
}

MainViewModel.cs
Then initialize the ViewModel properly by using the same ( shared ) DataLogModel instance, that is used by the MainViewModel :

class MainViewModel
{
  private ViewModel ViewModel { get; }
  private DataLogModel DataLogModel { get; }

  public MainViewModel()
  {
    var sharedDataLogModelInstance = new DataLogModel();
    this.DataLogModel = sharedDataLogModelInstance;

    // Allow ViewModel to reference the same instance of DataLogModel
    this.ViewModel = new ViewModel(sharedDataLogModelInstance);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM