My goal is to create an array to handle seating in a theater. The array part is fine , but I want the array to display an "x" or a 1 to show if a seat has been taken by someone , but I also want to be able to save a name to that location in the array so that I can check who is sitting where.
Let me know if you need more info.
Assuming your seating are in a grid style (Cinema)
Create an object like:
Public Class SeatingArrangement
Public Property Row As Int
Public Property Col As Int
Public Property Occupier As String
End Class
Then, create a list out of it
Public Property Items As New List(Of SeatingArrangement)
Then you can use Linq to manipulate the items, if occupier is null or blank, it's not taken, if the not null/blank, it's taken by the occupier (name).
For you displaying:
You can using INotifyPropertyChanged.PropertyChanged event to update the UI and optionally show occupier name on the tooltip.
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.
I have two WPF windows:
- one displays a list of data
- the other shows a detailed view of a particular data when the button in list is clicked
THE ACTUAL PROBLEM :
When i remove or delete the Car Type data from textbox or basically string values - the "empty" value is reflected back to list of data.
The textboxes are using Twoway mode.
However if i delete or empty the textbox that contains enum data or int data the empty value is not reflected back to list, only problem arises when the data is in string.
Below is snapshot of data that is selected from ListView:
New to MVVM architecture and WPF please take it easy on me :|
You can't set an int property or a Country property to an empty string.
You can set them to null if you define the properties as nullable:
public class Car
{
public int? Id { get; set; }
public string Name{ get; set; }
public int? Price { get; set; }
public Country? Country { get; set; }
public string Type { get; set; }
}
It seems you try to detach the view from the viewmodel (changed view but want to keep the viewmodel property unchanged) but that's just a wrong approach.
If you have a list of persisted data and you want to edit one item, without reflecting the edit in the list before saving, then you need a separate viewmodel with separate data.
Get initial values based on a list item, let the user edit the data and explicitely write the changed data back into the list item at saving.
Tools like AutoMapper can ease the creation of editor items to some extent, if you want to avoid manual copying of the data to/from the editor viewmodel.
Finally got the solution to what i was looking for; as said in my OP the problem was that whenever i changed a value of the property bound to textbox that data is reflected back (even if the data is blank white space) to source no matter what - you click update or cancel or any button on window that closes the form.
SOLUTION :
I set the UpdateSourceTrigger to Explicit.
By default UpdateSourceTrigger is set to default which means the value change in property is reflected back to source.
When UpdateSourceTrigger is set to Explicit which basically means that changes made to values of binding are not sent back to the source even if the Mode is TwoWay or OneWayToSource.
Check here what MSDN says about Binding.UpdateSourceTrigger Property
So I have 4 classes, for example:
Class A Inherits I
Public name as String
Public ID as Integer
Class B Inherits I
Public test as String
Public somethingElse as Integer
Class C Inherits I
Public banana as String
Public Type as String
Public length as Integer
Class D Inherits I
Public name as String
Public ID as String
Say I have a ComboBox in my WPF application that contains a list of I objects (some are of type Class A, some of type Class C, etc etc etc.).
When one is selected, I want the datagrid to populate with the public variables of the selected class - variable names in the left column and values in the right.
I want the right-hand column to be editable, but not to update the variables in the class directly.
My questions are, then - how do I bind the datagrid to the selected class if all/some of the variables are different in each class? And how do I then keep the association with the variable, so I can update it later if the changes the user makes passes my custom validation?
My idea (that I don't know how to implement):
Would each class need some sort of converter method that the DataGrid can bind to? but if so, what would that method return?
Would I just have to keep track that row 1 contains this variable, etc. for each class, so I can update later?
Perhaps if you had tried this before asking your question, then you would already have your answer? In WPF, the DataGrid control provides functionality that will auto generate its columns. In fact, that is the default behaviour and you have to turn it off if you don't want it. In your case however, it sounds like you want it left on.
You'll need an ObservableCollection<I> property in your code behind or view model to data bind to the DataGrid.ItemsSource property... let's call it Items:
<DataGrid ItemsSource="{Binding Items}" />
Now here's the tricky part when you change the type of objects in the collection and have to attempt to data bind all of the new properties to the DataGrid.Columns collection:
List<A> classAItems = GetClassAItems();
Items = new ObservableCollection<I>(classAItems);
That's it! Show a different class?:
List<B> classBItems = GetClassBItems();
Items = new ObservableCollection<I>(classBItems);
That's it!
UPDATE >>>
how can I have two columns - "field" and "value", with each variable populating a row?
I seem to have inadvertently answered your follow up question earlier, rather than you actual question. To get all column headers correctly populated, you should use an ObservableCollection<object> instead. To have only the common properties populated in the column headers, you should declare an interface with just those properties in. After implementing that interface in all of your classes, you will then be able to display them all in the same DataGrid (even mixed in the same collection).
I am working on a winforms application where I am rendering domain/object data via an ultrawingrid. I am using a bindingsource to bind the object to the grid.For simple objects this works quite well.
The thing I'm trying to get my head around is rendering an object with nested objects for e.g a Person class will have a property of Address class. I would like to display the properties of Address (Street, City, Country) as columns on the grid along with the properties of the Person class.
The grid has to be editable and any user changes need to reflect back on the domain object (which I'm doing via the binding source).
What's the best way to go about this?
Binding
I typically use some sort of code like this:
Dim persons = new BindingList(Of Person)
UltraGrid1.DataSource = persons
The binding list will handle the add/remove of rows for you, but it doesn't know about the properties inside Person. To get that part of the binding to work, you'll need to have Person implement INotifyPropertyChanged. This will notify the ultragrid when the properties have changed. The code will look something like this (yes, unfortunately this makes it so you can't use auto-implemented properties):
Private _phoneNumber As String
Public Property PhoneNumber As String
Get
Return Me._phoneNumber
End Get
Set(ByVal value As String)
If value <> _phoneNumber Then
Me._phoneNumber = value
NotifyPropertyChanged("PhoneNumber")
End If
End Set
End Property
Flattening object hierarchies
It looks like what you're ask for isn't directly possible. There are a few options:
Unbound columns in the UI that you fill in during the InitializeRow event
Modify your Person class to expose the properties of Address with some pass through code to handle the setting of the properties.
(I can provide a code samples if needed)
One-to-many Nested Objects
If you, for example, had multiple addresses per person, you could show them nested in an expandable section under each Person row. To do this, inside your Person you'd have a BindingList(Of Address) which also implements INotifyPropertyChanged. Not exactly what you want, but an option :)
Words of caution
A few notes if you are doing MVP. You'll need to have the same reference to the BindingList in your view and presenter, obviously. Also, if you need to reset the contents, I'd recommend calling list.Clear() instead of creating a new one. If you create a new one in your presenter, you'll have broken the connection with the UltraGrid and you'll have to re-set the DataSource property in the view.
I have a class that contains data from some model. This class has metadata along with the actual value.
class ServerValue {
public int SomeId {get;}
public int SomeOtherId {get;}
public DateTime LastChanged {get;}
public object Value {get;set;}
// this lets me show the value, but how do i update it from the grid?
public override string ToString(){
return Value.ToString();
}
}
Now I also have a class MyDataTable that derives from DataTable that has all kind of logic. It calls the server, gets a bunch of ServerValues and puts them into Rows and Columns.
Finally I have a WPF DataGrid that I bind to the MyDataTable and the data are displayed, because the DataGrid calls ToString on each ServerValue and gets back the value for display. Hurray so far.
Now, I want to have two way databinding, so input on the grid is written back to the ServerValue. So I want to bind the grid cells to the Value property of the ServerValue instead of the ServerValue itself.
Right now the ServerValue of the DataGrid cell is just replaced with a string. I could work around this and all but I'd to try the elegant route first.
So I have a datatable with a complex type in cells and i want two-way databinding to a specific property of that type.
Is this possible? I've been googling on this and i can't anything on this.
Thanks in advance,
John
What you want is a way to convert back and forth from your object to their text reprenstations.
Define a Converter for your Binding
http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx