Caliburn.Micro issue: PropertyChangedBase, BindableCollection and AutoMapper - c#

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; }
}
}

Related

Initializing property value in one class using the return value of a method in a different class

I have the following structure:
public class LogicStatement : ILogicStatement
{
public string TestLogic { get; set; }
public string CompareLogic { get; set; }
public string Operator { get; set; }
public string Expression();
public bool Value();
}
public class Test : ITest
{
public int TestId { get; set; }
public int LiteralId { get; set; }
public string TestName { get; set; }
public string TestText { get; set; }
public string TestDisplayName { get; }
**public ILogicStatement LogicStatement { get; set; }**
public string Expression { get; set; }
public bool Value { get; set; }
}
public class Literal : ILiteral
{
some property members...
**public List<ITest> Tests {get; set;}**
some method members...
}
Note that the class Test has a member of type LogicStatement, and the class Literal has a member of type List.
Note also that all classes have properties and methods that share the same name: Expression, Value, Expression(), Value().
The value of Expression and Value (properties and methods) depend on values in the LogicStatement class.
Throughout the whole project, I use the Interface Type for to instantiate each object to adhere with Dependency Inversion. To support this, I use a factory-like design to create new instances of Test and LogicStatement.
Example:
public static class Factory
{
public static ILogicStatement CreateLogicStatement()
{
return new LogicStatement();
}
public static ITest CreateTest()
{
return new Test(CreateLogicStatement());
}
public static List<ITest> CreateTests()
{
return new List<ITest>();
}
//repeat the same for evey other class.
}
My goal is to have Expression() and Value() be calculated only once in the bottom level class (LogicStatement), and somehow get transfered to their counterpart properties in the higher level classes.
I'm getting the data from Dapper and it looks like all the nested objects are returned from the Dapper module correctly with the same nested structure I intended, and with the right values for all of their members. All of them but Expression, Expression(), Value, Value() are null.
my constructors look like this:
public LogicStatement()
{
Expression();
Value();
}
public Test(ILogicStatement logicStatement)
{
_logicStatement = logicStatement;
Expression = _logicStatement.Expression();
Value = _logicStatement.Value();
}
public Literal(ITest test)
{
_test = test;
Expression = _test.Expression;
Value = _test.Value;
}
and my main:
List<ILiteral> literals = Factory.CreateLiterals();
List<ITest> tests = Facotry.CreateTests();
List<ILogicStatement> logicStatements = Factory.CreateLogicStatements();
literals = GetDataFromDapper();
This last line seems to assign correct values to all other members on all hierarchies. But I cannot get Expression and Value to be anything other than null.
If I test LogicStatement.Expression() and LogicStatement.Value() standalone, they do return the expexted values. but starting at the first parent class Test, these properties are all null.
I think I'm doing something wrong in the way i'm instantiating my objects. Primarily because I'm not sure i understand basic best practices to write constructors.
Maybe I the desired behavior should be implemented through events, where the Test and Literal classes subscribe to changes in the Expression() and Value() methods (or rather to what calculates them). But I never used events and I'd like to know if this fundamentally can be acheived without them first.
My question: How do I make the Expression() Value() at the bottom level class "Fire up" whenever LogicStatement is instantiated, and then have the Expression and Value properties be assigned accordingly as a result.
In other words, I want the following to always be true:
test[i].Expression == literal[i].Expression == LogicStatement[i].Expression()
I'm a beginner in OOP. So any fundamental explanation is welcome.
As you are new to object oriented programming I would start with the basics and leave factories and adhering with Dependency Inversion and the interfaces away for later.
You could tell Dapper to split joined tables into multiple entities (see https://www.learndapper.com/relationships), but for learning OOP I would start doing everything manually.
Your class design does not look proper to me yet. Not sure what Expression and Value of the LogicStatement are, but if they are calculations based on the other properties, I would implement them as (just to show off with complicated words) lazy initialized cached getter properties that are invalidated in the setters of the relevant properties. That ensures you only calculate them once for as many reads you like but recalculate them on first read after one or multiple properties have been updated.
public class LogicStatement {
private string _testLogic;
private string _compareLogic;
private string _operator;
private string? _expression;
private bool? _value;
public LogicStatement(string testLogic, string compareLogic, string #operator) {
_testLogic = testLogic;
_compareLogic = compareLogic;
_operator = #operator;
}
public string TestLogic {
get {
return _testLogic;
}
set {
_testLogic = value;
InvalidateCachedValues();
}
}
public string CompareLogic {
get {
return _compareLogic;
}
set {
_compareLogic = value;
InvalidateCachedValues();
}
}
public string Operator {
get {
return _operator;
}
set {
_operator = value;
InvalidateCachedValues();
}
}
public string Expression {
get {
string? result = _expression;
if (result is null) {
_expression = result = BuildExpression();
}
return result;
}
}
public bool Value {
get {
bool? result = _value;
if (result is null) {
_value = result = EvaluateValue();
}
return result.Value;
}
}
private void InvalidateCachedValues() {
_expression = null;
_value = null;
}
private string BuildExpression() {
//Your logic goes here
throw new NotImplementedException();
}
private bool EvaluateValue() {
//Your logic goes here
throw new NotImplementedException();
}
}
Sorry, it got a bit bigger with the full properties.
In the other classes I would not copy the Value and the Expression but simply remove these properties as anybody can easily access them through the LogicStatement property:
public class Test {
public Test(int testId, int literalId, string testName, string testText, string testDisplayName, LogicStatement logicStatement) {
TestId = testId;
LiteralId = literalId;
TestText = testText;
TestDisplayName = testDisplayName;
LogicStatement = logicStatement;
}
public int TestId { get; }
public int LiteralId { get; }
public string TestName { get; }
public string TestText { get; }
public string TestDisplayName { get; }
public LogicStatement LogicStatement { get; }
}
and the Literal could look like this (I got a bit confused whether this class has one Test or a list of them, I stick to your constructor + properties that hint in the direction of a single one):
public class Literal {
private Test _test;
public Literal(string property1, int property2, Test test) {
Property1 = property1;
Property2 = property2;
_test = test;
}
public string Property1 { get; }
public int Property2 { get; }
public string Expression => _test.LogicStatement.Expression;
public bool Value => _test.LogicStatement.Value;
}
As you decided not to expose the Test in the Literal it makes sense to provide Expression and Value, otherwise they could also be removed (or kept for convenience).

C# satisfy members of two interfaces with one property

Issue origin: I have a generic component to display data. The data may come from different data models. To unify the access inside the component I created one interface IOne that gets implemented by all classes. In order to abstract the data access layer from the application I'm using interfaces for each data model. So each data display model implements the IOne interface and additionally one of the data model interfaces.
The interfaces:
public interface IOne
{
public int idNameOne { get; set; }
}
public interface DataModelOne
{
public int anotherNameForId{ get; set; }
}
In my class I want both members to be satisfied by one single property. My current solution is as follows:
public class Implementation : IOne, DataModelOne
{
private int _id;
public idNameOne { get { return _id; } set { _id = value; } }
public anotherIdName { get { return _id; } set { _id = value; } }
}
Is there any way to declare that one property satisfies both members? What would be a clean solution for this?
No, there's no other way than what you are doing. I'd however clean up the code (and do proper casing), by removing the backing field (and use one auto-property) and only have the other one reference the first... something like:
public class Implementation : IOne, IDataModelOne
{
public int IdNameOne { get; set }
public int AnotherIdName { get => IdNameOne; set => IdNameOne = value; }
}
(notice I've used the proper casing for the properties... should be the same casing on the interfaces too... also named IDataModelOne correctly, with an I prefix)
To avoid having both on the public API, you should usually use "explicit interface implementation" for this, for example:
public class Implementation : IOne, DataModelOne
{
public int idNameOne { get; set; }
int DataModelOne.anotherNameForId
{
get => idNameOne;
set => idNameOne = value;
}
}
or
public class Implementation : IOne, DataModelOne
{
public int Id { get; set; }
int IOne.idNameOne
{
get => Id;
set => Id = value;
}
int DataModelOne.anotherNameForId
{
get => Id;
set => Id = value;
}
}
If I really had to do this I would make the idNameOne property an auto property with anotherIdName's getter and setter referring to that property.
public class Implementation : IOne, DataModelOne
{
public idNameOne { get; set; }
public anotherIdName { get { return idNameOne; } set { idNameOne = value; } }
}
It's a little bit cleaner and show the intention a little better too.

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

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.

Extend My Models

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.

Categories