Let's say I have a class StockMarket which has a list of Companies.
class StockMarket : IStock
{
private static List<IObserverPush> observersPush;
private static List<IObserverPull> observersPull;
public static List<Company> Companies { get; private set; }
public StockMarket()
{
observersPush = new List<IObserverPush>();
observersPull = new List<IObserverPull>();
Companies = new List<Company>() { new Company("Unilever", "UNA", 47.72, 0.77, 1.63, -3.45, "135B"),
new Company("ING Groep", "INGA", 13.40, -0.07, -0.50, -12.38, "60.4B"),
new Company("ArcelorMittal", "MT", 29.50, 0.14, 0.48, 36.05, "54.6B"),
new Company("ASML Holding", "ASML", 167.40, 2.00, 1.21, 36.49, "53.3B"),
new Company("Heineken", "HEIA", 87.66, -0.02, -0.02, 2.80, "49B"),
new Company("RELX", "REN", 18.15, 0.17, 0.95, -0.22, "38.9B"),
new Company("Philips", "PHIA", 35.49, 0.17, 0.47, 7.61, "33.3B"),
new Company("Unibail Rodamco", "UL", 196.40, -0.15, -0.08, -16.78, "20.3B"),
new Company("Akzo Nobel", "AKZA", 75.68, -0.16, -0.21, 0.33, "19.4B"),
new Company("Altice", "ATC", 7.58, 0.16, 2.16, -66.30, "17.6B")};
Thread thread = new Thread(SimulateMarket);
thread.Start();
}
public void Subscribe(IObserverPull o)
{
observersPull.Add(o);
o.UpdateMarket();
}
public void Unsubscribe(IObserverPull o)
{
observersPull.Remove(o);
}
public void Subscribe(IObserverPush o)
{
observersPush.Add(o);
o.UpdateMarket(Companies);
}
public void Unsubscribe(IObserverPush o)
{
observersPush.Remove(o);
}
public void NotifyObservers()
{
foreach(IObserverPush o in observersPush)
{
o.UpdateMarket(Companies);
}
foreach(IObserverPull o in observersPull)
{
o.UpdateMarket();
}
}
public void SimulateMarket()
{
while(observersPush.Count + observersPull.Count > 0)
{
//randomly change property values of companies
//and notify the observers about the changes
}
}
}
Company class has some properties.
public class Company
{
public string Name { get; private set; }
public string Symbol { get; private set; }
public double Price { get; set; }
public double Change { get; set; }
public double ChangePercentageDay { get; set; }
public double ChangePercentageYear { get; set; }
public string Capital { get; private set; }
public Company(string name, string symbol, double price, double change, double changePercentageDay,
double changePercentageYear, string capital)
{
Name = name;
Symbol = symbol;
Price = price;
Change = change;
ChangePercentageDay = changePercentageDay;
ChangePercentageYear = changePercentageYear;
Capital = capital;
}
}
The Forms have references to the StockMarket and they use it to retrieve data about the companies and to display it.
Form 1
public partial class ConcreteObserverPush : Form, IObserverPush
{
private StockMarket stockMarket;
public ConcreteObserverPush()
{
InitializeComponent();
stockMarket = new StockMarket();
stockMarket.Subscribe(this);
}
public void UpdateMarket(List<Company> companies)
{
stockMarketListView.Items.Clear();
foreach(Company c in companies)
{
ListViewItem item = new ListViewItem(c.Symbol);
item.SubItems.Add(c.Price.ToString());
item.SubItems.Add(c.Change.ToString());
item.SubItems.Add(c.ChangePercentageDay.ToString() + "%");
stockMarketListView.Items.Add(item);
}
}
private void ConcreteObserverPush_FormClosing(object sender, FormClosingEventArgs e)
{
stockMarket.Unsubscribe(this);
}
}
Form 2
public partial class ConcreteObserverPull : Form, IObserverPull
{
private StockMarket stockMarket;
public ConcreteObserverPull()
{
InitializeComponent();
stockMarket = new StockMarket();
stockMarket.Subscribe(this);
}
public void UpdateMarket()
{
stockMarketListView.Items.Clear();
foreach (Company c in StockMarket.Companies)
{
ListViewItem item = new ListViewItem(c.Symbol);
item.SubItems.Add(c.Name);
item.SubItems.Add(c.Price.ToString());
item.SubItems.Add(c.Change.ToString());
item.SubItems.Add(c.ChangePercentageDay.ToString() + "%");
item.SubItems.Add(c.ChangePercentageYear.ToString() + "%");
item.SubItems.Add(c.Capital);
stockMarketListView.Items.Add(item);
}
}
private void ConcreteObserverPull_FormClosing(object sender, FormClosingEventArgs e)
{
stockMarket.Unsubscribe(this);
}
}
The problem is that if the Form gets the list of companies through the property on StockMarket it can change their state. However, I want only StockMarket to have the ability to change the state of the company.
So what would be the best way to share Company state with Form when requested and preventing the Form from modifying it.
I know that a possible solution would be to return clones of Company objects, but I believe there should be a better solution.
Any help is appreciated!
The general gist of this would be to make your Company object immutable. Then you would add methods to the StockMarket object to manipulate the Company list and replace entries with new ones when you want to change a value.
Here's a quick example put together in LINQPad of making the Company class immutable and adding an UpdatePrice method to the StockMarket class.
Whether you want to be able to manipulate the Companies property from outside the StockMarket can be handled by returning the list as ReadOnlyCollection so that it's size can't be manipulated by a consumer.
void Main()
{
var sm = new StockMarket();
sm.Companies.Add(new Company("Test", "TST", 50, 0));
sm.UpdatePrice("Test", 45);
var testCompany = sm.Companies.First(x => x.Name == "Test");
Console.WriteLine($"{testCompany.Name},{testCompany.Symbol},{testCompany.Price},{testCompany.Change}");
//Output: Test,TST,45,-5
}
class StockMarket
{
public List<Company> Companies { get; private set; } = new List<Company>();
public void UpdatePrice(string name, double price) {
var index = Companies.FindIndex(x => x.Name == name);
if(index >= 0)
{
var previous = Companies[index];
Companies[index] = new Company(previous.Name, previous.Symbol, price, price - previous.Price);
}
}
}
class Company
{
public Company(string name, string symbol, double price, double change) {
Name = name;
Symbol = symbol;
Price = price;
Change = change;
}
public string Name { get; }
public string Symbol { get; }
public double Price { get; }
public double Change { get; }
///...
}
This would be a solution:
Create the Company class as a Private Inner Class inside of the StockMarket class, that way it'd only be accessible inside of it, and then provide an interface that only includes the get of all the properties and make Company implement it. You would have to make StockMarket's Company list to be the Interface's type.
Any modification you'd have to do you'd do it by casting the interface's List objects into the original class type.
Example:
class Program
{
public static StockMarket stockMarket = new StockMarket();
static void Main(string[] args)
{
}
}
public interface ICompany
{
string Name { get; }
}
public class StockMarket
{
public StockMarket()
{
Companies = SomeWildFunctionThatRetrievesAllCompanies();
}
public void OneWildFunctionThatModifiesACompany()
{
Company dunno = (Company)Companies[0];
dunno.Name = "Modification Made Possible";
}
private List<ICompany> SomeWildFunctionThatRetrievesAllCompanies()
{
return new List<ICompany>(new List<Company>());
}
public List<ICompany> Companies { get; private set; }
private class Company : ICompany
{
public string Name { get; set; }
}
}
Try this:
class Company
{
public Company(Type type,string name,string symbol,double price, double change)
{
if (type.Name == "StockMarket")
{
Name = name;
Symbol = symbol;
Price = price;
Change = change;
}
}
private string Name { get; set; }
private string Symbol { get; set; }
private double Price { get; set; }
private double Change { get; set; }
///...
}
This will allow you to change the state only if the type is StockMarket
like:
class StockMarket
{
public List<Company> Companies { get; set; }
public StockMarket()
{
Companies = new List<Company>();
}
public StockMarket someMethod()
{
//You can change the state here
StockMarket s = new StockMarket();
s.Companies.Add(new Company(this.GetType(), "aa", "_", 123, 1234));
return s;
}
//...
}
Now you cannot change the state here:
public partial class Observer: Form
{
private StockMarket stockMarket;
public ConcreteObserverPull()
{
InitializeComponent();
stockMarket = new StockMarket();
//Here you cannot change the state
stockMarket.Companies.Add(new Company(this.GetType(), "aa", "_", 123,12));
}
//...
}
Sorry, I don't know C#, but as an idea, you can wrap returned entities with decorator or proxy, which will throw an exception in case of trying to modify state of a company.
Returning clones with fields set as readonly is the safest way to go.
this should be a stupid problem but i dont know where to start,so i'll ask here.i've got a class named Routing which is binded to a datagrid. Inside this class theres an object from another class:
public class Routing : INotifyPropertyChanged
{
public int Sequenza { get; set; }
private ObservableCollection<Prodotti> availableProducts;
public ObservableCollection<Prodotti> AvailableProducts
{
get { return availableProducts; }
set
{
if (availableProducts != value)
{
availableProducts = value;
OnPropertyChanged("AvailableProducts");
}
}
}
private Prodotti product;
public Prodotti Product
{
get { return product; }
set
{
if (product != value)
{
product = value;
UpdateAvailableCosts();
OnPropertyChanged("Product");
}
}
}
}
and then the Product class:
public class Prodotti
{
public int Product_id { get; set; }
public string Product_description { get; set; }
public int Product_treshold { get; set; }
}
Everything works as intended,and if i just declare the "Sequenza = 1" in the routing the datagrid adds the 1 in the first row/column. But i would like to add more initial values,maybe based on data present in the database. but i cant come up with the constructor for it
Routes.Add(new Routing { Sequenza = 1,Prodotti=... });
Routes.Add(new Routing { Sequenza = 1,Prodotti= new Prodotti{Product_id =1, Product_description = "str", Product_treshold =1} });
I'm developing a simple Crud Application (a windows 8.1 store application) using Caliburn Micro 2.0.0-alpha2
I'm in trouble with navigation between viewmodels, passing object.
I read many times the solution proposed by
Anders Gustafsson (How to pass parameter to navigated view model with WinRT Caliburn.Micro?)
and i tried to adapt it to my scope.
But the object is alwais null.
I need to pass a single object selected from a listView to my crudPage.
The crudPage is composed by an userControl that shown the FormView.
So i want to initialize this Form, with the values of the passed object.
I think that the problem is that the "Parameter" is initialized only after the ViewModel is created, but i don't know how to fix that problem.
There is my code, according with the idea of Anders Gustafsson
TransporterListViewModel (a list of Transporters from Database)
public class TransporterListViewModel : ViewModelBase
{
public string Title { get; set; }
public TransporterListViewModel(INavigationService navigationService)
: base(navigationService)
{
LoadData();
}
public async void LoadData() {
_transporters = await TransporterService.GetAll();
}
private BindableCollection<Transporter> _transporters;
public BindableCollection<Transporter> Transporters
{
get
{
return this._transporters;
}
set
{
this._transporters = value;
NotifyOfPropertyChange(() => this.Transporters);
}
}
private Transporter _selectedItem;
public Transporter SelectedItem
{
get
{
return _selectedItem;
}
set
{
_selectedItem = value;
NotifyOfPropertyChange(() => this.SelectedItem);
navigationService.Navigated += NavigationServiceOnNavigated;
navigationService.NavigateToViewModel<TransporterCrudPageViewModel>(_selectedItem;);
navigationService.Navigated -= NavigationServiceOnNavigated;
}
}
private static void NavigationServiceOnNavigated(object sender, NavigationEventArgs args)
{
FrameworkElement view;
TransporterCrudPageViewModel transporterCrudPageViewModel;
if ((view = args.Content as FrameworkElement) == null ||
(transporterCrudPageViewModel = view.DataContext as TransporterCrudPageViewModel) == null) return;
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
}
TransporterCrudViewModel (the page that cointains the UserControl to initialize)
public class TransporterCrudPageViewModel : ViewModelBase
{
public string Title { get; set; }
public Transporter Parameter { get; set; }
public TransporterFormViewModel TransporterFormVM { get; set; }
public async void InitializeTransporterForm(Transporter enumerable)
{
TransporterFormVM = new TransporterFormViewModel(navigationService, enumerable);
await SetUpForm(enumerable);
}
public async Task SetUpForm(Transporter t){
TransporterFormVM.trName = t.trName;
TransporterFormVM.trUrl = t.trUrl;
}
public TransporterCrudPageViewModel(INavigationService navigationService)
: base(navigationService)
{
Title = "TransporterCrud Page";
//this.navigationService = navigationService;
this.InitializeTransporterForm(Parameter);
}
TransporterFormViewModel (the userContol to initialize)
public class TransporterFormViewModel :ViewModelBase
{
public string Title { get; set; }
public Transporter Transporter { get; set; }
public TransporterFormViewModel(INavigationService navigationService,Transporter trans)
: base(navigationService)
{
Transporter = trans;
}
private string _trName;
public string trName
{
get
{
return _trName;
}
set
{
_trName = value;
NotifyOfPropertyChange(() => trName);
}
}
public string trCode { get; set; }
public string trUrl { get; set; }
public int trId { get; set; }
In the constructor TransporterCrudViewModel class you have:
this.InitializeTransporterForm(Parameter);
where Parameter is a property of type Transporter not initialized and you will call the method InitializeTransporterForm with a null parameter. Then you'll call SetUpForm method with a null value of the parameter Transporter t. I think you should initialize in some way this property.
Then, supposing you're continuing in your TransporterListViewModel class with this:
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
in the method InitializeTransporterForm, you don't set the passed parameter as value of the property Parameter with something like this:
public async void InitializeTransporterForm(Transporter enumerable)
{
TransporterFormVM = new TransporterFormViewModel(navigationService, enumerable);
this.Parameter = enumerable; //setting the Parameter property..
await SetUpForm(enumerable);
}
Beside these notes, you should put a breakpoint with your IDE in the line
transporterCrudPageViewModel.InitializeTransporterForm(args.Parameter as Transporter);
Make sure that the property Parameter of the NavigationEventArgs object is not null.
Imagine a class as follows.. It's a class provided to me to work with.. I cannot change its source..
public class MyClass
{
object _Object { get; set; }
public void FuncA1() { _Object = new object(); }
public void FuncA2() { _Object = new List<object>(); }
public int FuncB1() { _Object = 0; return 0; }
public int FuncB2() { _Object = 123; return 123; }
public string FuncC1() { _Object = null; return null; }
public string FuncC2() { _Object = "Hello"; return "Hello"; }
}
Im trying to create a wrapper for this class, such that I can group its many functions into categories..
MyWrapper.Voids.FuncA1();
MyWrapper.Voids.FuncA2();
MyWrapper.Integers.FuncB1();
MyWrapper.Integers.FuncB2();
MyWrapper.Strings.FuncC1();
MyWrapper.Strings.FuncC2();
The only solution I can think of for this scenario is to design the wrapper like this:
public class MyWrapper
{
MyClass _Instance { get; set; }
public _Void Voids { get; private set; }
public _Integer Integers { get; private set; }
public _String Strings { get; private set; }
public class _Void
{
MyWrapper _Parent { get; set; }
public void FuncA1() { _Parent._Instance.FuncA1(); }
public int FuncA2() { return _Parent._Instance.FuncA2(); }
}
public class _Integer
{
...
}
public class _String
{
...
}
public MyWrapper()
{
_Instance = new MyClass();
Voids = new _Voids(this);
Integers = new _Integer(this);
Strings = new _String(this);
}
}
This solution works, but has a number of problems:
- The inner classes are forced to be public, which allows them to be instantiated by the user..
- I am forced to maintain a reference of the parent object in the child classes..
Is there a better way of doing this?
EDIT: The code posted initially was a bit confusing, in the sense that it was diverting attention away from the core issue and more into the issues of whether a function would cause exceptions or not if they all work on the same object..
NOTE: This is not actual code.. I hacked together this example to show what I'm trying to do.. CREATE A WRAPPER AROUND AN OBJECT (I cannot change the original object's code) AND GROUP FUNCTIONS INTO CATEGORIES..
FINAL EDIT: following suggestion by Juharr.. here's what ive done to accomplish what i wanted.. for the betterment of others..
public interface IVoid
{
void FuncA1();
void FuncA2();
}
public interface IInteger
{
int FuncB1();
int FuncB2();
}
public class MyWrapper
{
public MyClass Instance { get; private set; }
public IVoid Voids { get; private set; }
public IInteger Integers { get; private set; }
private abstract class MyBase
{
protected MyWrapper Parent { get; set; }
protected MyClass Instance { get { return Parent.Instance; } }
public MyBase(MyWrapper oParent) { Parent = oParent; }
}
private class MyVoid : MyBase, IVoid
{
public MyVoids (MyWrapper oParent) : base(oParent) { }
public void FuncA1() { Instance.FuncA1(); }
public void FuncA2() { Instance.FuncA2(); }
}
private class MyInteger : MyBase, IInteger
{
public MyInteger (MyWrapper oParent) : base(oParent) { }
public int FuncB1() { return Instance.FuncB1(); }
public int FuncB2() { return Instance.FuncB2(); }
}
public MyWrapper()
{
Instance = new MyClass();
Voids = new MyVoid(this);
Integers = new MyInteger(this);
}
}
You could write public interfaces instead. Then your inner classes don't have to be public. So something like this.
public interface IIntger
{
void Set(int iValue);
int Get();
}
public class MyWrapper
{
MyClass _Instance { get; set; }
public IInteger Integer { get; private set; }
private class _Integer : IInteger
{
MyWrapper _Parent { get; set; }
public void Set(int iValue) { _Parent._Instance.IntegerSet(iValue); }
public int Get() { return _Parent._Instance.IntegerGet(); }
}
public MyWrapper()
{
_Instance = new MyClass();
Integer = new _Integer(this);
}
}
EDIT:
To answer the second part of your question you will either need the reference to the parent class or a reference to the class you are wrapping. So you could have this instead.
public class MyWrapper
{
public IInteger Integer { get; private set; }
private class _Integer : IInteger
{
MyClass _Instance { get; set; }
public _Integer(MyClass myClass) { _Instance = myClass; }
public void Set(int iValue) { _Instance.IntegerSet(iValue); }
public int Get() { return _Instance.IntegerGet(); }
}
public MyWrapper(MyClass instance)
{
Integer = new _Integer(instance);
}
}
I have the following abstract class:
public abstract class TemplateBase
{
public abstract string TemplateName { get; }
public string RuntimeTypeName { get { return GetType().FullName; } }
public abstract List<AreaContainer> TemplateAreas { get; }
}
then these 2 inherited classes:
public class SingleColumnTemplate : TemplateBase
{
public override string TemplateName { get { return "Single column"; } }
public AreaContainer CenterColumn { get; private set; }
public SingleColumnTemplate()
{
this.CenterColumn = new AreaContainer("Middle");
}
private List<AreaContainer> templateAreas;
public override List<AreaContainer> TemplateAreas
{
get
{
if (this.templateAreas == null)
{
this.templateAreas = new List<AreaContainer>() { this.CenterColumn };
}
return this.templateAreas;
}
}
}
and
public class TwoColumnTemplate : TemplateBase
{
public override string TemplateName { get { return "Two column"; } }
public AreaContainer LeftColumn { get; private set; }
public AreaContainer RightColumn { get; private set; }
public TwoColumnTemplate()
{
LeftColumn = new AreaContainer("Left");
RightColumn = new AreaContainer("Right");
}
private List<AreaContainer> templateAreas;
public override List<AreaContainer> TemplateAreas
{
get
{
if (this.templateAreas == null)
{
this.templateAreas = new List<AreaContainer>() { this.LeftColumn, this.RightColumn };
}
return this.templateAreas;
}
}
}
I also have this class that is my model for editing:
public class ContentPage
{
public virtual int ContentPageId { get; set; }
public virtual string Title { get; set; }
public TemplateBase Template { get; set; }
}
Question:
for my ActionResults I have the following:
[HttpGet]
public ActionResult Edit()
{
var row = new ContentPage();
var template = new TwoColumnTemplate();
// Areas
HtmlArea html_left = new HtmlArea();
html_left.HtmlContent = "left area html content";
HtmlArea html_right = new HtmlArea();
html_right.HtmlContent = "right area html content";
template.LeftColumn.Areas.Add(html_left);
template.RightColumn.Areas.Add(html_right);
row.Template = template;
return View(row);
}
[HttpPost]
[ValidateInput(false)]
public ActionResult Edit(ContentPage row)
{
// Here i could loop through List -TemplateAreas and save each template Area to Db. I guess that would work
return this.View(row);
}
Question:
For HttpGet- how would I load row Template from the database? since it could be SingleColumnClass or TwoColumnClass.
how would my ViewModel look like to solve this?
thanks
You can write your own Model Binder that is responsible for binding TemplateBase. You will still need to have a way of knowing (in the model binder) which type you will be using a runtime, but you can always delegate that to a factory or service locator of some sort. I did a quick google search and here is a blog post I found that gives you some information for making a model binder for a similar scenario:
http://weblogs.asp.net/bhaskarghosh/archive/2009/07/08/7143564.aspx
EDIT: The blog leaves out how you tell MVC about your model binder. When the application starts, you can add your model binder to System.Web.Mvc.ModelBinders.Binders
HTH
You need to know the template type in you controller, so you can pass a parameter from the view to the controller, indicating the type (SingleColumn or TwoColumn). You could do this witn a Enum:
public enum TemplateType
{
SingleColumn,
TwoColumn
}
[HttpGet]
public ActionResult Edit(TemplateType templateType)
{
var row = new ContentPage();
TemplateBase template;
if (templateType == TemplateType.SingleColumn)
{
template = new SingleColumnTemplate();
}
else
{
template = new TwoColumnTemplate();
}
...
return View(row);
}
When you create the action link from your view you can specify:
<%= Html.ActionLink("Edit",
"Edit",
"YouController",
new
{
// singlecolumn or twocolumn
// depending on your concrete view
TemplateType = TemplateType.xxx
},
null);
I wonder if you could do something like this?
[HttpGet]
public ActionResult Edit(TemplateType templateType)
{
var row = new ContentPage();
TemplateBase template = (TemplateBase)Activator.CreateInstance(templateType);
...
return View(row);
}
templateType would have to be the exact name of your inherited classes (you can ignore case)