I'm wondering how to solve/simplify a task that sometimes pop-ups during developement with DevExpress XtraGrid suite.
Sometimes i fall into this case:
Given these 2 classes that represents the model classes that comes from the Business Logic layer:
public class Customer
{
public Int32 CustomerId { get; set; }
public String Name { get; set; }
public String Address { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public Int32 OrderId { get; set; }
public String ItemCode { get; set; }
public Int32 Quantity { get; set; }
public Decimal Price { get; set; }
public DateTime Date { get; set; }
}
I'd like to create a simple window that shows an XtraGrid that allows edit/add/remove a list of customers and its nested orders.
In order to do that, i've created a simple form with a GridControl and a GridView controls with the AllowAddRow and AllowDeleteRow properties == true.
Then, in Form1 class i've done the following:
//List of my customers
private List<Customer> _customers;
public Form1()
{
//Initialize UI components
InitializeComponent();
//Call the provider in order to get customers
CustomerProvider cp = new CustomerProvider();
_customers = cp.GetCustomers();
//Initialize bindingSource
BindingSource bs = new BindingSource();
bs.DataSource = _customers;
//Set GridControl's dataSource
gridControl1.DataSource = bs;
}
Now i've got beautiful GridControl that shows the content of my List.
But now here's the problem...how can i add or delete row?
Infact:
If i set the focus on a row and i press "Delete" key, it doesn't
work.
If i try to add a new row, when it looses focus, it suddently disappears.
Obviously i'm missing something. Unfortunately i found the DevExpress documentation a quite confusional (in my opinion) about this kind of argument and the best practises, so i can't reach my goal.
Someone can help me?
PS.
This is the hyperlink for the .csproj of my sample.
Set the gridControl1.UseEmbeddedNavigator property to true. You'll get a data navigator widget at the bottom of your view.
That only gets you halfway there, however. If you want to be able to add Orders to a Customer from the grid, your Customer class needs to use BindingList instead of List for the Orders property.
See here for more information. If you find the documentation lacking, you can also find helpful resources at the Support Center.
EDIT: Some other options to consider are:
Bind your grid to a database.
Bind to XML data and use a DataSet.
If you're already storing your data in a database, then option 1 would be the way to go. If you're not persisting your data anywhere yet, you could go either way.
Related
I probably have already asked similar question to this, but this one is little different.
I am at a point where user has an option to add a hardware in the system.
User can select its type while adding the Hardware, now based upon the type I can get its kind, and each Hardware kind its list of properties (some Hardware has one property, some two and some three or more).
The thing I haven't been able to figure out is how can I show input text boxes where the user can provide the properties based upon the type the user selects. I have to provide the name of the property based upon the type and user types the value. Which then I have to retrieve from view and save.
Here is a small image of what the hierarchy looks like.
Here is the image of what my current view looks like.
So, now whenever user types in ModuleID and selects the type, I need to be able to show some input text box or new form based upon the type where user can enter the property value.
Here is my view model, which I don't know is the right one or not for this situation.
public class CreateModule
{
[Required]
public string ModuleId { get; set; }
[DataType(DataType.DateTime)]
public DateTime DateEntered { get; set; }
[Required]
public string SelectedModuleTypeName { get; set; }
public IEnumerable<SelectListItem> TypeNames { get; set; }
public List<Property> Properties { get; set; }
}
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
Has anyone done something like this before, or have idea how to solve this problem? Any tips, idea, suggestions, useful links are welcome.
I hope I have explained my issue properly if not, do comment on how can i make it better.
I use breezejs in my Durandal web application.
Here is my code to get my invoice & lines behind it:
var getInvoiceById = function (invoiceId, invoiceObservable, forceRemote) {
// Input: invoiceId: the id of the invoice to retrieve
// Input: forceRemote: boolean to force the fetch from server
// Output: invoiceObservable: an observable filled with the invoice
if (forceRemote)
queryCacheInvoice = {};
var query = entityQuery.from('Invoices')
.where('id', '==', invoiceId)
.expand("Client, Client.Contacts, Lines")
.orderBy('Lines.Number');
var isInCache = queryCacheInvoice[invoiceId];
if (isInCache && !forceRemote) {
query = query.using(breeze.FetchStrategy.FromLocalCache);
} else {
queryCacheInvoice[invoiceId] = true;
query = query.using(breeze.FetchStrategy.FromServer);
}
return manager.executeQuery(query)
.then(querySucceeded)
.fail(queryFailed);
function querySucceeded(data) {
invoiceObservable(data.results[0]);
}
};
And here is the models for Invoice:
public class Invoice
{
[Key]
public int Id { get; set; }
public string Number { get; set; }
public DateTime? Date { get; set; }
public int? ClientId { get; set; }
public string Comment { get; set; }
public double? TotalExclVAT { get; set; }
public double? TotalInclVAT { get; set; }
public double? TotalVAT { get; set; }
public bool? WithoutVAT { get; set; }
public virtual List<InvoiceLine> Lines { get; set; }
public virtual Client Client { get; set; }
}
Please notice that for each invoice I have many invoice lines:
public virtual List<InvoiceLine> Lines { get; set; }
And here is the models for InvoiceLine:
public class InvoiceLine
{
[Key]
public int Id { get; set; }
[Required]
public int Number { get; set; }
[Required]
public string Description { get; set; }
public int InvoiceId { get; set; }
public Invoice Invoice { get; set; }
}
The problem: when I execute this breeze query I got the error below:
Error retreiving data. unable to locate property: Lines on type: Invoice
The problem is around the orderBy clause. I have a 1-to-many relationship between the Invoice and the InvoiceLine so it seems I cannot perform an order by in this case.
My question: how to proceed to be able to sort my lines of invoice by number?
Thanks.
Short answer: You can't. This is a limitation of Entity Framework, not Breeze.
You cannot filter, select, or order the related entities that you include with "expand" in an EF LINQ query.
You will probably manage the sort order of related entities on the client, e.g., the display of your order line items.
Note also that the collection of entities returned by a Breeze navigation path is unordered. I wasn't sure what happens if you tried to sort a Breeze entity navigation collection (e.g., Order.LineItems). I was afraid that would cause Breeze to think that you had made changes to the entities ... because a sort would seem to remove-and-add entities to the collection as it sorted. Your EntityManager would think there were changes pending when, in fact, nothing of substance has changed.
I tried an experiment and it all seems to work fine. I did something like this with Northwind:
fetched the Orders of a Customer ("Let's Stop N Shop")
checked the sequence of cust.Orders(); they have unordered OrderIDs: [10719, 10735, 10884, 10579]
executed a line like this: cust.Orders().sort(function(left, right){return left.OrderID() < right.OrderID()? -1 : 1})
checked the sequence of cust.Orders() again; this time they are sorted: [10579, 10719, 10735, 10884]
checked the customer's EntityManager.hasChanges() ... still false (no changes).
I confess that I am happily surprised. I need to write a proper test to ensure that this works reliably. And I have to make sure that the Knockout binding to the navigation property displays them in the sorted order. But I'm encouraged so far.
Important Notes:
Breeze won't keep the list sorted. You'll have to do that if you add new orders or if Breeze adds new orders to the collection as a result of subsequent queries.
Your sort affects every view that is bound to this navigation property. If you want each view to have its own sort of the entities in that collection, you'll have to maintain separate, view-specific collections that shadow the navigation property collection.
If I am wrong about all of this, you'll have to manage a shadow collection of the related entities, sorted as you wish, for each ViewModel.
Update 2 June
I suspected that we would have to let KO know about the array change after sort by calling valueHasMutated. I took 15 minutes for an experiment. And it seems to work fine.
I created a new KO Todo app from the ASP.NET SPA template (there's currently a phantom complaint about a missing jQuery.UI library which is totally unnecessary anyway).
I added a "sort link" to the index.cshtml just above the delete-TodoList link:
Sort
Then I implemented it in viewModel.js:
var sortAscending = true;
var viewmodel = {
...
sortList: sortList,
...
};
...
function sortList(list) {
list.todos().sort(function(left, right) {
return (sortAscending ? 1 : -1) *
(left.title().toLowerCase() < right.title().toLowerCase() ? -1 : 1);
});
sortAscending = !sortAscending; // reverse sort direction
list.todos.valueHasMutated(); // let KO know that we've sorted
}
Works like a charm. Sorting, adding, deleting Todos at will. The app is saving when expected as I add and delete ... but not during save.
Give valueHasMutated a try.
I have an MVC app using EF code first. I add a user to the system and enter pension details, part of this is a dropdown linked to a model called PensionBenefitLevel. This is the model -
[Key]
public int PensionBenefitLevelID { get; set; }
public string DisplayText { get; set; }
public int EmployeePercentage { get; set; }
public int EmployerPercentage { get; set; }
public virtual ICollection<Pension> Pension { get; set; }
When registered I have the PensionBenefitLevelID that came from the dropdown, but in my controller I was to peform a calculation using the EmployerPercentage value that is related to that ID. Can anyone point me in the correct direction?
Do I need to create a variable in the controller and use a linq query to get that value back? I've not been able to find any examples of something similar so if you could point me to one that would be great too.
If I understand the question correctly, you want to get back the entity corresponding to PensionBenefitLevelID and perform a calculation on the EmployerPercentage field.
Since you haven't mentioned what pattern you are using with EF (repository, unit of work, etc.) I can only give you a general answer:
var entity = [Your DB Context].[Your Entity].GetById(pensionBenefitLevelID);
if(entity != null)
{
[Calculation]
}
I've got this Venue object:
public class Venue
{
public int Id { get; set; }
[Required]
[MaxLength(512)]
public string Name { get; set; }
public string Description { get; set; }
[Required]
[Display(Name = "Venue Type")]
public int VenueTypeId { get; set; }
public virtual VenueType VenueType { get; set; }
[Required]
[Display(Name = "Company")]
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
public virtual ICollection<VenuePart> VenueParts { get; set; }
}
As you can see, it has a collection of VenueParts. I send the Venue to the view, and output the collection of VenueParts as a table of textboxes. This gets posted back to Edit(VenueDetailsViewModel venueDetailsViewModel). Using the debugger, I can verify that my change are in the VenueParts collection, so I think we're good on binding.
My controller tries to update the Venue. It succeeds on the properties directly on the object, such as Name. But, unless I loop through the collection, it does not update those objects. Is that typical behavior?
unitOfWork.VenueRepository.Update(venueDetailsViewModel.Venue);
// Should this loop be necessary?
foreach (var venuePart in venueDetailsViewModel.Venue.VenueParts)
{
unitOfWork.VenuePartRepository.Update(venuePart);
}
unitOfWork.Save();
At the moment, I'm not even worried about handling new stuff in the list or things that vanished from the list (although that is what I am tackling next). For my first step here, I just want to get the list updated. Is it necessary to loop through the collection and update each individual object? If I don't do this, they don't save. But it seems like they ought to without my loop. Are my expectations too high or am I doing something wrong?
My repository and unitOfWork objects are patterned after this tutorial if you are curious what that code looks like.
That is because unitOfWork.VenueRepository.Update(venueDetailsViewModel.Venue); will attach the object graph in Unchanged state and only change the venue as Modified. One alternative would be to move the foreach loop to the VenuePartRepository.Update method.
If you allow elements of VenueParts to be added or removed from the UI you will have a hard time applying the changes. If this is the case you will have to load the collection in the database and compare that with the changes coming in. Then manually change the states of VenuePart to Added or Deleted.
This is somewhat a two-part question (please let me know if they should be split up).
1) I have a model class with an array of objects contained inside it. I would like to be able to bind this automatically so I can accept a single pollModel argument in my controllers.
public class pollResponseModel
{
public long id { get; set; }
public long pollID { get; set; }
public string text { get; set; }
public long count { get; set; }
}
public class pollModel
{
public long id;
public long entID { get; set; }
public string question { get; set; }
public DateTime posted { get; set; }
public DateTime expiration { get; set; }
public pollResponseModel[] responses { get; set; }
}
The problem is that I'm not sure how to bind the responses field, seeing as it can be any arbitrary size. Well, I can bind it properly when displaying the edit view, but that's about it. That leads me to the second part of my question:
2) What's an acceptable way of dynamically creating and removing data in a list on the client, so that it can be bound to a model and accessed in its modified form on the server? I envision the creation/removal process working like the iPhone list GUI: a single + button will add a new element, and a - button on each row of data will remove it from the list. I would imagine jQuery is an appropriate starting point but my JS skills are very limited.
Check out this article by Phil Haack : Model Binding To a List. It explains exactly what you need to do to bind to list properties, or properties that are complex objects.
Essentially you just have to construct your POST data in the correct way for the model binder to parse it. The article explains how to add hidden index fields and represent your complex properties in your form.