This question relates to strategy rather than specific code. I'm struggling to think of a viable approach and I'm hoping somebody may have encountered a similar challenge.
I'm building an Animate program that will:
Enable the user to draw a line on the stage (by means of a series of mouse clicks).
Capture the coordinates of each mouse click in an array, and add them to a table that is displayed and updated in the UI.
Display the resulting line.
Enable the user to edit any of the coordinates, and update all of (1) (2) and (3) accordingly.
The first three steps are working OK, but darn ... how should I make that data editable? I've put a listener on the textfield that holds the set of coordinates, but I think that's a dead end. It's just a string with line breaks, so it would be hard to edit a particular few characters and have AS3 / Animate detect what had changed.
Is there a good technique for this?
Background: was comfortable with AS1, skipped AS2 completely, and now (12 years later), I'm grappling with AS3. It's been frustrating, but ... I've built a package and it's working.
The most important concept you need is the Model-View-Controller design pattern.
model
Start with defining your model which represents the data in your application.
Do so according to your requirements.
The minimum requirement for an object to work as the model is that it notifies others when it got changed, because MVC is based heavily on the observer pattern and you want to observe when your data changes. In As3, using the built in EventDispatcher makes sense.
In your case, a nested model makes sense, that is, you have a model for each individual point and then another model that acts as a collection of all points.
Add methods to your model classes to modify the data. You want to add and remove points to the collection of points and you want to modify the coordinates of each individual point.
view
Each view receives a reference to the model object that represents the collection of points. One view might draw them into a coordinate system and connect them with lines, another might be a tabular representation.
controller
The controller knows about the model and reacts to user input on the view.
For example, when somebody clicks on the view, a new point is inserted.
Given the simplicity of your application, it might makes sense to combine view and controller, so that each view directly manipulates the model.
how should I make that data editable?
By creating methods on the model that allow you to do exactly that.
Say for example the LineView class receives the PointListModel which includes all points and stores it in a private variable
public LineView (pointList:PointListModel)
{
list = pointList;
Now it will create graphical assets to represent the points, based on the list (this should be executed whenever the PointListModel object signals a change:
private onListChanged(event:Event):void
{
for each(var point:PointModel in list)
{
// draw circle or whatever
As the creation of each visual asset in the view is based on the model, it should be possible to connect them. When the user now modifies the visual asset, the made modifications can be applied to the PointModel object. Remember: any modifications made to a model will cause it to dispatch an event that it was modified. This will inform all other views that this point was modified.
This is how dragging & dropping the circle representing a point in one view will make the coordinates of that point displayed in another view update as if they are linked together, because they are: via the shared model object.
I am still a bit hazy about how I would associate the displayed table with underlying array data. Should I make each row of the table a separate textfield with an associated listener?
This is up to you.
You could make the entire table only listen to the change event of the model that represents all points. This would cause the entire table to be updated.
Or you could make the table observe the point list model to add or remove table entries and each entry in the table observe a point in that list. This allows you to only update a single entry in the table if only one point got updated.
Keep in mind that the components of flash do a lot of this for you. Flex has even more sophisticated components. The way each entry is rendered can be defined with a custom itemrenderer.
Related
Say I have a main database class, TvShowDatabase, which contains a list of Shows. Each Show has a list of Episodes, each of which has a Length. The objects TvShowDatabase, Show and Episode all implement INotifyPropertyChanged.
I would like to compute and show TotalViewingPotential, a property of TvShowDatabase, which sums the Length of each Episode of each Show.
Moreover, let's say the user is looking at this structure in a tree style, and is able to edit the Length of any Show. When they do this, the TotalViewingPotential should update accordingly, and the results be seen on the screen.
My question: in WPF (and specifically, using Prism for MVVM), what is the best way to hook up the plumbing for these change notifications?
I've considered intercepting adds to each list (using ObservableCollection) and giving any new items a Parent. This, however, gets messy, and a top-down approach is preferable.
I've seen hyper-observable collections (http://www.codeproject.com/Tips/694370/How-to-Listen-to-Property-Chang), but I worry about event-subscription based memory leaks with this method, and it still requires a good bit of manual plumbing to raise the OnPropertyChanged(TotalViewingCollection) events where needed.
Is there a pattern for this?
The task, as you describe it, has a simple solution. You do not need to propagate changes along the collections.
TotalViewingPotential is a normal INotifyPropertyChanged property. Make it accessible from your Length properties.
In the setter of the Length property, compute the difference between the new and the old values, and increment the TotalViewingPotential by the difference.
That's all.
To speed up initial loading, in order not to generate notifications for all Lengths, you may set the values of the backing fields of Lengths, and compute and and set TotalViewingPotential based on the values from the DB.
Right now I use 2 arrays one of TImage the other of TMemo to draw an image next to text data row after row inside a scroll box.
I am looking to replace the TMemo with several components on a Panel. So probably some static text a button and some labels. But the way I'm doing it now it seems as if this will become messy.
Is there a better way of going about this without writing a component or class. Should I be using some sort of multi dimensional array or can I use a record structure to declare the components and have an array of that?
A record would at least relieve you of the burden of managing lots of parallel arrays, but you can do better than a record, and better than an array.
First, you can design a frame to represent one "row" of your form. Give it an image, button, label, and whatever else you need. Then create an instance of that frame class each time you need one. It will create the components for you automatically. You said you didn't want a custom component, and that's essentially what a frame is, but by getting to design it visually like you would a form, much of the burden of creating a composite control is lifted, so you just get the benefit of a group of related controls that can interact with each other as a unit.
Instead of an array, you might find better success with a different collection object, especially TComponentList which can grow and shrink more easily than an array, and also helps manage ownership of its contents.
Simple example:
I have VM with hierarchical item structure and I have designed View containing TreeView. Each tree node corresponds to data item related to the one in the Model.
GUI must respect user preferences and settings and store different parameters including whether each particular node is expanded or not. As long as every node relates to some data we need to tie View settings with the Model. I beleive it's not OK to define property IsExpanded in the class standing for the Model.
So where should I store the settings so they stayed consistent with the data?
I believe it's not OK to define property IsExpaned in the class standing for the Model.
Why? Will there be occassions when the model is linked to two different tree-views?
If that is the case you can certainly create an "adapter view-model" that wraps your models and adds the property. Just be careful because that can technique can easily lead to a memory leak.
Otherwise, it is perfectly acceptable to put it right in the model, especially if that model is being used specifically to serve that view. As with all things, start with the simplest, most pragmatic approach.
I was in a similar situation, here's what I did to solve it.
Each ViewModel node had an IsExpanded property, which was bound to the TreeViewItem's IsExpanded property. I didn't want to persist the state of the tree by storing it in the Model in IsExpanded properties (it's to do with visual state right?). So instead, I had the VM tree structure generate a dictionary that stored the expansion state for each node against a string key generated from the node's state in the tree:
Dictionary<string, bool> treeExpandedStates
Each node in the tree had an ID, so in my case the key was something like "/1/3/7", but anything unique will do. This Dictionary was then serialized to a file on application close (in actual fact it was a SerializableDictionary). Then on application restart, it was deserialized and used to set the expansion state after the hierarchy had been loaded back up. This meant that the state of the tree was exactly as the user had left it, but nothing was stored in the Model.
I recognize the problem as a common dilemma in MVVM. I can look at it from two sides.
Approach A)
In the separation of View, Model, and ViewModel, what you describe resides in the Model. You write for example that it needs to be stored. That does not mean it is the same part of the model as the other model data.
Consider the following separation:
FolderModel - A model of the content or properties of a folder.
TreeNodeModel - A model of a users choices when exploring the tree view.
It might not be that easy to do a separation just like that, but the point is that MVVM is not meant to force you to stuff everything in the same place, and I do not think MVVM prevents you from keeping models for user interactions, the same way as you keep models for data content. Microsoft writes:
The data could come from a database, a Web service, a named pipe, a file on disk, or even carrier pigeons: it simply does not matter.
Why should not data for a data model be able to come from interactions of a user? :)
Approach B)
My usual approach to these dilemmas, though, is that properties like IsExpanded does mostly not need to be stored between sessions. That way, a property in the ViewModel, with a default value instead of a stored value, suffices. :)
And if it needs to be stored, it does not need to be stored in the Model. The ViewModel is for logic for presenting model data. If the ViewModel wants to save its logic state, that does not have to be stored in the Model.
Summary
In my point of view, you can store it any of these ways without breaking the MVVM pattern.
Say I have a WinForm CRUD(like) application. I want to follow best practices on this so I try and make it follow OOP and a n-Tiered design.
Unfortunately I am familar with the words but not the practice of them. So lets go with the following example: My CaseNote program. I have a tabbed application where you go to the search tab to find a member and then one of the options is to go to the CaseNote tab to create a new case note, look at existing CaseNotes, or create a follow up CaseNote to a Parent Note. All of this is for the member you selected from the search tab.
So if I am creating objects and passing them around to be used by different forms where should I be instantiating them? I had thought I would have 3 layers; UI, Object, DAL. Where I fall flat is where I instance tho objects. I can't instance them in the object layer, can I? Also, if I instance them on the form how do I pass them from form to form in a quite large app?
CaseNotes Screen Shot
If you want to look at some more words around this problem you want to look at MVP and MVC. (These stand for Model View Controller and Model View Presenter). Some people will shoot me down for saying this but they are quite similar in concept.
The aim of MVP and MVC is to allow you to design your application logic without even having to think about your application apperance. It also allows you to define your user interactions without implementing an actual GUI. Esentially your model is your application logic, your data, your classes which actually do stuff like talk to your database. Your presenter or controller is what interacts with your model and what controls your user interface and reacts to user operations on the interface. Finally your View is your winforms design or your web page.
I'm sure you will be able to find plenty of material on the web about this but to give you some concrete help with this problem should serve to inform and illustrate your reading.
The first thing you need to do is start creating your objects that represent your data. So you will have a CaseNote object which is contains the casenote data. You will have a case note data container of some sort such as a case note database. You can define the logical operations and properties of these as if they where real items.
Then you would move on to define your presenter or controller which will define the operations that you want to support from the GUI. At the same time you should define an Interface that will define for the presenter/controller what operations is can perform on the GUI. So for instance your presenter may expose a method called SearchForCaseNote which takes a string parameter. Your view Interface will expose a method called DisplayCaseNote. When a user clicks on the search button the view will pass through the command to the presenter which will then call the model to get the data. The presenter may format the data at this point, i.e. convert DateTime object to a string and then pass the data back to the view through the interface define method called DisplayCaseNote.
You don't have to use the View interface, you could call into the view directly, but having the interface means you can have many different view implementations.
One last thing i need to mention is where you create these different parts of your application. My view is everything thing should fall out from the presenter/controller. So when you application starts it creates the presenter/controller object which then create and displays your view passing itself as a variable to the view. The presenter/controller can then either create the initial models by loading them from disk or ideally discover them through a dependency injection container like unity. In fact using unity to discover the view implementation is probably a better idea again as it gives you true seperation between view and presenter/controller. When you come to move to another view, (i.e. open another window), your presenter/controller should expose a method such as DisplayDetailPage which the view calls when a button is clicked. This would create the presenter/controller for the next view which would in turn create the view and get a reference to the model.
Hope this helps.
I think you should use the RocketFramework
Background
WinForms application using NHibernate. Application is in MDI style and each MDI child form opens a new NHibernate session at Load which remains open for the life of the form.
Question
My application is basically an "order management" or "purchasing" system. One particular form uses a lot of "lookup" lists. Like a list of products, a list of vendors, a list of locations, a list of UnitsOfMeasurement, a list of PriceQuotes, etc.
Lots of lists, that all get loaded when the form is constructed.
Problem: I need the lookup lists, but I need the form to be a bit faster to load. The form is taking too long to perform all the lookups. How can I get better performance and keep my lookup lists?
My Thoughts
Can I load the lookup lists once and hold on to them for the life of the application, and periodically check to see if the lists are stale?
Can I load just the text description for the lists, and instead of holding a bunch of IList, IList, etc, I could hold a bunch of IList, and then when I save, perform the Gets against NHibernate to get the real object.
Is there some other approach that I just haven't thought of?
You should definitely cache slowly changing data to improve performance. How often you need to check for stale data depends on the type of data and your business, e.g. units of measure probably doesn't change as frequently as a list of products. You should also provide a method for manually refreshing lists so that the user can refresh them if something appears to be missing.
If you need the business objects in the list in order to perform a database operation, you can call ISession.Lock(obj) to lock the object into the current ISession. One thing to be aware of is that the lock doesn't automatically cascade to child objects: I think there's a mapping setting to do that or you can do it manually.
Are you sending lists of full objects to your UI? I recently worked on an app using DTO's between the data layer and the UI so I'm not sending the full object, just a description and an identifier. That could help you trim out some unneeded data. So basically when the screen loads a service call is made, nhibernate gets all of the objects I want for my list box, then the UI binds to the list. I bound my listbox display member to the description and the value member to the identifier.