Silverlight 5 datagrid is missing data - silverlight

I am trying to bind an xml data set to a Silverlight data grid. There are 21 rows in my xml The grid shows the heading ok and the 21 rows but the rows show no data. If I look at the ItemsSource property it shows the row date as being there.
I am having a problem posting all the code because of the edits that Stack Overflow does. Here is the key piece. If you need to see anything else, I will post it as comments.
grdData.ItemsSource = cloData.LoadData("Sample.xml")
public class clsData
{
public System.Collections.IEnumerable LoadData(string pName)
{
XDocument nutritionsDoc = XDocument.Load(pName);
List<Nutrition> data = (from nutrition in nutritionsDoc.Descendants("Nutrition")
select new Nutrition
{
Group = nutrition.Attribute("Group").Value,
Name = nutrition.Attribute("Name").Value,
Quantity = nutrition.Attribute("Quantity").Value
}).ToList();
return data;
}
}
What am I missing?

I just made the nutrition class a public class and it was magically solved.
Bob

Related

ObservableCollection does not update

I have ObservableCollection<Customer> on my window.
ObservableCollection<Customer> customers = new ObservableCollection<Customer>();
public ObservableCollection<Customer> Customers { get { return customers; } set { customers = value; OnPropertyChanged("Customers"); } }
This ObservableCollection has bound to ListView on the window. Once Use select on Customer from listView and click on edit a new window will appear with selected customer's data.
Second window's constructor
public EditCustomerWindow(Customer c)
{
InitializeComponent();
customerobj = c; //Original object
tempCustomerobj = new Customer(c); //Getting a copy of the customer object
CustomerDataGrid.DataContext = tempCustomerobj;
}
Once user clicks on Save Button customer object will get updated and window will closes.
But my issue is ObserverCollection does not get update on fist window even though I set new edited customer object before editing window get closed. Cannot find what is the wrong I am doing. Please advice me.
customerobj = tempCustomerobj;
You appear to be creating a new Customer object that is not in your ObservableCollection
tempCustomerobj = new Customer(c);
and then editing that new object.
CustomerDataGrid.DataContext = tempCustomerobj;
Doing that will not in any way affect the original Customer object that is still in your ObservableCollection.
To solve, don't create a new Customer, but rather edit an existing one.
Update
Based on your comments
The line
customerobj = c; //Original object
causes customerobj to be an alias to c, your object that is actually in the ObservableCollection.
The line
customerobj = tempCustomerobj;
causes customerobj to now be an alias to tempCustomerobj, which is your brand-new Customer object that is (I presume) a clone of c.
Change your constructor to
public EditCustomerWindow(Customer c)
{
InitializeComponent();
CustomerDataGrid.DataContext = c;
}
Update 2
The object you're editing should support IEditableObject. See
https://stackoverflow.com/a/1091240/141172
Alternatively, you can serialize the object before you start editing and deserialize the saved state if the edit is canceled.

Updating of BindingSource in WinForms

During my attempts to use DataBinding in Winforms I've encountered with a problem. It looks like after updating of DataSource DataGridView doesn't refresh the data. Can't understand where is a problem.
var companies = new List<Company> { new Company { Name = "Test", Id = 100 }}
And here is the code for binding of items list to DataGridView:
bindingSource1.DataSource = _context.Companies;
dataGridView1.DataSource = bindingSource1.DataSource;
But after that if I update companies list like this
companies.Add(new Company { Name = "MDG", Id = 500 });
I can't find the newly added item into the DataGridView. Could someone help me to understand what I'm missing?
The problem here is that there is no way the BindingSource and the DataGridView can be made aware of changes to a List automatically.
Instead, use a new BindingList(). This has events that will be called to notify the BindingSource, and in turn the DataGridView that a new item to the list has been added.
var companies = new BindingList<Company>();
companies.Add(new Company { Name = "Test", Id = 100 });

Reusing Binding Collections for WPF

I am working on a WPF app using the MVVM patterm, which I am learning. It uses EF4. I am trying to use a similar tabbed document interface style; several combo boxes on these tabs have the same items sources (from a sql db). Since this data almost never changes, it seemed like a good idea to make a repository object to get them when the app starts, and just reuse them for each viewmodel. For whatever reason though, even though I use new in the constructors, the lists are connected.
If I set a bound combo box on one tab, it gets set on another (or set when a new tab is created). I don't want this to happen, but I don't know why does.
The repository object is initialized before anything else, and just holds public lists. The views simply use items source binding onto the ObservableCollection. I am using the ViewModelBase class from the article. Here is the Viewmodel and model code.
ViewModel
TicketModel _ticket;
public TicketViewModel(TableRepository repository)
{
_ticket = new TicketModel(repository);
}
public ObservableCollection<Customer> CustomerList
{
get { return _ticket.CustomerList; }
set
{
if (value == _ticket.CustomerList)
return;
_ticket.CustomerList = value;
//base.OnPropertyChanged("CustomerList");
}
}
Model
public ObservableCollection<Customer> CustomerList { get; set; }
public TicketModel(TableRepository repository)
{
CustomerList = new ObservableCollection<Customer>(repository.Customers);
}
EDIT: I am sure this is the wrong way to do this, I am still working on it. Here is the new model code:
public TicketModel(TableRepository repository)
{
CustomerList = new ObservableCollection<Customer>((from x in repository.Customers
select
new Customer
{
CM_CUSTOMER_ID = x.CM_CUSTOMER_ID,
CM_FULL_NAME = x.CM_FULL_NAME,
CM_COMPANY_ID = x.CM_COMPANY_ID
}).ToList());
}
This causes a new problem. Whenever you change tabs, the selection on the combo box is cleared.
MORE EDITS: This question I ran into when uses Rachels answer indicates that a static repository is bad practice because it leaves the DB connection open for the life of the program. I confirmed a connection remains open, but it looks like one remains open for non-static classes too. Here is the repository code:
using (BT8_Entity db = new BT8_Entity())
{
_companies = (from x in db.Companies where x.CO_INACTIVE == 0 select x).ToList();
_customers = (from x in db.Customers where x.CM_INACTIVE == 0 orderby x.CM_FULL_NAME select x).ToList();
_locations = (from x in db.Locations where x.LC_INACTIVE == 0 select x).ToList();
_departments = (from x in db.Departments where x.DP_INACTIVE == 0 select x).ToList();
_users = (from x in db.Users where x.US_INACTIVE == 0 select x).ToList();
}
_companies.Add(new Company { CO_COMPANY_ID = 0, CO_COMPANY_NAME = "" });
_companies.OrderBy(x => x.CO_COMPANY_NAME);
_departments.Add(new Department { DP_DEPARTMENT_ID = 0, DP_DEPARTMENT_NAME = "" });
_locations.Add(new Location { LC_LOCATION_ID = 0, LC_LOCATION_NAME = "" });
However, now I am back to the ugly code above which does not seem a good solution to copying the collection, as the Customer object needs to be manually recreated property by property in any code that needs its. It seems like this should be a very common thing to do, re-using lists, I feel like it should have a solution.
Custom objects, such as Customer get passed around by reference, not value. So even though you're creating a new ObservableCollection, it is still filled with the Customer objects that exist in your Repository. To make a truly new collection you'll need to create a new copy of each Customer object for your collection.
If you are creating multiple copies of the CustomerList because you want to filter the collection depending on your needs, you can use a CollectionViewSource instead of an ObservableCollection. This allows you to return a filtered view of a collection instead of the full collection itself.
EDIT
If not, have you considered using a static list for your ComboBox items, and just storing the SelectedItem in your model?
For example,
<ComboBox ItemsSource="{Binding Source={x:Static local:Lists.CustomerList}}"
SelectedItem="{Binding Customer}" />
This would fill the ComboBox with the ObservableCollection<Customer> CustomerList property that is found on the Static class Lists, and would bind the SelectedItem to the Model.Customer property
If the SelectedItem does not directly reference an item in the ComboBox's ItemsSource, you need to overwrite the Equals() of the item class to make the two values equal the same if their values are the same. Otherwise, it will compare the hash code of the two objects and decide that the two objects are not equal, even if the data they contain are the same. As an alternative, you can also bind SelectedValue and SelectedValuePath properties on the ComboBox instead of SelectedItem.

Can't add new item to Silverlight DataForm when ICollectionView is sorted or filtered

I've got a DataForm on a Silverlight 4 page. I bind it to the View on the class below. I am able to add, delete, edit, and move forward/back through records just fine using the controls built into the DataForm. But as soon as I remove the comment for the Filter or SortDescription, then every time I press the Add + button I get the dreaded "cannot change currency when an item has validation errors or it is being edited and AutoCommit is false" error. I've been stuck on this for hours and don't have a clue.
public class TestData {
OperationsDataContext context;
ICollectionView view;
public ICollectionView View { get { return view; } }
public IEditableCollectionView EditableView { get { return ((IEditableCollectionView)view); } }
public TestData() {
context = new OperationsDataContext();
context.Locations.Add(new Location { LocationId = 1, LocationName = "Home", CreatorUserId = 1 });
context.Locations.Add(new Location { LocationId = 2, LocationName = "Work", CreatorUserId = 1 });
context.Locations.Add(new Location { LocationId = 3, LocationName = "Office", CreatorUserId = 1 });
view = ((ICollectionViewFactory)context.Locations).CreateView();
// View.Filter = (o) => true;
// View.SortDescriptions.Add(new SortDescription("LocationName", ListSortDirection.Ascending));
}
}
I have attempted to add data manually using code - not the DataForm - and it works just fine even when both a filter and sort are specified.
TestData testData = new TestData();
Location item = testData.EditableView.AddNew() as Location;
testData.EditableView.CommitNew();
Why would it work from code but not via the DataForm? And why does the DataForm work when no filter is specified, but fail when a no-op filter that always returns true is specified?
may be you have similar issue as http://forums.silverlight.net/p/111217/250982.aspx post
Okay, so I just ran into the exact same issue. In my case, I was using a DomainCollectionView that was bound to both, a DataGrid and a DataForm. Apparently, this can cause issues because both controls want to manage the currency.
The solution was to not bind the DataForm directly to the DomainCollectionView, but instead to bind it to the DomainCollectionView.SourceCollection property.
The downside to this is that you have to bind DataGrid.SelectedItem and DataForm.CurrentItem to keep them both in sync. I have not found any other issues with this approach, but it definitely solved the error when trying to add a new item after sorting in the DataGrid.
See the comment from Jeff Handley about this issue: http://jeffhandley.com/archive/2011/08/02/ToolkitAugust2011.aspx

Entity Framework 4 Binding a related field to a DataGridView column

I have a DataGridView that is bound - via a binding source - to a list of entities:
VehicleRepository:
private IObjectSet<Vehicles> _objectSet;
public VehicleRepository(VPEntities context)
{
_context = context;
_objectSet = context.Vehicles;
}
List<Vehicle> IVehicleRepository.GetVehicles(Model model)
{
return _objectSet
.Where(e => e.ModelId == model.ModelId)
.ToList();
}
In my presenter
private List<Vehicle> _vehicles;
...
_vehicles = _vehicleRepository.GetVehicles(_model);
_screen.BindTo(_vehicles);
in my view
public void BindTo(List<Vehicle> vehicles)
{
_vehicles = vehicles;
if (_vehicles != null)
{
VehicleBindingSource.DataSource = _vehicles;
}
}
This works fine - my grid displays the data as it should. However, in the grid I am wanting to replace the ModelId column with a description field from the Model table. I've tried changing the binding for the column from ModelId to Model.ModelDescription but the column just appears blank.
I'm pretty sure that the data is being loaded, as I can see it when I debug, and when the same list is passed to a details screen I can successfully bind the related data to text fields and see the data.
Am I doing something obviously wrong?
It's a bit manual, but it 'works on my machine'.
Add a column to your DataGridView for the description field and then after you set your DataSource iterate through like so.
Dim row As Integer = 0
foreach (entity In (List<Entity>)MyBindingSource.DataSource)
{
string description = entity.Description;
MyDataGridView.Rows[row].Cells["MyDescriptionCell"].Value = description;
row ++;
}
You get a readonly view of your lookup. I make the new column readonly, but you could write something to handle the user changing the field if you wanted updates to run back to the server. Might be messy though.
The answer involves adding unbound read only columns and setting their value in the DataGridView's DataBindingComplete event
as described here
You can just add a column to your DataGridView, and in the DataPropertyName you must set the [entity].[Field name you need] in your case you could do: VehiclesType.Description
then you must add another binding source for the VehiclesTypes to the form, fill it using your context, and your good to go ;)

Resources