windows wpf MVVC pattern, how to fit code logic in that pattern - c#

I'm working now with converting existing winform app to WPF. I've never coded anything using MVVC pattern before and I have a hard time trying to figure out how to correctly set models, viewmodels and views in my specific case.
App that I want to convert is used for communicate with physical devices using serial port.
Let's first start with existing winform app. Almost any of the logic is in separate .dll file that expose interface which winform consumes. There are really no "code behind" while displaying data because dll already expose everything that is needed. There are only some additional functions in GUI for manipulation of the data, saving current data etc.
The thing is that I really don't understand how to fit this nicely in MVVM. As model as I understand I will need to create some let's say device that will have all the properties that are changeable and readable. And first question, if model needs to be concrete class or it may be some interface (which I already have) or maybe abstract device class (that implements this interface)?
Second question about ViewModels. I understand that ViewModel is somehow used for "glue" model data to view. And from that perspective in ViewModel I can put all the code that as a result format data from device to output it in some nice formats that will be easily databinded to View. But, here is a quirk. I think that this inteface that I'm now using in that .dll file fits nicely to be databinded in gui (or maybe not?).
Another question is about View itself. I understand that View wouldn't be aware of model etc. But could I put in view that code that I already have in winform GUI? (for saving data to csv files, for doing some measurements etc) or maybe I will need to create another model (I have no idea what it may be looks like).
And the last question is where to put all the logic for using serial port, for concrete implementation of all functions etc. I believe that models should be as easy as possible (like in mvc pattern) without any logic and so on (but again, if it should be in that way, maybe model should be only interface?). And ViewModels should have only code for data manipulation to expose data in friendly format to view and convert it back to model. So where rest of the logic should exist? Thank you all in advance for explaining this thing. And in order to put some code to my post, below is interface that I'm using in that .dll file
public interface IScope
{
event EventHandler NewDataInBuffer;
bool Destroy();
bool Connect();
bool Disconnect();
bool StartCapture();
bool StopCapture();
string ScopeName { get; }
IParameter<float> CurrentVoltageLimit { get; }
IParameter<int> DataSamplesPerDiv { get; }
List<IParameter<Config.Timebase>> AvailableTimebaseSettings { get; }
List<IParameter<Config.Coupling>> AvailableCoupleSettings { get; }
List<IParameter<Config.Slope>> AvailableTriggerSlopeSettings { get; }
List<IParameter<Config.VerticalSensitivity>> AvailableSenitivitySettings { get; }
List<IParameter<Config.TriggerMode>> AvailableTriggerModeSettings { get; }
List<IParameter<Config.RecordLength>> AvailableRecordLength { get; }
IParameter<Config.Timebase> TimeBase { get; set; }
IParameter<Config.Coupling> Couple { get; set; }
IParameter<Config.Slope> TriggerSlope { get; set; }
IParameter<Config.VerticalSensitivity> Sensitivity { get; set; }
IParameter<Config.TriggerMode> TriggerMode { get; set; }
IParameter<Config.RecordLength> RecordLength { get; set; }
int TriggerPosition { get; set; }
float TriggerLevel { get; set; }
}
public interface IParameter<T>
{
string ParameterName { get; }
string ParameterValue { get; }
string ParameterUnit { get; }
bool IsReadOnly { get; }
T GetParameter { get; }
}

Related

Controller accepting different models

I want to accept different model type from body based on query param value.
Example:
[HttpGet]
[Route("GetSystemdetails")]
public string Getdeatils([FromBody] SystemDetail sysdetails, string type)
{
//some code here
string details = getdetails(sysdetails);
}
// abc model
public class abc
{
public int UserID { get; set; }
public string Email { get; set; }
}
//xyz model
public class xyz
{
public int xyzid { get; set; }
public string systemval { get; set; }
public string snum { get; set; }
}
type abc and xyz will have it's own model. So based on type I receive in query param I wanted to pick the model and proceed.
Sample url:
localhost/GetSystemdetails/type=abc
localhost/GetSystemdetails/type=xyz
I thought of creating a new model SystemDetail which holds these two models(xyz and abc) and based on system pick them.
I wanted to know what are possible ways to achieve this kind of requirements without creating multiple methods in controller(I don't want to change the format of the URL).
That's not something that's supported out of the box. Your linked solution is probably the closest you'll get to that.
ASP.NET Core is not supposed to take values of the parameters into account when routing, except for validation.
There are several possible ways to do so
Having multiple model objects
As in the link you provided, you can declare multiple model objects. The site has given the example of
public class PostUserGCM
{
public User User { get; set; }
public GCM GCM { get; set; }
}
but you can use your own examples.
Base model
Your models can inherit from some base model. If you only need a single model at a time and they share some similarities, then you could just create a base model which the other two are inheriting from, be agnostic at implementation time and your use cases will mainly differ on instantiation inside the controller, while some service methods could handle other differences.

Issue with lambda expressions in c# data retrieval

i'm writing a system to track observation values from sensors (e.g. temperature, wind direction and speed) at different sites. I'm writing it in C# (within VS2015) using a code-first approach. Although i've a reasonable amount of programming experience, I'm relatively new to C# and the code-first approach.
I've defined my classes as below. I've built a REST api to accept observation reading through Post, which has driven my desire to have Sensor keyed by a string rather than an integer - Some sensors have their own unique identifier built in. Otherwise, i'm trying to follow the Microsoft Contoso university example (instructors - courses- enrolments).
What I am trying to achieve is a page for a specific site with a list of the sensors at the site, and their readings. Eventually this page will present the data in graphical form. But for now, i'm just after the raw data.
public class Site
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Sensor> Sensors { get; set; }
}
public class Sensor
{
[Key]
public string SensorName { get; set; }
public int SensorTypeId { get; set; }
public int SiteId { get; set; }
public ICollection<Observation> Observations { get; set; }
}
public class Observation
{
public int Id { get; set; }
public string SensorName { get; set; }
public float ObsValue { get; set; }
public DateTime ObsDateTime { get; set; }
}
and I've created a View Model for the page I'm going to use...
public class SiteDataViewModel
{
public Site Site { get; set; }
public IEnumerable<Sensor> Sensors { get; set;}
public IEnumerable<Observation> Observations { get; set; }
}
and then i try to join up the 3 classes into that View Model in the SiteController.cs...
public actionresult Details()
var viewModel.Site = _context.Sites
.Include(i => i.Sensors.select(c => c.Observations));
i used to get an error about "cannot convert lambda expression to type string", but then I included "using System.Data.Entity;" and the error has changed to two errors... on the 'include', I get "cannot resolve method 'include(lambda expression)'...". And on the 'select' i get "Icollection does not include a definition for select..."
There's probably all sorts of nastiness going on, but if someone could explain where the errors are (and more importantly why they are errors), then I'd be extremely grateful.
Simply you can you use like
viewModel.Site = _context.Sites
.Include("Sensors).Include("Sensors.Observations");
Hope this helps.
The way your ViewModel is setup, you're going to have 3 unrelated sets of data. Sites, sensors, and observations. Sites will have no inherent relation to sensors -- you'll have to manually match them on the foreign key. Realistically, your ViewModel should just be a list of Sites. You want to do
#Model.Sites[0].Sensors[0].Observations[0]
not something convoluted like
var site = #Model.Sites[0]; var sensor = #Model.Sensors.Where(s => SiteId == site.Id).Single(); etc...
Try doing
viewModel.Site = _context.Sites.Include("Sensors.Observations").ToList();
Eager-loading multiple levels of EF Relations can be accomplished in just one line.
One of the errors you reported receiving, by the way, is because you're using 'select' instead of 'Select'
And lastly, be aware that eager-loading like this can produce a huge amount of in-memory data. Consider splitting up your calls for each relation, such that you display a list of Sensors, and clicking, say, a dropdown will call an API that retrieves a list of Sites, etc. This is a bit more streamlined, and it prevents you from getting held up because your page is loading so much information.
Update
I've created a sample application for you that you can browse and look through. Data is populated in the Startup.Configure method, and retrieved in the About.cshtml.cs file and the About.cshtml page.. This produces this page, which is what you're looking for I believe.

ASP.net MVC Is it possible to modify a class object in a view?

I am a newbie and creating a website where you can create your own custom quizes. Ive made a database that stores a class object mytests that consists of a name, and a list of questions parameter.
public class MyTests
{
public int ID { get; set; }
public string name { get; set; }
public string description { get; set; }
public List<MyQuestions> AllTestQuestions;
}
//using this object for questions
public class MyQuestions
{
public string QuestionDescription { get; set; }
public string MultipleChoiceCorrect { get; set; }
public string MultipleChoiceB { get; set; }
public string MultipleChoiceC { get; set; }
public string MultipleChoiceD { get; set; }
public string Answerexplanation { get; set; }
}
I'm using the default database code generated by visual studio. I have no problem adding this test object(mytest) to the database, but what I want to do is that on the edit.cshtml view I want to be able to add elements to the question list before returning the object to the database saved.
The problem is I don't know how to edit the model object from the view, or if this is even possible. I could maybe get it to work through a redirect? but I thought that adding the elements directly from the view would be easier. Is it possible to modify the model.object inside a view from the view (putting security concerns aside)?
For example model.title = something;
or
model.list.add()
Is anything like this possible?
If this question is not clear please let me know and I will try to clarify in the comments.
Yes, it is possible to edit the model from within the view.
From within your .cshtml file specify the view model using the #model declaration, then edit the model like so:
#model Namespace.For.MyTests
#Model.name = "Hello World";
<p>#Model.name</p>
Whilst this would work, it's not really what the view is for so I wouldn't recommend it.
The view is about presenting your data, not mutating it - that should be done in the controller, or domain layer. As soon as the user leaves the page then your changes will be lost due to the stateless nature of the web (.NET MVC passes data to the view from the controller, then ends the request).
This should be done at the controller level. You could do it on a view but it's not what the view is for.
Your issue is that if the page is refreshed you will lose you content, so if you do anticipate on the page refreshing you will need a way in which to temporarily hold the information before it being saved.
On a side note, I'd also consider renaming your classes "MyTests" to "MyTest" (singular) and "MyQuestions" to "MyQuestion"... it's just good practice because then you'd have a List of singleton "MyQuestion" in a "MyTest". EntityFramework Codefirst will pluralise the names when the database is created/update.

How do I design a specific super class

I am trying to refactor a solution to bring on board another project.
I have a Core project where common classes across projects reside.
I've tried to simpify my question by using 2 imaginary projects: Holidays and Weather...
I have a file load process setup for the Holidays project which has the following 2 classes:
public class Job
{
public virtual string CreatedBy { get; set; }
public virtual DateTime? CreatedDate { get; set; }
public virtual Security Security { get; set; }
protected IList<File> _files = new List<File>();
public virtual IEnumerable<File> Files
{
get { return _files; }
}
}
public class File
{
public virtual string FileName { get; set; }
public virtual FileType FileType { get; set; }
public virtual FileStatusType FileStatusType { get; set; }
public virtual Job Job { get; set; }
}
The file load process for the Weather project has exactly the same structure as Holidays, except that the Jobs class does not have a Security property.
My question is, is it possible to somehow move both classes into the Core project to allow both projects to use them?
Obviously Weather does not need the Security property, so I was thinking I would have a Core.Job class without Security, and then extend the Core.Job in Holidays.Job.
But once I do that, in the Core.File class, what Job is it referring to? As it sits in the Core project it must be the Core.Job.
So would I then need to have Job and File sit in Holidays, and Weather (and any other future projects) use the Core.Job and Core.File?
I don't want the Core project to have any references to sub projects.
I am using NHibernate, and so have mapping files - adding to the complexity.
Hope this is clear enough
Thanks
You can certainly do this, but I am not sure whether it brings you true benefit:
Does the Core itself work with the base Job in any way? If it does not, implementing Job separately in each project may help you keep coupling loose, even though I'd a little redundant. In code I wrote, I have sometimes introduced unnecessary dependencies by extracting interfaces without adding true benefit. This is why I am a bit precautious.
In case Core does acutal work with it, the part to refactor into the common base Job is perhaps the interface it works with.
You may think of an interface instead of a base class. Security may semantically belong to another interface. Moreover, you hand over a lot of control over your classes to the Core.
Do you ever hand a job from one project to another (or are they mapped to the same DB table via NHibernate?)? If you don't, an internal redundant class may be fine too.
Not very clear why confuse on the soluton offered by you (assuming that I right understood you)
//Core DLL
public class Job
{
public virtual string CreatedBy { get; set; }
public virtual DateTime? CreatedDate { get; set; }
protected IList<File> _files = new List<File>();
public virtual IEnumerable<File> Files
{
get { return _files; }
}
}
in the Hollidays you have
public class HollidayJob : Job
{
public virtual Security Security { get; set; }
}
in Weather simply use a type Job, if it selfsufficient.
In this case you refer CoreDLL from Holliday project and Weather. When you serialize it via NHibernate it for HollidayJob save one field more, but when Weather reads the same table it skips that field, as don't know anything, and don't actually care abotu it.
Hope this helps.

ASP.NET MVC 2 and AutoMapper - dissecting and mapping uploaded file info with AutoMapper

This is the 3rd major edit to this question, so I'm going to write a quick little summary first, then ask the question.
I have an input/edit model I'm planning on using with an EF4-backed MVC 2 site. The model is as follows:
public class AdminGameEditModel
{
[Required]
public int GameID { get; set; }
[Required(ErrorMessage="A game must have a title")]
public string GameTitle { get; set; }
[Required(ErrorMessage="A short URL must be supplied")]
public string ShortURL { get; set; }
[Required(ErrorMessage="A box art image must be supplied")]
public HttpPostedFileBase BoxArt { get; set; }
[Required(ErrorMessage="A large image for the index page is required")]
public HttpPostedFileBase IndexImage { get; set; }
[Required(ErrorMessage="A game must have a review")]
public string ReviewText { get; set; }
[Required(ErrorMessage="A game must have a score")]
public int ReviewScore { get; set; }
[Required(ErrorMessage="A game must have at least one Pro listed")]
public string[] Pros { get; set; }
[Required(ErrorMessage="A game must have at least one Con listed")]
public string[] Cons { get; set; }
[Required(ErrorMessage="A game must belong to a genre")]
public int GenreID { get; set; }
[Required(ErrorMessage="A game must be associated with at least one platform")]
public int[] PlatformIDs { get; set; }
}
I'd like to map it to a Game entity for creation/updating. There's a snag, though - I need to save the images in a particular folder, and then take their paths and save those as properties in my entity. So, an example for clarity's sake: rather than my Game entity having a actual BoxArt image, it would instead have the path to the correct BoxArt image. I hope this makes sense. Let me know if I need to clarify.
Can I do this with AutoMapper? If so, can anyone provide some code guidance?
EDIT:
Part of the problem is that my model is fairly complex, as it contains a many-to-many relationship. The PlatformIDs are ultimately used to build/rebuild (depending whether I'm creating or updating an entity) linked Platform entities in the Game/Platform map. I'm not sure if AutoMapper can do something that complex without needing to go through my repository.
Then there's the problem of the image paths. The paths aren't a property of HttpPostedFileBase, but must be constructed by hand like so:
if (BoxArt.ContentLength > 0) {
var fileName = Path.GetFileName(BoxArt.FileName);
var path = Path.Combine(Server.MapPath("~/Content/Images/BoxArt"), fileName);
BoxArt.SaveAs(path);
}
So, what I'm looking for is more complex than just trying to map simple properties across objects. I'd like to keep a reference to my edit model out of my repository. Separation of concerns, and all that. Because of that, I need to map to an entity before I attempt to pass it to my repo for saving. I'm just not sure how to do it without blending app layers.
If I understand you, all you need to do is update your properties first prior to the call to AutoMapper.
Make sure your object has the correct values prior to calling AutoMapper.
After the call to do the mapping, your destination object will have all the matching properties copied over.
Post some more code if this doesn't answer the questoin.

Categories