I'm currently working on a solution that has a set of composite ViewModels that are mapped from domain models coming back from a set of data access services.
So far I've had a good amount of success with implementing INotifyPropertyChanged on the base ViewModel object and notifying the UI of changes to the property objects via property changed events.
Here's an example of a view model:
public class DisplayDataModel : INotifyPropertyChanged{
private DateTime _lastRefreshTime;
public DateTime LastRefreshTime {
get { return _lastRefreshTime; }
set {
_lastRefreshTime = value;
this.NotifyPropertyChanged(lddm => lddm.LastRefreshTime, PropertyChanged);
}
}
private string _lineStatus;
public string LineStatus {
get { return _lineStatus; }
set {
if (_lineStatus != value) {
_lineStatus = value;
this.NotifyPropertyChanged(lddm => lddm.LineStatus, PropertyChanged);
}
}
}
private ProductionBrickModel _productionBrick;
public ProductionBrickModel ProductionBrick {
get { return _productionBrick;}
set {
if (_productionBrick != value) {
_productionBrick = value;
this.NotifyPropertyChanged(lddm => lddm.ProductionBrick, PropertyChanged);
}
}
}
}
public class ProductionBrickModel{
public int? Set { get; set; }
public int? Theoretical { get; set; }
public int? Actual { get; set; }
public string LineName { get; set; }
public TimeSpan? ShiftOverage { get; set; }
public SolidColorBrush ShiftOverageBrush {
get {
if (ShiftOverage.HasValue && ShiftOverage.Value.Milliseconds < 0) {
return Application.Current.FindResource("IndicatorRedBrush") as SolidColorBrush;
}
return Application.Current.FindResource("IndicatorWhiteBrush") as SolidColorBrush;
}
}
public string ShiftOverageString { get { return ShiftOverage.HasValue ? ShiftOverage.Value.ToShortTimeSpanString() : ""; } }
}
So currently I'm firing notification events on the base model and not the production brick property, mostly because the production brick properties will be changing almost every refresh anyways.
Recently I've started cranking refresh times down to around 350ms and I'm seeing situations where the ShiftOverageBrush is changing to white for a split second even though the values are still negative.
My question is by going through and implementing INotifyPropertyChanged on the object types that make up the base view model will I gain any performance, or even possibly solve this issue? Or is this coming from something else entirely that I'm not understanding?
There are two obvious sources of inefficieny in your code:
1) ShiftOverageBrush is using FindResource every time it's called. Why not cache the brushes?
private SolidColorBrush _redBrush;
private SolidColorBrush IndicatorRedBrush
{
get{ return _redBrush ?? (_redBrush =
Application.Current.FindResource("IndicatorRedBrush") as SolidColorBrush));
}
... same for white brush
public SolidColorBrush ShiftOverageBrush {
get {
if (ShiftOverage.HasValue && ShiftOverage.Value.Milliseconds < 0) {
return IndicatorRedBrush;
}
return IndicatorWhiteBrush;
}
}
2) Using a lambda expression for NotifyPropertyChanged is convenient but is pretty slow since it uses reflection. If you're cranking up the update rate, then replace the lambdas with strings.
Related
This may be trivial. But I could not able to get my heads over this.
public class Manager
{
public int ID { get; set; }
public List<Employee> Employees { get; set; }
public bool IsAllEmpEngaged { get; set; }
public void UpdateIsAllEmpEngaged()
{
IsAllEmpEngaged = Employees.All(emp => emp.IsEngagedwithWork == true);
}
}
public class Employee
{
public int ID { get; set; }
public bool IsEngagedwithWork { get; set; }
}
So, Whenever, the IsEngagedwithWork of Employee is setted with some value, I want to check whether all the Employees under aManager is Engaged with work or not and update the value of IsAllEmpEngaged of the respective Manager.
I just want to call UpdateIsAllEmpEngaged on changes in property IsEngagedwithWork of Employee. How can I achieve this?
Any other ways are also welcome.
Note: I tried with a having an event on Employee and attach Action from the Manager that will callback if any changes in Employee property. But I will be having hundreds of List<Manager>. I dont want to add event for each and every instance of Employee class. Any easy way?
Update:
I am working with WPF MVVM approach, I cannot use direct get with LinQ as it will not notify the UI. I have to set the property manually for change so that it will Notify the UI.
Also, In actual case, the IsEngagedwithWork will be updated in UI for the property IsEngagedwithWork.
Simple solution is add ManagerId as well to the Employee model class and after your line of code that sets IsEngagedwithWork of the employee instance (say emp), do the below thing
Manager mngr = managers.Select(m => m.ID == emp.ManagerId).FirstOrDefault();
if(mngr != null)
mngr.IsAllEmpEngaged = mngr.IsAllEmpEngaged && emp.IsEngagedwithWork;
I'd use the getter of the property like that
public bool IsAllEmpEngaged {
get {
return (Employees != null) &&
Employees.All(e => e.IsEngagedwithWork)
}
}
and you add the following method for the Manager Class
public void NotifyChanged() { OnPropertyChanged(() => IsAllEmpEngaged }
then you call it from the Employee Class (assuming you have the managers' list or an equivalent way)
private int _ID;
private bool _IsEngagedwithWork;
public int ID {
get { return _ID};
set {
_ID = value;
OnPropertyChanged(()=>ID );
notifyMe = managerList.FirstOrDefualt(m => m.ID == _ID);
if (notifyMe != null) { notifyMe.NotifyChanged()}
}
}
public bool IsEngagedwithWork {
get { return _IsEngagedwithWork ;}
set {
_IsEngagedwithWork = value;
OnPropertyChanged(()=>IsEngagedwithWork );
notifyMe = managerList.FirstOrDefualt(m => m.ID == _ID);
if (notifyMe != null) { notifyMe.NotifyChanged()}
}
}
What i would do :
make IsAllEmpEngaged private
make the collection Empoyess private and add a function that add a new employee:
Add a function that adds a new employee.
And now two choiches :
1) after the employee is added, iterate the collection and update the IsAllEmpEngaged property
public void AddNewEmployee(Employee employee){
this.Employees.Add(employee);
bool all = true;
foreach(Employee emp in this.Employees){
if (!emp.IsEngagedwithWork){
all = false;
break;
}
}
this.IsAllEmpEngaged = all;
}
2)
start with IsAllEmpEngaged = true; when the class is inited and the collection is empty
when the employee is added, update the IsAllEmpEngaged property but keeping count of the last choice (this works only if you don't remove employess)
public void AddNewEmployee(Employee employee){
this.Employees.Add(employee);
this.IsAllEmpEngaged = this.IsAllEmpEngaged && employee.IsEngagedwithWork
}
I can't say that this is any better, but surely this is a simple and easy way.
Why not just make IsAllEmpEngaged a method? You don't even need the set property accessor, so a method should suffice.
public bool IsAllEmpEngaged()
{
if (Employees == null)
{
// throw error
}
return Employees.All(e => e.IsEngagedwithWork);
}
Maybe it work's when you pass your Manager as parameter to Employee and then call your Method if IsEngagedWithWork is set to true.
public class Employee
{
private Manager _parentManager;
public Employee(Manager parentManager)
{
_parentManager=parentManager;
}
public int ID { get; set; }
private bool _isEngangedWithWork;
public bool IsEngagedwithWork
{
get{ return _isEngangedWithWork; }
set
{
_isEngangedWithWork=value;
if(_isEngangedWithWork)
_parentManager.UpdateIsAllEmpEngaged();
}
}
}
I have class which have too many related calculated properties.
I have currently kept all properties are read only.
some properties need long calculation and it is called again when its related properties are needed.
How can create this complex object .Also i want these properties should not be set from external code. I need show hide as i am binding properties for UI. Also i think order is also important.
My Class is something like
public string A
{
get
{
return complexMethod();
;
}
}
public string B
{
get
{
if (A == "value")
return "A";
else return "B";
;
}
}
public bool ShowHideA
{
get
{
return string.IsNullOrEmpty(A);
;
}
}
public bool ShowHideB
{
get
{
return string.IsNullOrEmpty(B);
;
}
}
public string complexMethod()
{
string value = "";
// calculation goes here
return value;
}
}
Thanks
You need to use Lazy type provided by .net:
Lazy<YourType> lazy = new Lazy<YourType>();
Make your properties internal to not be set from external code.
Well tall order isn't it?
One of the coolest things about extension methods is you can use types. This is perfect for writing external programs to calculate property values. Start like this...
public static class XMLibrary
{
public static MC CalculateValues(this MC myclass)
{
//for each property calculate the values here
if (myclass.Name == string.Empty) myclass.Name = "You must supply a name";
if (myclass.Next == 0) myclass.Next = 1;
//when done return the type
return myclass;
}
}
public class MC
{
public string Name { get; set; }
public int Next { get; set; }
}
public class SomeMainClass
{
public SomeMainClass()
{
var mc = new MC { Name = "test", Next = 0 };
var results = mc.CalculateValues();
}
}
There are many other ways to do class validation on a model, for example dataannotations comes to mind, or IValidatableObject works too. Keeping the validation separate from the class is a good idea.
//Complex properites are simple
public class MyComplextClass{
public List<MyThings> MyThings {get;set;}
public List<FileInfo> MyFiles {get;set;}
public List<DateTime> MyDates {get;set;}
}
So I have been following this guide for data binding on Windows Forms controls (MAD props to the author, this guide is great), and I have used this to create a custom class and bind a DataGridView to a collection of this class:
class CompleteJobListEntry
{
private string _jobName;
private Image _jobStatus;
private DateTime _jobAddedDate;
private string _jobAddedScanner;
private string _jobAddedUser;
private string _jobLastActivity;
private DateTime _jobActivityDate;
private string _jobActivityUser;
public string JobName { get { return _jobName; } set { this._jobName = value; } }
public Image JobStatus { get { return _jobStatus; } set { this._jobStatus = value; } }
public DateTime JobAddedDate { get { return _jobAddedDate; } set { this._jobAddedDate = value; } }
public string JobAddedScanner { get { return _jobAddedScanner; } set { this._jobAddedScanner = value; } }
public string JobAddedUser { get { return _jobAddedUser; } set { this._jobAddedUser = value; } }
public string JobLastActivity { get { return _jobLastActivity; } set { this._jobLastActivity = value; } }
public DateTime JobActivityDate { get { return _jobActivityDate; } set { this._jobActivityDate = value; } }
public string JobActivityUser { get { return _jobActivityUser; } set { this._jobActivityUser = value; } }
}
At this point, I import a bunch of data from various SQL databases to populate the table, and it turns out great. The guide even provides an excellent starting point for adding filters, which I intend to follow a bit later. For now, though, I am stuck on the sorting of my newly generated DataGridView. Looking around, I've discovered that the DataGridView has its own Sort method, usable like:
completeJobListGridView.Sort(completeJobListGridView.Columns["JobName"], ListSortDirection.Ascending);
However, when I try to do this, I get an InvalidOperationException that tells me "DataGridView control cannot be sorted if it is bound to an IBindingList that does not support sorting." I've found both the IBindingList and IBindingListView interfaces, but making my class inherit either of these interfaces doesn't solve the problem.
How do I do this? I am completely stuck here...
If your data is in a collection, you should be able to use the BindingListView library to easily add sorting capabilities to your DGV. See How do I implement automatic sorting of DataGridView? and my answer to How to Sort WinForms DataGridView bound to EF EntityCollection<T> for more information and code snippets.
As is well known, CM doesn't support passing a object of complex type through NavigationService like MVVM Light. So I searched for a workaround and did it like this.
There are two viewmodels: MainPageViewModel and SubPageViewModel.
I first defined 3 classes, namely GlobalData, SnapshotCache and StockSnapshot. StockSnapshot is the type of which the object I want to pass between the 2 viewmodels.
public class SnapshotCache : Dictionary<string, StockSnapshot>
{
public StockSnapshot GetFromCache(string key)
{
if (ContainsKey(key))
return this[key];
return null;
}
}
public class GlobalData
{
private GlobalData()
{
}
private static GlobalData _current;
public static GlobalData Current
{
get
{
if (_current == null)
_current = new GlobalData();
return _current;
}
set { _current = value; }
}
private SnapshotCache _cachedStops;
public SnapshotCache Snapshots
{
get
{
if (_cachedStops == null)
_cachedStops = new SnapshotCache();
return _cachedStops;
}
}
}
public class StockSnapshot
{
public string Symbol { get; set; }
public string Message { get; set; }
}
Next, I call the navigation service on MainPageViewModel like this:
StockSnapshot snap = new StockSnapshot {Symbol="1", Message = "The SampleText is here again!" };
GlobalData.Current.Snapshots[snap.Symbol] = snap;
NavigationService.UriFor<SubPageViewModel>().WithParam(p=>p.Symbol,snap.Symbol).Navigate();
And on SubPageViewModel I've got this:
private string _symbol;
public string Symbol
{
get { return _symbol; }
set
{
_symbol = value;
NotifyOfPropertyChange(() => Symbol);
}
}
public StockSnapshot Snapshot
{
get { return GlobalData.Current.Snapshots[Symbol]; }
}
And that's where the problem lies. When I run the program, I find out that it always runs to the getter of Snapshot first, when Symbol hasn't been initialized yet. So later I've tried adding some extra code to eliminate the ArgumentNullException so that it can run to the setter of Symbol and then everything goes fine except that the UI doesn't get updated anyway.
Could anyone tell me where I've got wrong?
Thx in advance!!
Why not just use:
private string _symbol;
public string Symbol
{
get { return _symbol;}
set
{
_symbol = value;
NotifyOfPropertyChange(() => Symbol);
NotifyOfPropertyChange(() => Snapshot);
}
}
public StockSnapshot Snapshot
{
get { return Symbol!=null? GlobalData.Current.Snapshots[Symbol]:null; }
}
In this case you don't try and get the data from GlobalData when Symbol is null (sensible approach anyway!) and when "Symbol" is set you call NotifyOfPropertyChange() on Snapshot to force a re-get of the property.
I have a class which has been steadily growing over time. It's called LayoutManager.
It started as a way for me to keep track of which dynamically created controls were on my page. So, for instance, I have this:
public CormantRadDockZone()
{
ID = String.Format("RadDockZone_{0}", Guid.NewGuid().ToString().Replace('-', 'a'));
MinHeight = Unit.Percentage(100);
BorderWidth = 0;
HighlightedCssClass = "zoneDropOk";
CssClass = "rightRoundedCorners";
LayoutManager.Instance.RegisteredDockZones.Add(this);
}
In this way, during the beginning stages of the Page Lifecycle, controls would be re-created and they would add themselves to their respective control's list.
A while later I found myself passing the 'Page' object between methods. This was for the sole purpose of being able to access controls found on Page. I thought to myself -- well, I already have a Layout Manager, I'll just treat the static controls in the same way.
As such, my Page_Init method now looks like this mess:
protected void Page_Init(object sender, EventArgs e)
{
SessionRepository.Instance.EnsureAuthorized();
LayoutManager.Instance.RegisteredPanes.Clear();
LayoutManager.Instance.RegisteredDocks.Clear();
LayoutManager.Instance.RegisteredDockZones.Clear();
LayoutManager.Instance.RegisteredSplitters.Clear();
LayoutManager.Instance.RegisteredSplitBars.Clear();
LayoutManager.Instance.RegisteredPageViews.Clear();
LayoutManager.Instance.CheckBox1 = CheckBox1;
LayoutManager.Instance.CheckBox4 = CheckBox4;
LayoutManager.Instance.StartEditButton = StartEditButton;
LayoutManager.Instance.FinishEditButton = FinishEditButton;
LayoutManager.Instance.RadNumericTextBox1 = RadNumericTextBox1;
LayoutManager.Instance.RadNumericTextBox2 = RadNumericTextBox2;
LayoutManager.Instance.LeftPane = LeftPane;
LayoutManager.Instance.DashboardUpdatePanel = DashboardUpdatePanel;
LayoutManager.Instance.CustomReportsContainer = CustomReportsContainer;
LayoutManager.Instance.HistoricalReportsContainer = HistoricalReportsContainer;
RegenerationManager.Instance.RegenerateReportMenu();
LayoutManager.Instance.MultiPage = DashboardMultiPage;
LayoutManager.Instance.MultiPageUpdatePanel = MultiPageUpdatePanel;
LayoutManager.Instance.TabStrip = DashboardTabStrip;
RegenerationManager.Instance.RegenerateTabs(DashboardTabStrip);
RegenerationManager.Instance.RegeneratePageViews();
LayoutManager.Instance.Timer = RefreshAndCycleTimer;
LayoutManager.Instance.Timer.TimerEvent += DashboardTabStrip.DoTimerCycleTick;
RegenerationManager.Instance.RegeneratePageState();
}
I'm looking at that and saying no, no, no. That is all wrong. Yet, there are controls on my page which are very dependent on each other, but do not have access to each other. This is what seems to make this so necessary.
I think a good example of this in practice would be using UpdatePanels. So, for instance, DashboardUpdatePanel is being given to the LayoutManager. There are controls on the page which, conditionally, should cause the entire contents of the dashboard to update.
Now, in my eyes, I believe I have two options:
Inside the object wanting to call UpdatePanel.Update(), I recurse up through parent objects, checking type and ID until I find the appropriate UpdatePanel.
I ask LayoutManager for the UpdatePanel.
Clearly the second one sounds cleaner in this scenario... but I find myself using that same logic in many instances. This has resulted in a manager class which looks like this:
public class LayoutManager
{
private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly LayoutManager _instance = new LayoutManager();
private LayoutManager() { }
public static LayoutManager Instance
{
get { return _instance; }
}
private IList<CormantRadDock> _registeredDocks;
private IList<CormantRadDockZone> _registeredDockZones;
private IList<CormantRadPane> _registeredPanes;
private IList<CormantRadSplitter> _registeredSplitters;
private IList<CormantRadSplitBar> _registeredSplitBars;
private Dictionary<string, StyledUpdatePanel> _registeredUpdatePanels;
private IList<CormantRadPageView> _registeredPageViews;
public RadMultiPage MultiPage { get; set; }
public CormantTimer Timer { get; set; }
public CormantRadListBox HistoricalReportsContainer { get; set; }
public CormantRadListBox CustomReportsContainer { get; set; }
public StyledUpdatePanel MultiPageUpdatePanel { get; set; }
public CormantRadTabStrip TabStrip { get; set; }
public RadPane LeftPane { get; set; }
public StyledUpdatePanel DashboardUpdatePanel { get; set; }
public RadButton ToggleEditButton { get; set; }
public CheckBox CheckBox1 { get; set; }
public CheckBox CheckBox4 { get; set; }
public RadNumericTextBox RadNumericTextBox1 { get; set; }
public RadNumericTextBox RadNumericTextBox2 { get; set; }
public RadButton StartEditButton { get; set; }
public RadButton FinishEditButton { get; set; }
public IList<CormantRadDock> RegisteredDocks
{
get
{
if (Equals(_registeredDocks, null))
{
_registeredDocks = new List<CormantRadDock>();
}
return _registeredDocks;
}
}
public IList<CormantRadDockZone> RegisteredDockZones
{
get
{
if (Equals(_registeredDockZones, null))
{
_registeredDockZones = new List<CormantRadDockZone>();
}
return _registeredDockZones;
}
}
public IList<CormantRadPane> RegisteredPanes
{
get
{
if (Equals(_registeredPanes, null))
{
_registeredPanes = new List<CormantRadPane>();
}
return _registeredPanes;
}
}
public IList<CormantRadSplitter> RegisteredSplitters
{
get
{
if (Equals(_registeredSplitters, null))
{
_registeredSplitters = new List<CormantRadSplitter>();
}
return _registeredSplitters;
}
}
public IList<CormantRadSplitBar> RegisteredSplitBars
{
get
{
if (Equals(_registeredSplitBars, null))
{
_registeredSplitBars = new List<CormantRadSplitBar>();
}
return _registeredSplitBars;
}
}
public Dictionary<string, StyledUpdatePanel> RegisteredUpdatePanels
{
get
{
if( Equals( _registeredUpdatePanels, null))
{
_registeredUpdatePanels = new Dictionary<string, StyledUpdatePanel>();
}
return _registeredUpdatePanels;
}
}
public IList<CormantRadPageView> RegisteredPageViews
{
get
{
if (Equals(_registeredPageViews, null))
{
_registeredPageViews = new List<CormantRadPageView>();
}
return _registeredPageViews;
}
}
public StyledUpdatePanel GetBaseUpdatePanel()
{
string key = MultiPage.PageViews.Cast<CormantRadPageView>().Where(pageView => pageView.Selected).First().ID;
return RegisteredUpdatePanels[key];
}
public CormantRadDockZone GetDockZoneByID(string dockZoneID)
{
CormantRadDockZone dockZone = RegisteredDockZones.Where(registeredZone => dockZoneID.Contains(registeredZone.ID)).FirstOrDefault();
if (Equals(dockZone, null))
{
_logger.ErrorFormat("Did not find dockZone: {0}", dockZoneID);
}
else
{
_logger.DebugFormat("Found dockZone: {0}", dockZoneID);
}
return dockZone;
}
public CormantRadPane GetPaneByID(string paneID)
{
CormantRadPane pane = RegisteredPanes.Where(registeredZone => paneID.Contains(registeredZone.ID)).FirstOrDefault();
if (Equals(pane, null))
{
_logger.ErrorFormat("Did not find pane: {0}", paneID);
}
else
{
_logger.DebugFormat("Found pane: {0}", paneID);
}
return pane;
}
public CormantRadDock GetDockByID(string dockID)
{
CormantRadDock dock = RegisteredDocks.Where(registeredZone => dockID.Contains(registeredZone.ID)).FirstOrDefault();
if (Equals(dock, null))
{
_logger.ErrorFormat("Did not find dock: {0}", dockID);
}
else
{
_logger.DebugFormat("Found dock: {0}", dockID);
}
return dock;
}
}
Am I on a bad path? What steps are generally taken at this point?
EDIT1: I have decided to start down the path of improvement by finding the controls which are least-integrated into LayoutManager and finding ways of breaking them down into separate objects. So, for instance, instead of assigning the HistoricalReportsContainer and CustomReportsContainer objects to LayoutManager (which is then used in RegenerationManager.RegenerateReportMenu) I have moved the code to RadListBox "Load" event. There, I check the ID of the control which is loading and react accordingly. A strong first improvement, and has removed 2 controls and a method from LayoutManager!
Inversion of control is a general approach that people use for such problems. Your dependencies should not be stored in the one Jack-Bauer-kind-of-style class, but rather be injected, for example via constructor. Take a look at the IoC containers, such as Castle Windsor, Unity, NInject or any other.
I'm not sure how this would interact with future plans of MVC, but had you considered refactoring chunks of LayoutManager into an abstract class that inherits from Page, then having your actual pages inherit from that abstract class?