Extend My Models - c#

I am working on a WPF MVVM framework
I have a ItemType Model
public class ItemType
{
public long ItemTypeID { get; set; }
public long ItemCategoryID { get; set; }
public string Name { get; set; }
}
and other ItemCategory Model
public class ItemCategory
{
public long ItemCategoryID { get; set; }
public string Name { get; set; }
}
Now I want to Bind ItemType to a data grid, But I don't want to show ItemCategoryID. I want to show ItemCategory.Name
How can this be done without changing my original class?

This is what MVVM uses the ViewModel for.
Do not modify the Model but instead create ViewModel classes that are structured according to the needs of the View. that is where you would leave out the properties you don't want.
EDIT
Here is a way of doing that:
public class ItemTypeViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
OnPropertyChanged("Name");
}
}
}
private string category;
public string Category
{
get { return category; }
set
{
if (category != value)
{
category = value;
OnPropertyChanged("Category");
}
}
}
public static ItemTypeViewModel FromModel(ItemType model)
{
var itemTypeViewModel =
new ItemTypeViewModel
{
Name = model.Name,
Category = categories[model.CategoryID].Name;
};
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var p = PropertyChanged;
if (p != null)
{
p(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ItemTypesViewModel : ObservableCollection<ItemTypesViewModel>
{
private ObservableCollection<ItemTypeViewModel> _collection;
public ObservableCollection<ItemTypeViewModel> Collection
{
get { return _collection; }
set
{
if (_collection != value)
{
_collection = value;
OnPropertyChanged(new PropertyChangedEventArgs("Collection"));
}
}
}
}
Use the static method to create instances of the ItemTypeViewModel for each ItemType in the model. Put them all in the 'Collection' property of the ItemTypeSViewModel.
Bind the DataGrid to the Collection.
I 'removed' the relation that exists in your model between ItemType and ItemCategory from my ViewModel. This is just one way of handling such a construction. Instead you could create a ViewModel class for the ItemCategory too and have a reference in the ItemTypeViewModel class to an instance of the ItemCategoryViewModel class.
Note that this is just one way of handling this. You could solve this problem in some other ways too. Also: you will need to provide a transformation from the ViewModel classes back to the Model as well.
A final bit of advise: if this is new to you start reading/watching tutorials on MVVM: https://stackoverflow.com/questions/2267903/learning-mvvm-for-wpf

The answer provided by both guys are perfectly valid and fit your needs, as much as I understood your question. The basic idea is:
#Amani:
//check if the query fits your needs
var temp = from i in categoryList
from it in itemList
where i.ItemCategoryID == it.ItemCategoryID
select new { i, it };
Make a cross query to generate a new composed type, which can be
public class ItemTypeViewModel
{
public long ItemTypeID { get; set; }
public string ItemCategoryName { get; set; }
public string ItemTypeName { get; set; }
}
this is according to MVVM patter guideline responded by #Erno. Use a collection of ItemTypeViewModel object to bind them to your view.
Hope this helps.
Regards.

If this is your database model you can easily make a view for join those tables. or you can use this following code as pattern :
var temp = from i in categoryList
from it in itemList
where i.ItemCategoryID == it.ItemCategoryID
select new { i, it };
Hope this help.

Related

ReactiveUI BindTo not updating the ReactiveList

I have a ReactiveObject class named GroupedIssueTypeSearchFilter:
public class GroupedIssueTypeSearchFilter : ReactiveObject
{
public int CategoryID { get; set; }
public string CategoryTitle { get; set; }
int selectionCode;
public int SelectionCode
{
get { return selectionCode; }
set { this.RaiseAndSetIfChanged(ref selectionCode, value); }
}
public ReactiveList<IssueTypeSearchFilter> IssueTypeFilterList { get; set; }
public GroupedIssueTypeSearchFilter(int catID, string catTitle, List<IssueTypeSearchFilter> issueTypeList)
{
this.CategoryID = catID;
this.CategoryTitle = catTitle;
this.IssueTypeFilterList = new ReactiveList<IssueTypeSearchFilter>(issueTypeList);
}
}
Then in IssueTypeFilterTableViewDelegate class, one of its properties is GroupedIssueTypesFilter:
public ReactiveList<GroupedIssueTypeSearchFilter> GroupedIssueTypesFilter { get; set; }
I also have a ViewController and its ViewModel. I used BindTo to bind one of the view model properties to that class:
IssueTypeFilterTableViewDelegate tvd = new IssueTypeFilterTableViewDelegate();
this.WhenAnyValue(vm => vm.ViewModel.GroupedStandardIssueTypesTV)
.BindTo(this, x => x.tvd.GroupedIssueTypesFilter);
GroupedStandardIssueTypesTV type is ReactiveList<GroupedIssueTypeSearchFilter>.
Later, with some operation inside the ViewModel, the GroupedStandardIssueTypesTV.IssueTypeFilterList value is changed.
But, GroupedIssueTypesFilter.IssueTypeFilterList value is not changed. I need to close the View, re-open it, then its value will be updated.
How to make the GroupedIssueTypesFilter follows the changes in GroupedStandardIssueTypesTV?
Fully agree with #ramonesteban78 answer.
But you can go in another way and refill your ReactiveList with Clear and Add/AddRange methods.
Like this:
public GroupedIssueTypeSearchFilter(int catID, string catTitle, List<IssueTypeSearchFilter> issueTypeList)
{
this.CategoryID = catID;
this.CategoryTitle = catTitle;
this.IssueTypeFilterList.Clear();
this.IssueTypeFilterList.AddRange(issueTypeList);
}
Don't forget read about 'Suppressing Notifications' here: https://docs.reactiveui.net/en/user-guide/lists/index.html
I think your problem is that you are not raising changes in your ReactiveList declaring it like:
public ReactiveList<IssueTypeSearchFilter> IssueTypeFilterList { get; set; }
Try this:
ReactiveList<IssueTypeSearchFilter> issueTypeFilterList
public ReactiveList<IssueTypeSearchFilter> IssueTypeFilterList
{
get { return issueTypeFilterList; };
set { this.RaiseAndSetIfChanged(ref issueTypeFilterList, value); };
}
Without the RaiseAndSetIfChanged you won`t be communicating changes to the view in your collection.
You can also subscribe to the observables that ReactiveList exposes like:
ItemsAdded
ItemsRemoved
ItemsMoved
Changed
etc
More info here: https://docs.reactiveui.net/en/user-guide/lists/index.html

Caliburn.Micro issue: PropertyChangedBase, BindableCollection and AutoMapper

I'm using Caliburn.Micro MVVM framework in my WPF application.
There is a DataGrid in a View that is binded to a BindableCollection<SomeObjectStatus>.
public class SomeObjectStatus
{
public string Message { get; set; }
public bool IsInitializing { get; set; }
}
I need to notify the UI somehow is any property of SomeObjectStatus is changed. The common way is to inherit PropertyChangedBase and Call NotifyOfPropertyChange() in each property setter:
public class SomeObjectStatus : PropertyChangedBase
{
private string _message;
private bool _isInitializing;
public string Message
{
get { return _message; }
set
{
if (value == _message)
return;
_message = value;
NotifyOfPropertyChange();
}
}
public bool IsInitializing
{
get { return _isInitializing; }
set
{
if (value == _isInitializing)
return;
_isInitializing = value;
NotifyOfPropertyChange();
}
}
}
But SomeObjectStatus is a Model class that I don't want to clog with a stuff like NotifyOfPropertyChange() etc.
I can create a clone class of SomeObjectStatus with same properties and implemented PropertyChangedBase specially for the ViewModel, lets call it SomeObjectStatusWithNotify. But in this case I need manually assign each property from SomeObjectStatus to SomeObjectStatusWithNotify when changes happen. In real project there are too much properties to assign them manually. So I need somehow to solve the question how to assign values from SomeObjectStatus to SomeObjectStatusWithNotify with same names automatically. It feels like I need AutoMapper-like functionality here. But I need to assign values to existing object instead creating a new one like AutoMapper does. Or maybe there is a elegant way to create PropertyChangedBase from regular class?
AutoMapper can assign values to an existing object if you use the correct overload of Map():
[TestClass]
public class C
{
[TestMethod]
public void M()
{
Mapper.CreateMap<SomeObject, SomeOtherObject>();
SomeObject source = new SomeObject {Name = "An Object"};
SomeOtherObject target = new SomeOtherObject {Id = 123};
Mapper.Map(source, target);
Assert.AreEqual(123, target.Id);
Assert.AreEqual("An Object", target.Name);
}
private class SomeOtherObject
{
public int Id { get; set; }
public string Name { get; set; }
}
private class SomeObject
{
public string Name { get; set; }
}
}

How to know what values get updated from the UI side in Generic list in C#

Case scenario:
I have an application in silverlight, in that application we have list which is bind with respective ViewModel in that ViewModel I have three (ID, Name and Age) properties and one of the (Age) property value will be update by the user, from the view or UI. UI displayed a list of records in each list item have NAME is read only and AGE is Editable. (AGE have Textbox in the list Item and it has TwoWay binding with the Age property). When user Update AGE value from the UI side I am getting updated values in the bind list itself due to INotifyPropertyChanged.
In this case I wanted to update only those records only which being updated in the list I don't want to send back entire list to server.
public class EmpViewModel : INotifyPropertyChanged
{
public List<EmpModel> Employees { get; set; }
public ICommand SaveCommand { get; set; }
private List<EmpModel> _employees;
public EmpViewModel()
{
SaveCommand = new BaseCommand(UpdataInfo, CanUpdataInfo);
this.Employees = new List<EmpModel>();
_employees = new List<EmpModel>();
}
public void GetEmployeeInfo()
{
Employees = new List<EmpModel>
{
new EmpModel { ID =1, NAME="Amar", AGE=0},
new EmpModel { ID =2, NAME="Sameer", AGE=0},
new EmpModel { ID =3, NAME="Ram", AGE=0},
new EmpModel { ID =4, NAME="Rahim", AGE=0}
};
_employees = Employees;
}
private void UpdataInfo(object param)
{
// one way to update information
// why --> when user update the from the UI side _employees.AGE value also get update.
List<EmpModel> difference = this.Employees.Except(_employees) as List<EmpModel>;
foreach (var item in difference)
{
// save only updated records
}
// second way to updat information
// (?) how to to know what are the values get updated from the UI side in Generic list in C#
}
private bool CanUpdataInfo(object param)
{
return true;
}
private void PropertyChangedEvent(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
/// <summary>
/// Property Changed EventHandler
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
}
public class EmpModel
{
public int ID { get; set; }
public string NAME { get; set; }
public int AGE { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void FirePropertyEvent(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
So here is My Questions for UpdataInfo() Method.
1) why --> when user update the value from the UI side _employees.AGE
value also got update. so i am not able to identify.
2) (?) how to to know what are the values get updated from the UI side
in Generic list in C#.
Please let me know to solve this issue.
I have seen techniques which include a IsDirty property in the model (base) class.
public class ModelBase
{
public bool IsDirty { get; set; }
}
public class EmpModel : ModelBase
{
public int ID { get; set; }
public string Name {get; set;}
public int Age
{
get { return _age; }
set
{
_age = value;
// Only if change comes from the user...
IsDirty = true;
}
}
private int _age;
}
When the user changes a value in the UI (e.g. Age) you set IsDirty to true.
In your UpdateInfo method you use only the items which have IsDirty == true, perform the update, and then set their IsDirty back to false when you are done.

WPF DataGrid binding doesn't update

Here's my DataGrid:
<DataGrid x:Name="MoonMining"
ItemsSource="{Binding MarketData.MoonMinerals, ElementName=window}">
<DataGrid.DataContext>
<local:MoonMineral/>
</DataGrid.DataContext>
<DataGrid.Columns>
.. Yes i have columns and they are irrelevant to my question .
</DataGrid.Columns>
</DataGrid>
MarketData is a class which contains most of my programs logic. MoonMinerals is defined in that class:
public class MarketData
{
private ObservableCollection<MoonMineral> _moonMinerals = new ObservableCollection<MoonMineral>();
public ObservableCollection<MoonMineral> MoonMinerals
{
get { return _moonMinerals; }
set { _moonMinerals = value; }
}
}
And here's my MoonMineral class:
[NotifyPropertyChanged]
public class MoonMineral
{
public MoonMineral()
: this("Def", "Def")
{
}
public MoonMineral(string name, string rarity)
{
Name = name;
Rarity = rarity;
}
public string Name { get; set; }
public double Price { get; set; }
public double Volume { get; set; }
public string Rarity { get; set; }
public double TransportVolume { get; set; }
public double TransportCosts { get; set; }
public double GrossProfit { get; set; }
public double NetProfit { get; set; }
}
As you can see, I'm using PostSharp to clear up my code, but when I manually implement INotifyPropertyChanged I have the same problem.
Now the problem is that my DataGrid doesn't update by itself, I have to manually call this in a method which modifies MoonMinerals:
var bindingExpression = MoonMining.GetBindingExpression(ItemsControl.ItemsSourceProperty);
if (bindingExpression != null)
bindingExpression.UpdateTarget();
I know this isn't big of a deal, but I wanted to finally manage to bind data to ui entirely using xaml. All my previous attempts involved setting DataGrids ItemsSource property every time I updated the data.
To sum up comments you're implementing INotifyPropertyChanged interface for MoonMineral class and use ObservableCollection which will handle changes to the collection but there seems to be nothing in place to handle changes to MoonMinerals property
private ObservableCollection<MoonMineral> _moonMinerals = new ObservableCollection<MoonMineral>();
public ObservableCollection<MoonMineral> MoonMinerals
{
get { return _moonMinerals; }
set { _moonMinerals = value; }
}
You can either implement INotifyPropertyChanged interface in the class that exposes MoonMinerals property or change it to read-only and use only one instance of _moonMinerals and simply clear it and add/remove items
private readonly ObservableCollection<MoonMineral> _moonMinerals = new ObservableCollection<MoonMineral>();
public ObservableCollection<MoonMineral> MoonMinerals
{
get { return _moonMinerals; }
}
Also, as a side note, you don't need
<DataGrid.DataContext>
<local:MoonMineral/>
</DataGrid.DataContext>
as this will set DataContext of the DataGrid to new instance of MoonMineral. It works in your case as you change binding context of ItemsSource using ElementName so DataContext is not used in your case.

Bind List of Classes into DataGridView

I need to bind a List<MyClass> myList to a DataGridView. And get in the results table with two columns ID and Name.
Code snippets:
private List<MyClass> myList = new List<MyClass>(){...};
public void BindClass()
{
dataGridView.DataSource = myList;
}
public MyClass
{
public MyDataClass Data{ get; set; }
}
public MyDataClass
{
public string ID { get; set; }
public string Name { get; set; }
}
Is it possible?
How about binding to an anonymous type:
public void BindClass()
{
dataGridView1.DataSource = myList.Select(myClass => new {myClass.Data.ID, myClass.Data.Name}).ToList();
}
Will you be updating the data in the datagridview ?
To do that without changing the model is exceptionally tricky (but possible), requiring ICustomTypeDescriptor or TypeDescriptionProvider, and a custom PropertyDescriptor. To be honest: not worth it.
Just add pass-thru properties:
public MyClass
{
public MyDataClass Data{get; set;}
[DisplayName("ID")]
public string DataID {
get {return Data.ID;}
set {Data.ID = value;}
}
[DisplayName("Name")]
public string DataName {
get {return Data.Name;}
set {Data.Name = value;}
}
}
It's easy with LINQ as you can see in This answer
Here's a simple implementation of something I needed to attach to datagridview.
DataGridView1.DataSource = _
(From i In ItemList Select i.ListID, i.FullName, i.PurchaseDesc, i.EditSequence).ToList
No, you can't do this out of the box. You will have to write a custom binding source (most likely with specialized logic for your specific purpose) to allow 'drilling' deeper than just 1 level of properties.

Categories