I have two classes:
public class Equipment
{
public int EquipmentId { get; set; }
public string Name { get; set; }
public Nullable<int> EquipmentTypeId { get; set; }
}
public class EquipmentType
{
public int EquipmentTypeId { get; set; }
public string TypeName { get; set; }
}
I'm returning list of Equipment from database as a List<Equipment>.
Then I bind it to the Gridview through Gridview1.DataSource = equipmentList
That works fine, but what I need to do is I need to display EquipmentType Name instead of ID. And, in edit mode I need to display Dropdown list with all available EQuipmentTypeNames. Any suggestion on how to do that? what is the architecture?
For displaying in the GridView you have a couple of options, ranging from easiest to hardest.
Use a nested SqlDataSource for equipment type, and display in a disabled dropdownlist.
Load the equipment type with the equipment for data binding.
Programmatically load equipment type in the RowDataBound event.
Here is an example of how you could include the equipment type data:
var equips = GetEquipment();
var equipTypes = GetEquipmentTypes();
var data = from eq in equips
join et in equipTypes on eq.EquipmentTypeId equals et.EquipmentTypeId
select new { eq.EquipmentId, eq.Name, et.TypeName };
GridView.DataSource = data;
For editing in the GridView, it is simplest just to use a DropDownList with it's own SqlDataSource (or custom data source), which is bound to Equipment.EquipmentTypeId, something like this:
<EditItemTemplate>
<asp:DropDownList ID="EquipmentTypeDropDown" runat="server"
SelectedValue='<%#Bind("EquipmentTypeId") %>'
DataSource='<%#GetEquipmentTypes() %>'
DataTextField="TypeName" DataValueField="EquipmentTypeId">
</asp:DropDownList>
</EditItemTemplate>
Where GetEquipmentTypes() is just a method that returns List<EquipmentType> that I'm using for databinding. In this case, since I don't have a database, I'm just generating data in a stub object. You would replace this method with your BAL class that reads the data from the database (or ADO.Net code, or an ObjectDataSource, etc.).
protected List<EquipmentType> GetEquipmentTypes()
{
return new List<EquipmentType> {
new EquipmentType { EquipmentTypeId = 1, TypeName = "Baseball" },
new EquipmentType { EquipmentTypeId = 2, TypeName = "Football" },
new EquipmentType { EquipmentTypeId = 3, TypeName = "Soccer" },
};
}
I'd recommend extending your Equipment class out with a child class (let's call it EquipmentEx for now), and add on the EquipmentTypeName property. Instead of your stored proc returning just the Equipment columns, join in the EquipmentType table, and include the additional type name column.
Or create a view that does the join, and have your stored proc return the view contents instead of the table contents - it depends on how your app is generally organized.
Once you have an EquipmentEx object, then you've got your answer for displaying the type name instead of ID - it's already part of the object, so you can display it just like normal.
As far as putting the dropdown into the GridView, you'll probably be working with the EditItemTemplate tag inside your template field, but I really haven't worked enough with the GridView to know exactly what it will look like. I've always preferred to handle this kind of thing a little more manually, rather than trusting ASP.NET to do it for me. But since your dropdownlist's value will represent the EquipmentTypeID, you should be able to bind this value to the object when you save it, instead of a TextBox value.
First of all , if you want to bind it like that you need to make your own class that contains all informations , like that:
public class CompositeEquipment
{
public int EquipmentId { get; set; }
public string Name { get; set; }
public string EquipmentTypeName { get; set; }
}
And if you are using sql statements or stored procedures you can write the following :
SELECT Equipment.EquipmentID,Equipment.Name,EquipmentType.TypeName FROM Equipment
INNER JOIN EquipmentType ON Equipment.EquipmentTypeId = EquipmentType.EquipmentTypeId
and when reading SqlDataReader do the following :
List<Equipment> lstEquipment = new List<Equipment>();
while(reader.read())
{
Equipment eq = new Equipment();
eq.reader["EquipmentId"];
eq.reader["Name"];
eq.reader["TypeName "];
lstEquipment.Add(eq);
}
Gridview1.DataSource = lstEquipment;
and if you are using EntityFramework as Data Access Layer you can write the following :
using(YourOwnContext context = new YourOwnContext)
{
List<Equipment> lstEquipment = (from e in context.Equipments
select new Equipment
{
EquipmentId = e.EquipmentId,
Name = e.Name,
TypeName = e.EquipmentType.TypeName
}}).ToList();
Gridview1.DataSource = lstEquipment;
Hope this helps you.
Related
I am using an api for a shopping cart that has some complex json (very complicated to me) data structured like in my screenshot below. In this scenario in my code I am trying to fix an error which I am going to explain by illustrating the data and how its structured as I am very new to JSON and arrays.
This is from the Visual Studio json reader of the data that belongs to an order placed by a customer. This item at the index of [0] has a customFields which has a value.
When a customer completes a purchase, some items they bought can have custom fields, like the size of a shirt (Large) or (Medium) or (Small) etc... In the JSON these customFields have a value which in this case is the size of the shirt for me to display at the thank you page so the customer knows what size he bought. Essentially I am trying to have the data ready to pass to the thank you page view.
When I am calling for these items in my controller, the code only works if ALL the items that were purchased have a customFields. If the customer buys something like a coffee mug that has NO custom fields, then the application breaks because I guess my code is only accounting for items that actually have customFields.
This is the code that I have so far that only works when ALL items that were purchased have a custom field. This is inside my controller.
public ActionResult Thankyou(string token)
{
int itemsCountAddedToCart = (int)obj["items"].Count();
var items = obj["items"].Select(o =>
new Item
{
name = o["name"].ToString(),
quantity = int.Parse(o["quantity"].ToString()),
price = double.Parse(o["price"].ToString()),
image = o["image"].ToString(),
url = o["url"].ToString(),
//This customFields is what works, but only if all items had custom fields.
customFields = o["customFields"][0]["value"].ToString(),
});
thankYouViewModel.OrderItems = items;
}
//ThankYou View Model that loads hold the data to be able to show in the view.
public class ThankYouViewModel
{
public IEnumerable<Item> OrderItems { get; set; }
}
public class Item
{
public string name { get; set; }
public double price { get; set; }
public int quantity { get; set; }
public string image { get; set; }
public string url { get; set; }
//customFields
public string customFields { get; set; }
}
So that code above works, but breaks when I have items that do not have customFields. This is the error that I get:
System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index'
So how should my code look where its currently breaking so that it can account for situations where one of the items from the JSON does not have a customFields attribute? I am very stuck and have tried to add some conditional statements but did not work because I am dealing with some complex json I do not understand very well yet.
If you want to forget the possibility of more than one element in the customFields array, and only cast the first element value to a string, then use this:
customFields = (o["customFields"] == null || o["customFields"].Count() == 0)?null:o["customFields"][0]["value"].ToString(),
With customFields = o["customFields"][0]["value"].ToString(), you directly receive the value from the customFields Array. If there is no Array in your case then there is nothing to get.
I would recommend you to check if your customFields exists:
var item = new Item ();
item.name = o["name"].ToString();
item.quantity = int.Parse(o["quantity"].ToString());
item.price = double.Parse(o["price"].ToString());
item.image = o["image"].ToString();
item.url = o["image"].ToString();
if(o["customFields"] != null)
{
item.customFields = o["customFields"][0]["value"].ToString();
}
I have a WinForms application with only one DevExpress GridControl inside.
This GridControl uses two GridViews and one relationship in Master-detail mode.
As dataSource for gridControl I am using following class:
public class DashboardParameter
{
public string Name { get; set; }
public int DataType { get; set; }
public int ValueType { get; set; }
public BindingList<DashboardParameterValue> Detail { get; set; }
public DashboardParameter()
{
Detail = new BindingList<DashboardParameterValue>();
}
}
public class DashboardParameterValue
{
public string Value { get; set; }
}
Here is the code for data loading:
private void MasterDetail_Load(object sender, EventArgs e)
{
data = new BindingList<DashboardParameter>();
var p1 = new DashboardParameter() { Name = "First", DataType = 1, ValueType = 1};
p1.Detail.Add(new DashboardParameterValue() { Value = "Value1" });
p1.Detail.Add(new DashboardParameterValue() { Value = "Value2" });
var p2 = new DashboardParameter() { Name = "Second", DataType = 1, ValueType = 1 };
p2.Detail.Add(new DashboardParameterValue() { Value = "Value3" });
p2.Detail.Add(new DashboardParameterValue() { Value = "Value4" });
data.Add(p1);
data.Add(p2);
gridControl.DataSource = data;
}
As I understand it, in this way my gridControl automatically finds master-detail relation and creating Columns for each field in class for DataSource (If AutoPopulateColumns property is true).
Trouble: I can not change ANYTHING in my detailView Columns. I dont know in wich time my dataView columns are being created. All of detailView properties are ignored.
For example, if i changing detailView.AutoPopulateColumn = false, Columns still are being created.
If I create custom GridColumn gridColumn1 and add there detailView.Columns.Add(gridColumn1), it will be ignored.
Only one thing i can do is using [DisplayAttribute] for changing DisplayName, Visible and so on.
Question: How must I change my code to be able to change my detailView.
For example, can I add Column in detailView after all auto-generated columns, or change Column type to ComboBox (using RepositoryItemComboBox).
You can customize a detail view in the GridView.MasterRowExpanded event handler. This event is raised when a master row is expanded and a corresponding detail view is created and configured. To access this view, use the GridView.GetDetailView method. Alternatively, you can handle the GridControl.ViewRegistered event.
One more solution is to create a pattern detail view and customize it at runtime or design time.
I suggest you to go through documentation for Working with Master-Detail Relationships in Code and Master-Detail Relationships.
You can create your views either based on your database structure or pragmatically by adding views and columns with their settings.
You can customize a detail view in the GridView.MasterRowExpanded event handler. Fires immediately after a particular detail clone has become visible. The MasterRowExpanded event fires when expanding a master row or when switching between details.
Example:
//Assign a CardView to the relationship
CardView cardView1 = new CardView(gridControl1);
gridControl1.LevelTree.Nodes.Add("CategoriesProducts", cardView1);
//Specify text to be displayed within detail tabs.
cardView1.ViewCaption = "Category Products";
//Hide the CategoryID column for the master View
gridView1.Columns["CategoryID"].VisibleIndex = -1;
//Present data in the Picture column as Images
RepositoryItemPictureEdit riPictureEdit = gridControl1.RepositoryItems.Add("PictureEdit") as RepositoryItemPictureEdit;
gridView1.Columns["Picture"].ColumnEdit = riPictureEdit;
//Stretch images within cells.
In the parent there is a Observable Collection PendingPayment that has a list of all pending payments of sales with a column amount paid.
Then the user can select a particular sale and open it in new child window.
The thing thats going wrong is if the user just edits the text box paid amount in child window and closes the window without saving the new paid amount to database,the observable collection containing Amount paid column in the parent window gets updated.
What I want is it the collection to get updated only when the values are updated in the database.
This can be achieved by creating a copy of your sale object when the user select it in the list, and then using this copy as the view model of your child view.
You will then be able to set the new values in the original object from your list only once the save button has been clicked and the database update succeed.
An other way to proceed if you need to edit only few of the object properties would be to create and editor object and use it as the child window's view model.
Something like this :
public class Sale
{
public int PaidAmount { get; set; }
public int Some { get; set; }
public int More { get; set; }
public int Properties { get; set; }
}
public class SaleEditor
{
private Sale _sale;
public int PaidAmount { get; set; }
public SaleEditor(Sale sale)
{
_sale = sale;
PaidAmount = sale.PaidAmount;
}
public void Save()
{
// update your data here
_sale.PaidAmount = PaidAmount;
}
}
If you need your original object to update the database, then the save method could first update the object and the revert the changes if DB update failed :
public void Save()
{
var oldAmount = _sale.PaidAmount;
_sale.PaidAmount = PaidAmount;
if (!SalesDB.Update(_sale))
_sale.PaidAmount = oldAmount;
// you could also read back the value from DB
}
Whenever possible (I've never see a reason why it cannot),for listing purpose use proxy or flatted objects, you can implement this using projections query. Then user select an item from a list and the only thing you need to grab is a key to load the full object with its required object graph as the use case might dictate.
Here is a sample implementation using Entity Framework and c# lambda expressions:
Using anonymous object:
var anonymousListProjection = DbContext.PendingPayments.Select( pp=>
new { pp.Order, pp.Amount})
Using a hardcoded proxy:
var hardcodedListProjection = DbContext.PendingPayments.Select( pp=>
new PendingPaymentProxy { Order = pp.Order, Amount = pp.Amount})
//To return an observable:
var observableColl = new ObservableCollection<PendingPaymentProxy>
(hardcodedListProjection.Tolist());
public class PendingPaymentProxy
{
public string Order { get; set; }
public decimal Amount{ get; set; }
}
Apart from avoiding possibles performance problems due to unintentional loading real objects, this way you only have to worry for your list when the user do save in the detail view.
I want to bind the data in to list, the data can be parsed through xml and stored in local variables.
Next I want to bind these data in to list and stored in isolated storage. Please help me out.
if (e.Result != null)
{
string serviceDate, name, taskname;
XDocument doc;
doc = XDocument.Parse(e.Result);
foreach (var absentDate in doc.Element("Classworks").Descendants("Classwork"))
{
serviceDate = absentDate.Element("ClassworkDate").Value.ToString();
name = absentDate.Element("Subject").Value.ToString();
taskname = absentDate.Element("Task").Value.ToString();
}
}
else
{
}
These are the steps you need to perform approximately :
Create a model to represent single data in List, say it MyModel. If MyModel property values is not static (can be changed at runtime), you may also need to implement INotifyPropertyChanged in the model.
Create a property of type ObservableCollectoin<MyModel>, say it MyItemsSource
Put a LongListSelector or similar user control that capable of displaying multiple data on the Page. Bind the control's ItemsSource to MyItemsSource property
Set the Page's DataContext properly.
Populate MyItemsSource in code, you can do this in foreach part shown in question.
Later, when you need to save the data to IsolatedStorage, the data is available in MyItesSource property. Just save it.
Step 1
public class MyModel
{
public string ClassworkDate { get; set; }
public string Subject { get; set; }
public string Task { get; set; }
}
Step 2
public ObservableCollection<MyModel> MyItemsSource { get; set; }
Step 4
public MainPage()
{
InitializeComponent();
MyItemsSource = new ObservableCollection<MyModel>();
this.DataContext = this;
}
Step 5
foreach (var absentDate in doc.Element("Classworks").Descendants("Classwork"))
{
serviceDate = absentDate.Element("ClassworkDate").Value.ToString();
name = absentDate.Element("Subject").Value.ToString();
taskname = absentDate.Element("Task").Value.ToString();
var newModel = new MyModel{
ClassworkDate = serviceDate,
Subject = name,
Task = taskname
};
MyItemsSource.Add(newModel);
}
Above are codes related to logic in steps I explained before. With those codes setup, your data is ready to be bound. Note that in this example I didn't implement INotifyPropertyChanged for brevity.
I have a simple database with two tables: Photo and Tag. There is a one-to-many (a photo can have many tags) relationship between the two tables. Here is a diagram:
Now I have made a Photo class and set it up for LINQ-to-SQL using attributes. The code for that class is below:
[Table]
public class Photo
{
[Column(IsDbGenerated = true, IsPrimaryKey = true, CanBeNull = false)]
public int ID { get; set; }
[Column(CanBeNull = false)]
public string Filename { get; set; }
[Column(CanBeNull = false)]
public string Description { get; set; }
[Column(CanBeNull = false)]
public DateTime DateTaken { get; set; }
public List<string> Tags { get; set; }
public override string ToString()
{
string result = String.Format("File: {0}, Desc: {1}, Date: {2}, Tags: ",
Filename, Description, DateTaken);
if (Tags != null)
foreach (string tag in Tags)
result += tag + ", ";
return result;
}
}
You will notice that currently I do not have any attributes for the Tags list. I would like to be able to setup the attributes (associations) for the Tags list so that it would be populated with Name field of the Tag table for all entries in the Tag table of a particular PhotoID. It would be preferable if I could do this directly (i.e. without having to setup a Tag class mimicking/relating to the Tag table). Since I'm only interested in one field (the Name in the Tag table) rather than many fields, I would think there is a way to do this.
Is this possible, and if so how would I further decorate the class with attributes and what would be the syntax for a simiple Select query using LINQ-to-SQL?
If it helps, here is the code I am using to simply add a new photo and then grab all of the photos out of the database (obviously the tag information is not pulled out as the code stands now).
DataContext context = new DataContext(connectionString);
// add new photo
Photo newPhoto = new Photo { Filename = "MyImage1.jpg", Description = "Me", DateTaken = DateTime.Now };
context.GetTable<Photo>().InsertOnSubmit(newPhoto);
context.SubmitChanges();
// print out all photos
var photoQuery = from m in context.GetTable<Photo>() select m;
foreach (Photo myPhoto in photoQuery)
textBox1.Text += Environment.NewLine + myPhoto.ToString();
First I'd suggest you to use a tool to generate your entity classes (the classes that correspond to the database tables). We'r using sqlmetal and it does the job very well.
Next, (if you have a Tag entity) than write a function that fetches the tags for some photos:
void GetTags(IEnumerable<Photo> photos)
{
var ids = photos.Select(p=>p.ID).ToList();
var tagsG = (from tag in context.GetTable<Tag>() where ids.Contains(tag.PhotoID) select new {PhotoID, Name}).GroupBy(tag=>tag.PhotoID);
foreach(ph in photos){
ph.Tags = tagsG[ph.ID].Select(tag=>tag.Name).ToList();
}
}
Note, the code might not compile I've written it in the browser...
You should refer to the Attribute Based Mapping article on msdn.
Also, this article shows how to decorate an EntitySet property with an Association attribute to accomplish the relationship modeling.
It would be preferable if I could do this directly (i.e. without having to setup a Tag class mimicking/relating to the Tag table).
Not possible. LinqToSql needs to know what is or isn't a table, so it can generate the proper sql text.
What you can do instead, is make one set of types for representing database structure, and another set of types for use elsewhere in your program. The second set of types could have a single class representing Photo and Tag data.
Write a query with the first set of types, then use them to construct instances of the second set.