I have a model that contains a flat list with person objects containing a parent ID. This list is not ordered, so children could come before their parent.
class PersonModel
{
int ID;
int Parent;
string Name;
}
I want to display this in a Treeview and therefore in the VM I need to define the Children & Parent as a reference to other VM's. SO I have setup my PersonViewModel like so;
class PersonVM
{
readonly PersonModel = _Person;
PersonVM Parent;
List<PersonVM> Children;
public Name {
get { return Person.Name; }
}
public PersonVM(PersonModel Person)
{
_Person = Person;
}
}
In my TreeView VM I have a constructor that creates PersonVM's from a list of PersonModels;
class TreeViewVM
{
public List<PersonVM> PersonList;
TreeViewVM(List<PersonModel> Persons)
{
foreach (PersonModel Person in Persons)
{
PersonList.Add(new PersonVM(Person ));
}
}
}
I am now struggling to figure out the proper way of creating the heirarchy between the PersonVM's. I cannot access the Model from my Main VM, and in my thinking it should be like that. However that is where the ID is stored.
So what is the proper way of doing this?
Related
This is the layout
House -> HouseDef -> Room -> Door
L---> Windows
The problem is that any class may or may not have lists and nested classes like HouseDefinition does. The point is it should be flexible to handle any of these three cases for class variations:
1. hasList,
2. hasNestedObject with List inside that Nested Object
3. Has neither a List nor Nested class
Example of 1 being a Room class which contains a Window List
Example of 2 like House Class
Example of 3 like a Window Class
I have these two classes that I want to access generically from another class. I want to be able to get the Rooms List in House Definition by access of House class stored as an object in MyTreeNode. How can I do this not bound by types, or polymorphic to support a deeper hierarchy level in the future?
public class House
{
string name;
HouseDefinition definition;
public string Name() { return name; }
public HouseDefinition Definition {get {return definition;}}
public House(string name,HouseDefinition definition)
{
this.name = name;
this.definition = definition;
}
}
public class HouseDefinition
{
private List<Room> rooms = new List<Room>();
string type;
public List<Room> Rooms { get { return rooms; } }
public Room this[int i] { get { return rooms[i]; } }
public HouseDefinition(string type)
{
DefaultLayout();
this.type = type;
}
}
public class MyTreeNode : TreeNode
{
string label;
IEnumerable items;
bool hasList;
object item;
public string Label { get {return label; } }
public IEnumerable Items { get { return items;} }
public object Item { get { return item; } }
public bool HasList { get { return hasList; } }
public MyTreeNode(object item)
{
this.item = item;
label = item.ToString();
hasList = false;
}
public MyTreeNode(object item, IEnumerable Items)
{
this.item = item;
label = item.ToString();
hasList = true;
}
}
I think your classes look fine, but I would remove HouseDefinition, since those properties just seem like House to me. If you want to practice inheritance, you could create an IBuilding interface that forces House, Mansion, Shack to implement GetRooms() or something like that. A couple other things:
Your TreeNode class should be using generic types like public MyTreeNode(T item)
You should check out autoimplemented properties - the public fields automatically create private backing fields, so you don't need to create a private field and a getter like you did here: public object Item { get { return item; } }
it's considered bad practice to use the "object" type, so you should convert those to the generic types mentioned above
Good luck, your code is looking good so far!
Assume I have following Model structure:
class Team {
public string Name {get;set; }
public List<Player> players {get;set;}
}
class Player {
public int Age {get;set;}
public string Name {get;set;}
public Team Team {get;set;}
}
I wish to create Viewmodels for this model. However, I also would like to avoid duplicating all properties from Player in the TeamVM and vice versa (for this simple example this would be feasable, but in reality rather cumbersome).
Looking at the literature and online articles, it seems that the "Pure" way would be to create a ViewModel for each Model and to have a ViewModel only return other ViewModels and never Models. This is all fine, but my problem is: how do you create these viewmodels without getting into a recursion trap. Assume I do it like this:
public class TeamVM: ViewModel<Team> {
private ObservableCollection<PlayerVM> _players;
public TeamVM(Team t): base(t) {
_players = new ObservableCollection();
foreach (Player p in t.players) {
_players.Add(new PlayerVM(t));
}
}
public string Name {
get { return _modelElement.Name; }
set { _modelElement.Name = value; NotifyPropertyChanged(); }
}
public ObservableCollection<PlayerVM> Players {
get { return _players; }
}
}
and
public class PlayerVM : ViewModel<Player> {
private TeamVM _teamVM;
public PlayerVM(Player p): base(p) {
_teamVm = new TeamVM(p.Team);
}
public int Age {
get { return _modelElement.Age; }
set { _modelElement.Age = value; NotifyPropertyChanged(); }
}
public string Name {
get { return _modelElement.Name; }
set { _modelElement.Name = value; NotifyPropertyChanged(); }
}
public TeamVM Team {
get { return _teamVM; }
set { _teamVm = value; NotifyPropertyChanged(); }
}
}
Obviously, the above can never work, since it creates recursion: creation of a TeamVM results in the creation of PlayerVMs which in turn spawn TeamVMs again etc.
Right now, I have solved this, by adding an intermediate class as follows:
public class TeamMinimalVM: ViewModel<Team> {
public TeamVM(Team t): base(t) {
}
public string Name {
get { return _modelElement.Name; }
set { _modelElement.Name = value; NotifyPropertyChanged(); }
}
}
public class TeamVM: TeamMinimalVM {
private ObservableCollection<PlayerVM> _players;
public TeamVM(Team t): base(t) {
_players = new ObservableCollection();
foreach (Player p in t.players) {
_players.Add(new PlayerVM(t));
}
}
}
And then having PlayerVM depend on TeamMinimalVM instead of TeamVM. This means that in the views, you would be able to do: {Binding Player.Team.Name} but not {Binding Player.Team.Players.Name}, which is kind of ok for me I guess since I don't think it's a great idea to do this anyway.
My question now is: is there a better/more "standard" way to do "Pure" VMs of bidirectional model elements? I do not want to clone properties of one type in the other (there are too many), nor do I want to expose Model elements directly.
Finally, the ViewModel class I use is this one (just for completeness, but it is not essential to the question I think.)
public class ModelElementViewModel<T> : ObservableObject where T : class
{
private bool _modelElementChanged;
private T _modelElement;
public ModelElementViewModel(T element)
{
_modelElement = element;
}
/// <summary>
/// The underlying model element for this viewmodel. Protected as one should not bind directly to model elements from the gui.
/// </summary>
internal T ModelElement {
get { return _modelElement; }
set {
if (_modelElement != value)
{
_modelElement = value;
ModelElementChanged = false;
NotifyAllPropertiesChanged();
}
; }
}
/// <summary>
/// Property that can be used to see if the underlying modelelement was changed through this viewmodel (note that an external
/// change to the model element is not tracked!)
/// </summary>
public bool ModelElementChanged {
private set
{
if (_modelElementChanged != value)
{
_modelElementChanged = value;
NotifyPropertyChanged();
}
}
get
{
return _modelElementChanged;
}
}
protected override void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
ModelElementChanged = true;
base.NotifyPropertyChanged(propertyName);
}
}
Edit:
What wasn't clear from my original question is that Players are not used exclusively by teams. I want following three scenarios to work:
I want to be able to create a view for a single player that displays all player information
I want to be able to create a view for a team, displaying the information of that team and a table of all players with their statistics
I also want to be able, for example, to have a Playersbook view, which consists of a table displaying all known players with their teamname for example.
Your classes have a clear hierarchy: teams aggregate players. Teams are owners, players are owned. Therefore, when creating a player VM, you can pass team VM as a constructor argument.
The obvious limitation of this is now you can't have players without teams. Possible solutions are: enforcing players to always be owned by some team; supporting null as a team VM and setting a proper value later; creating a "null team" object and using it for team-less players.
In cases like these, when there's a clear aggregation hierarchy, I use my OwnedObservableCollection<T, TOwner>. With it, I can create create a collection _players = new OwnedObservableCollection<PlayerVM, TeamVM>(this) in a team, then just add and remove players to and from the teams by using just Add and Remove.
I have a tree structure like this:
public class Node
{
public Node Parent { get; set; }
public List<Node> Children { get; set; }
public NodeValue Item { get; set; }
}
And a NodeViewModel like this:
public class NodeViewModel : INotifyPropertyChanged
{
public Node Node
{
get;
private set;
}
public NodeViewModel(Node node)
{
this.Node = node;
this._children = new ObservableCollection<NodeViewModel>();
}
public string Code {
get
{
return this.Item.Code;
}
set
{
this.Item.Code = value;
NotifyPropertyChanged("Code");
}
}
public Node Parent
{
get
{
return this.Node.Parent;
}
set
{
if (value != this.Node.Parent)
{
this.Node.Parent = value;
NotifyPropertyChanged("Parent");
}
}
}
public NodeValue Item
{
get
{
return Node.Item;
}
set
{
this.Node.Item = Item;
}
}
private ObservableCollection<NodeViewModel> _children;
public ObservableCollection<NodeViewModel> Children
{
get
{
_children.Clear();
foreach(var child in Node.Children)
{
_children.Add(new NodeViewModel(child));
}
return _children;
}
protected set
{
this._children = value;
NotifyPropertyChanged("Children");
}
}
The problem is the last property because when I want to update the model using view model, for example when I want to add a new node I must update _children ObservableCollection from NodeViewModel and also Children List<Node> from Node class.
If I update only the model the UI does not update because NotifyPropertyChanged isn't called and if I update only the view, the changes will be lost because the getter will create another ObservableCollection and also the changes are not reflected over the model.
How can I update the model through view model class?
Whichever way you slice it, the view model needs to fully encapsulate the model. If you had a "save" command you could just update/recreate the model's collection at that time.
Assuming you don't have a "save" command though, and the model should always reflect the current state of the view model, one option is to subscribe to the ObservableCollection<T>.CollectionChanged event and update the underlying collection on the fly.
A side note, you most likely also don't want to create a new collection every time Children_get is called, and are better off just lazy-loading one you keep around.
ObservableCollection already implements INotifyPropertyChanged.
However it will only work if the count of the collection changes.
Also why do you want a ViewModel Collection?
But I think you're looking for this implementation:
private ObservableCollection<Node> _children;
public ObservableCollection<Node> Children {
...code logic
}
Don't forget to handle the changed event
I'm sure there should be an easier way to do this. I have a class based on Collection, and that class is a collection of another class. Currently, whenever I have a new item in my class, I assign that item to the listbox. I can't seem to figure out a way to assign all of the values in the collection class, because it is a collection, to the collection of the listbox. Any ideas? Thanks
Ok, what I've done so far is I have a tostring override in the Class used in the collection. This is what I want the listbox to show.
public override string ToString()
{
return string.Format("{0} {1}: {2}", tTypev.ToString(),
Datev.ToString("MM/dd/yyyy"), Amountv.ToString("C"));
}
Which is what I want each item in the listbox to show.
class Transactions : System.Collections.CollectionBase
{
...
}
Is my collections class, containing a collection of the other class, Tansaction. Curently, I use the lstTransactions.Items.Add(), .Remove, .RemovAt, etc to add items to the list box, and the .Add(), .Remove, etc to add items to the Collection Class, Transactions. But I'm trying to decrease reliance on outside controls, and only use them in a few lines of code. I was trying to use something like:
lstTransactions.DataSource = (Transaction)myTrans;
but that didn't seem to work. Mainly because I couldn't figure out what property DataSource took.
I also tried:
lstTransactions.Items =
but it told me that items was read only.
In Windows Form:
You could used DataSource property to bind a collection of object.
DisplayMember property to the name of a property in the data source object.
Sample Code:
In the below sample the output list box would display 2 items : Apple and Ball.
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
EmployeeCollection data = new EmployeeCollection();
data.AddEmployee(new Employee("Apple", 25));
data.AddEmployee(new Employee("Ball", 50));
listBox1.DataSource = data;
listBox1.DisplayMember = "Name";
}
}
public class Employee
{
public Employee(string name, int age)
{
this.Name = name;
this.Age = age;
}
public int Age
{
get; private set;
}
public string Name
{
get; private set;
}
}
public class EmployeeCollection : System.Collections.CollectionBase
{
public void AddEmployee(Employee employee)
{
this.List.Add(employee);
}
}
}
In WPF:
You could use ItemSource property of ListBox to bind a collection (which may be a List, Enumerable, Collections.._ do the job
Sample snippet:
IList<string> data = new List<string>() {"A", "B"};
ListBox listBox = new ListBox();
listBox.ItemsSource = data;
I'm having a bit of an issue. I don't quite know how to handle the situation so I'll just explain a simplified scenario and hopefully you can help me.
I'm trying to map a parent database object to a parent bll object. In this parent database object, there is a foreign key to the ID of the child, and in my parent bll object I use the child bll object (containing more than just an ID).
So here are my bll objects:
public class Parent
{
public int ID { get; set; }
public Child Child { get; set; }
}
public class Child
{
public int ID { get; set; }
public string FirstName { get; set; }
}
And here is my mapper class/method:
public class ParentMapper
{
public Parent MapFromSource(ParentDatabaseObject parentDO)
{
Parent parent = new Parent();
parent.ID = parentDO.ID;
parent.Child = ???;
return parent;
}
}
I don't think it's very important what the ParentDatabaseObject looks like in this case, I'd just like to know how I should map the parent.Child object.
I have considered the following:
parent.Child = new Child();
parent.Child.ID = doParent.Child.Id;
parent.Child.FirstName = doParent.Child.FirstName;
Which doesn't feel right, 'cause I kind of have the urge to put this in my ChildMapper, which leads me to my second way of implementing this (assuming I have a seperate child mapper and have an instance of it called childMapper):
parent.Child = childMapper.MapFromSource(parentDO.Child);
But I kind of have the feeling that using this way of mapping is making my code a bit tightly coupled, because I'd be using my ChildMapper in my ParentMapper.
So I guess my question is: how should I implement this kind of mapping. Is this last method correct or is there something even better? I'm already discarding the first thing I tried.
Thanks for your help!
(I did research before posting this question and this was the closest I could find:
Data Mapper for Child Objects , but I wasn't really content with the only answer in there)
Shouldn't it be better -
parent.Child = childMapper.MapFromSource(parentDO.FoeignKeyToTheChild);
I think you should have methods to get object by Id.
EDIT : If your mapper doesn't DataAccess code, then you have to map the child within your Repository. As your Repository already have DataObjects ready, you can do it the following way -
ParentMapper:
public class ParentMapper
{
public Parent MapFromSource(ParentDo parentDo)
{
Parent parent = new Parent();
parent.Id = parentDo.Id;
return parent;
}
}
ChildMapper:
public class ChildMapper
{
public Child MapFromSource(ChildDo childDo)
{
Child child = new Child();
child.Id = childDo.Id;
child.FirstName = childDo.FirstName;
return child;
}
}
Repository:
public class Repository
{
//you already have parentDo
//you already have childDo
public Parent GetParent()
{
Parent parent = parentMapper.MapFromSource(parentDo);
parent.Child = childMapper.MapFromSource(childDo);
return parent;
}
public Child GetChild()
{
Child child = childMapper.MapFromSource(childDo);
return child;
}
}
Otherwise, your Mapper must have access to DataAccess code.