I am working on project where I need help and direction on displaying master child rows with object as datasource
I have following issues, please kindly help me
1) How to Assign the objectdatasource to data grid so that user can click master row to see child rows
2) Once master child data shown in grid, user can edit any column, if user clicks any column I have to re calculate the values. How to do that?
For example: if user is editing product price then I have to calculate the net price. I am not sure how to implement this functionality. Where I have to put my calculation logic and how to call my calculation logic from Grid?
Please let me know what the best way of doing above tasks. i have done some research but unable to find out clear example on implementation
Thanks,
If you don't already, you need to make sure your class is a composite. In other words:
class Child
{
int ChildId { get; set; }
}
class Parent
{
int ParentId { get; set; }
List<Child> Children { get; set; }
}
From here, Dev Express will do all of the heavy lifting for you.
Let's say you have two grids, gridMaster and gridChild. Create Two binding sources, bindMaster and bindChild.
In the designer, make the DataSource for the bindMaster an object of type Parent. Make the DataSource for bindChild bindMaster (not the object Master) and set the DataMember property to "Child."
Now, when you refresh the data for the master and select rows, the child grid will automatically populate to whatever master record's children are -- without any hand-written code!
Here is an example from another similar question:
Devexpress master-detail in 2 gridcontrols
As far as the product price / net price, you have two options:
Make an unbound column in the grid
(my preference), add the property directly to the object
Something like this would work:
public double ProductPrice { get; set; }
public double NetPrice
{
get { return ProductPrice * Quantity; }
}
You might have to fire a RefreshData() on the Grid View if the data in the grid changes, but I'd test it first to see if it's necessary.
Related
I am working on a c# windows forms project where i have to open all the cells of datagridview in edit mode as soon as it gets focus . But i am unake to find out how to do it . I am new to c# .
MVVM
Programmers who are new to programming a DataGridView tend to fiddle with the cells directly when reading and writing values. Although this can work, it is not efficient: your code will be hard to read, hard to debug, hard to change, and hard to debug.
In modern programming, there is a tendency to separate your data (= model), from the way that this data is presented to the operator (= view). An adapter class, often called ViewModel, is used to connect the model to the view. Toggether these three classes are abbreviated MVVM. Consider to read some background information about this.
One of the nice things about separating your model from your view, is that you can change your view without having to change your model: if you want to show your data in a Graph instead of a Table, your data won't have to change. Or if you want to add extra columns to your data, but you don't need to display them, your View won't have to change. Similarly: if you plan to fetch the data from a file, instead of a database: no changes in the view.
Other advantages of separating your model from your view, is that you can reuse the model in forms that show other aspects of your data, you can unit test the model without using a form, during development you can mock the model, to show your forms without real data.
MVVM and DataGridView
Alas, you didn't specify what you want to show in your DataGridView, so let's assume you want to show a table of Products:
Model
class Product
{
public int Id {get; set;}
public string Name {get; set;}
public string Description {get; set;}
public decimal Price {get; set;}
public int StockCount {get; set;}
public int LocationId {get; set;} // foreign key to location
...
}
Of course your Model needs a procedure to fetch the Products that must be shown:
class MyModel
{
IEnumerable<Product> FetchProducts()
{
// TODO implement; out of scope of the question
}
... // other Model methods
}
So in the Model, you won't have methods that have anything to do with how the data is displayed.
ViewModel
Suppose, on this specific form (this view) you don't want to show all Products, you only want to show products that are out-of-stock. This is a typical adapter method to couple your Model to your View
class MyViewModel
{
private MyModel Model => ...
IEnumerable<Product> FetchOutOfStockProducts()
{
return this.Model.FetchProducts()
.Where(product => product.StockCount == 0);
}
}
If in future you don't want to show the Products that are completely out-of-stock, but you also want to show Products that are almost out-of-stock, you will only have to change this procedure (and maybe it's name, for better readability).
If you really want to separate the Model from the View, consider to create a class DisplayedProduct, which internally holds a Product. This gives you the opportunity to add or hide properties of Product. In future you can change the Product, without having to change the 20 forms that use the Product. You can create DisplayedProducts that internally use something else than a product. Or if you want to add a property:
public decimal TotalStockValue => this.Product.StockCount * this.Product.Price;
Whether you need to create a DisplayedProduct class depends on how much the displayed data differs from your model, how often you think that the Product will change, how often the Product will be displayed on different forms and how often you expect the View to change.
The View
This is the interesting part. Using Visual Studio designer you have added a DataGridView and columns. You need to tell the DataGridView which column should show which value of the Product.
You can also do this using Visual Studio Designer. If you want to make sure that the compiler will complain if you made a typing error, do something like this in the constructor:
public MyForm()
{
InitializeComponent();
// define which columns should show which properties of Product:
this.columnId.DataPropertyName = nameof(Product.Id);
this.columnName.DataPropertyName = nameof(Product.Name);
this.columnPrice.DataPropertyName = nameof(Product.Price);
... // etc.
Of course you don't need columns for values that you won't show anyway. Another advantage of seperation of model and view: if in future a Property is added to the column that you don't need to show, your code will still work.
Your form also has a property that holds the ViewModel:
private MyViewModel ViewModel => ...
You can fill this in the constructor, or create a new one every time you need it. Which method you'll need depends on how much work it is to create a ViewModel.
To define which Products are shown:
private BindingList<Product> DisplayedProducts
{
get => (BindingList<Product>)this.dataGridViewProduct.DataSource;
set => this.dataGridViewProduct.DataSource = value;
}
Of course your form needs a procedure to display the initial set of Products:
private IEnumerable<Product> FetchInitialProductsToShow()
{
return this.ViewModel.FetchOutOfStockProducts();
}
Now all you have to do to show the Products when your Form is loaded:
private void OnFormLoading(object sender, ...)
{
this.DisplayedProducts = new BindingList<Product>(
this.FetchInitialProductsToShow().ToList());
}
If you allowed it when creating the datagridview, the operator can Add / Remove / Change products. When he is finished making the changes, he can inform you by clicking an Ok, or Apply Now button:
private void OnButtonOk_Clicked(object sender, ...)
{
ICollection<Product> editedProducts = this.DisplayedProducts;
// find out which Products are added / removed / changed
this.ProcessEditedProducts(editedProducts);
}
BTW, did you notice, that because I separated the Model from the View, that most procedures in your form are "one-liners"?
Two nice methods: get the current product, and if you allow multiple selection: get all selected products:
private Product CurrentProduct =>
(Product) this.DataGridViewProducts.CurrentRow?.DataBoundItem;
private IEnumerable<Product> SelectedProducts =>
this.DataGridViewProducts.SelectedRows
.Cast<DataGridViewrow>()
.Select(row => row.DataBoundItem)
.Cast<Product>();
By default all cells are already editable. You will have to click twice one for selecting cell and then to edit it or press F2 after selecting cell.
You will have to make sure of following things
The DataGridView control is enabled.
The EditMode property value is not EditProgrammatically.
The ReadOnly properties of the cell, row, column, and control are all set to false.
Here's the excerpt from https://learn.microsoft.com/en-us/archive/blogs/vinsibal/5-random-gotchas-with-the-wpf-datagrid:
5.Data source items should implement IEditableObject to get transactional editing functionality.
If you are not familiar with IEditableObject, see this MSDN article
which has a good explanation and code sample. The DataGrid has baked
in functionality for transactional editing via the IEditableObject
interface. When you begin editing a cell, the DataGrid gets into cell
editing mode as well as row editing mode. What this means is that you
can cancel/commit cells as well as cancel/commit rows. For example, I
edit cell 0 and press tab to the next cell. Cell 0 is committed when
pressing tab. I start typing in cell 1 and realize I want to cancel
the operation. I press ‘Esc’ which reverts cell 1. I now realize I
want to cancel the whole operation so I press ‘Esc’ again and now cell
0 is reverted back to its original value.
I get this same transactional behavior even without implementing IEditableObject. What am I missing?
public class Person {
public string FirstName { get; set; }
public string LastName { get; set; }
public string Description { get; set; }
}
People = new List<Person> {
new Person(){FirstName = "fname", LastName = "lname", Description = "description"}
};
<DataGrid ItemsSource="{Binding Path=People}" />
Of course it doesn't matter that much for a simple string in memory. But there are uses for IEditableObject.
For example, imagine you want to store every edited record to a database. You probably would want to commit all changes in one transaction, which would be in EndEdit(). Similar uses can be found for the other interface methods.
Also, you are not always directly editing the bound object. Your IEditableObject instance might hold a variable of another type wich is the actual data source. You would not apply the edited values until EndEdit() was called in that case, and CancelEdit() would restore the values from the original data source.
I have a DataGrid with a combobox inside a template column. Elsewhere on this screen, the user makes a 'customer' selection from a separate control altogether. In order to populate the comboboxes in my datagrid, I need to pass in that selected customer as a parameter, in addition to other information from each row in the grid.
Basically... the grid contains part information, and the combobox items are based on a combination of the following: selected customer, part number, and manufacturer. Each row's combobox can potentially have a different source list. Is there a way I can bind the ItemsSource for that combobox in XAML?
I may not understand correctly, but you could possibly have an object that contains all that information together, and bind that to the combo box.
ie
public class ContextualInfo
{
public Customer Customer { get; set; }
public int PartNumber { get; set; }
public Manufacturer Manufacturer { get; set; }
}
In reply to comment.
How about having the rows returned from the query to also be in the ContextualInfo mentioned above? You can then bind the itemsource to that. You could potentially run the query in the constructor for the ContextualInfo class.
I'm about to redo an old WinForms application as a WPF application. The core of this applicaton is a custom "grid" component. I'd like some ideas on the best way to do this as a WPF component.
The application displays a grid of data for different countries/sectors. Each cell of the grid displays different information (e.g. graph, image) depending on the available data for that country/sector.
I have a domain model assembly that I want to keep clean - to maximize reuse. The structure is as follows:
Table
Continents
Countries
Sectors
Data[country, sector]
The grid displays countries down the left and sectors across the top.
In the current application, the grid component has a (POCO) Table property and an Refresh() methods to manually redraw it. So, if the Table is updated, the parent of the grid component refreshes it. The grid component also has a number of events that are fired if a continent, country or cell is clicked - so that the parent can response with pop-up menus, etc.
This all works fine.
However, I'm wondering whether this is the correct model to use for a WPF application. Looking at many of the WPF example, they support data-binding, etc. But, it's not clear, from the simple examples, how I might bind a complex object to my components - or whether it would even be worthwhile.
Also, the WinForms component is completely custom drawn - there are no sub-controls (e.g. Labels) in use. Would it be better to use a WPF user control and build the table from a GridLayout and lots of Label, Shape, etc controls? In practice, they are maybe 20 rows and 20 columns in the grid, and the user regular removes and adds countries/sectors (rows/columnms) while using the application.
My immediate goal is to make sure my design plays well in the WPF eco-system, but I have a secondary goal of learning how to do things in a WPFy way - given this is my first WPF app. I'm pretty on top of the use of building a general WPF app - it's just the custom control stuff that remains a little fuzzy (even after reading around it a little).
Any insights/guidance would be appreciated.
You definitely want to adapt the MVVM approach, as outlined by Josh Smith. Practically, this means that your custom grid component will be contained in it's own View. Backing the view will be your ViewModel, where you will define an ObservableCollection of objects containing your data. These objects will probably come from your Model. This interaction is shown below:
Models:
public class TableData
{
public string Country { get; set; }
public string Continent { get; set; }
public object Sector { get; set; }
}
public class TableManager : ITableManager
{
public Collection<TableData> Rows;
public void GetData()
{
this.Rows = new Collection<TableData>();
this.Rows.Add(...
}
}
ViewModel:
public class TableViewModel
{
private ITableManager _tableManager;
public TableViewModel() : base(new TableManager())
{
}
// for dependency injection (recommended)
public TableViewModel(ITableManager tableManager)
{
_tableManager = tableManager;
_tableManager.GetData();
}
public ObservableCollection<TableData> Rows
{
get { return _tableManager.Rows; }
}
}
View:
<ctrls:CustomDataGrid
ItemsSource={Binding Rows}
AutoGenerateColumns=True
>
<!-- Use AutoGenerateColumns if the # of sectors is dynamic -->
<!-- Otherwise, define columns manually, like so: -->
<DataGridTextColumn
Width="*"
Header="SectorA"
Binding="{Binding Country}
/>
</ctrls:CustomDataGrid>
I used CustomDataGrid in the view because I assume you're going to subclass your own DataGrid. This will allow you to override events to customize the DataGrid to your liking:
public class CustomDataGrid : DataGrid
{
public override Event...
}
In a framework for an app that I'm creating, I can have multiple "plug-in" 's added to a "workspace", where they can be rearranged and re-sized as necessary. I want each plug-in to be able to register WorkspaceParameters (global parameters shared by all plug-ins) with a WorkspaceManager (exposed through IWorkspaceManager interface) class. When the plug-in registers a global parameter, the WorkspaceManager first checks if one with the same name as already been registered and if it has, the plugin just subscribes to be notified if it changes. If it one has not been created already with the given name, a new one is created. The WorkspaceParameter class currently looks like this:
public class WorkspaceParameter
{
public string Label { get; set; }
public Type ParameterType { get; set; }
public object Value { get; set; }
}
I then have a view in the app that I want to display each WorkspaceParameter for the user to be able to change the value. The UI should be based on the ParameterType member of each WorkspaceParameter and all of the parameters should be displayed in a StackPanel. For example, if the Type is DateTime, I want to display a textbox with a calendar selector next to it. If it's a string, a simple Textbox with the label next to it, and lastly for now, if it's an enum type, a dropdown with the available enum options.
How can I implement this dynamic view using the principles of PRISM and MVVM?
I would recommend to look at a series of Blogpost by Glenn Block.
In the last article he refactored his solution to use the mvvm pattern.
He uses MEF heavily this is a framework for extensibilty.
This is No. 5 of a sequal please read all of them it could be basically what you were looking for.
http://msmvps.com/blogs/theproblemsolver/archive/2009/02/18/getting-started-with-the-repository-pattern-in-silverlight.aspx