从另一个 ViewModel 访问 ViewModel 中的方法
Access method in ViewModel from another ViewModel
以前我有一个 ViewModel,它变得相当大,所以我决定将它分散到两个单独的 ViewModel 中。现在我想出了在 ViewModel 之间访问方法的问题。我需要 运行 例如现在位于 DataLogModel.cs
的 UpdateDataGridView();
并且每次 window 时我都需要从 ViewModel.cs
运行已更改(在 ViewModel.cs
中的 WinEventProc 方法内部)。
正确的做法是什么?我试过:
var DL = new DataLogModel();
DL.UpdateDataGridView();
没有错误,但也没有访问方法(= DataGrid 没有更新)。但是从 DataLogModel.cs
访问相同的方法工作得很好(= DataGrid 按预期更新)
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(),
});
}
}
}
主要ViewModel.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();
此代码的问题在于 DL 是一个 'new' DataLogModel...它与您的 DataGrid 所在的 DataLogModel 实例不同,因此它无法以您想要的方式刷新它。
如果您尝试在 'correct' DataLogModel 上调用该方法,您需要的是对该 DataLogModel 的引用。例如,当您创建 ViewModel 和 DataLogModel 时,您会将 DataLogModel 的特定实例传递给 ViewModel(通常在构造函数本身中)以便能够调用它。
您必须始终注意您在正确的实例上操作:
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
当您将 MainViewModel
的实例分配给视图的 DataContext
时,您必须仅使用此实例(及其聚合实例)来修改视图。
目前您正在使用 DataLogModel
的两个实例,一个被视图引用,一个被断开:
// This instance is disconnected from the view.
// It is not the same instance that is referenced inside MainViewModel
var DL = new DataLogModel();
DL.UpdateDataGridView();
您必须确保使用正确的引用正确初始化了所有属性。如果 ViewModel
应该访问 DataLogModel
那么 ViewModel
需要聚合一个(共享)引用到这种类型的实例:
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();
...
}
}
主要ViewModel.cs
然后使用 MainViewModel
:
使用的相同 (shared) DataLogModel
实例正确初始化 ViewModel
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);
}
}
以前我有一个 ViewModel,它变得相当大,所以我决定将它分散到两个单独的 ViewModel 中。现在我想出了在 ViewModel 之间访问方法的问题。我需要 运行 例如现在位于 DataLogModel.cs
的 UpdateDataGridView();
并且每次 window 时我都需要从 ViewModel.cs
运行已更改(在 ViewModel.cs
中的 WinEventProc 方法内部)。
正确的做法是什么?我试过:
var DL = new DataLogModel();
DL.UpdateDataGridView();
没有错误,但也没有访问方法(= DataGrid 没有更新)。但是从 DataLogModel.cs
访问相同的方法工作得很好(= DataGrid 按预期更新)
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(),
});
}
}
}
主要ViewModel.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();
此代码的问题在于 DL 是一个 'new' DataLogModel...它与您的 DataGrid 所在的 DataLogModel 实例不同,因此它无法以您想要的方式刷新它。
如果您尝试在 'correct' DataLogModel 上调用该方法,您需要的是对该 DataLogModel 的引用。例如,当您创建 ViewModel 和 DataLogModel 时,您会将 DataLogModel 的特定实例传递给 ViewModel(通常在构造函数本身中)以便能够调用它。
您必须始终注意您在正确的实例上操作:
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
当您将 MainViewModel
的实例分配给视图的 DataContext
时,您必须仅使用此实例(及其聚合实例)来修改视图。
目前您正在使用 DataLogModel
的两个实例,一个被视图引用,一个被断开:
// This instance is disconnected from the view.
// It is not the same instance that is referenced inside MainViewModel
var DL = new DataLogModel();
DL.UpdateDataGridView();
您必须确保使用正确的引用正确初始化了所有属性。如果 ViewModel
应该访问 DataLogModel
那么 ViewModel
需要聚合一个(共享)引用到这种类型的实例:
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();
...
}
}
主要ViewModel.cs
然后使用 MainViewModel
:
DataLogModel
实例正确初始化 ViewModel
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);
}
}