Can I map or automap an object to a Form and from Form to an object?
I have the following code:
// Class model
class Model1
{
Property1;
Property2;
Property3;
...
}
// Form. I use this form to create and update data of Model1
public partial class FormModel1 : Form
{
private Model1 model1;
...
private void LoadData()
{
Property1Txt.Text = model1.Property1;
Property2Txt.Text = model1.Property2;
Property3Txt.Text = model1.Property3;
}
private void SaveButton_Click(object sender, EventArgs e)
{
model1.Property1 = Property1Txt.Text;
model1.Property2 = Property2Txt.Text;
model1.Property3 = Property3Txt.Text;
model1.Save();
}
}
Note that I have simplified the example.
I want to set data from my model to the form and from form to my model dinamically.
How can I do this?
You can implement data bindings as follows
property1Txt.DataBindings.Add("Text", model1, "Property1");
To have this update the textbox when the property changes you also need to implement inotifypropertychanged. See http://msdn.microsoft.com/en-us/library/ms743695.aspx.
then you will need to use
property1Txt.DataBindings.Add("Text", model1, "Property1", false, DataSourceUpdateMode.OnPropertyChanged );
One way you can do to achieve this is by using Reflection.
Another way, is by using the controls' DataBindings property.
Take a look at these, take your time to understand the concepts and come back if you have any questions. In a new question, of course :)
Happy learning!
Related
I have a Silverlight/WPF application I'm enhancing. I have a UserControl and it needs to disable or enable some controls when a property on the model changes. It has to do some other logic, so I can't just bind them to the property.
In the control's code behind, I have a reference to the model. I know there is a way to bind to certain properties, and I know how to do it in XAML, but not in the code-behind.
I've seen a lot of instances say to use the INotifyPropertyChanged interface, but it doesn't seem to apply in this case.
An example of what I'm trying to do:
public partial class MyControl : UserControl
{
private readonly MyModel _model;
public MyControl(MyModel model)
{
_model = model;
// bind to model's ImportantThing property here
}
...
// Some method gets called when property changes
...
}
public class MyModel
{
...
public bool ImportantThing
{
get { return _importantThing; }
set
{
_importantThing = value;
// This is existing code and notifies some controls, but not the ones
// I'm interested in. It should notify MyControl as well. I know in
// most applications, this is OnPropertyChanged();
RaisePropertyChanged("ImportantThing");
}
}
}
Any pointers?
Some Pointers....
Your issue\solution sounds like a task for a ValueConverter. But first, I can see code in the UserControl code-behind file, you really should adopt and apply the MVVM pattern... OK there is a [steep] learning curve and sometimes you wonder if it's worth the effort (know I did when I started with XAML)... But take my word for it.... MVVM, there simply in no other way to develop using WPF. If you try to apply the WinForms UI Logic to WPF it will become an unmaintainable, unmanageable monolithic pile of spaghetti code....
you might find this link to Rachel Lim's Blog useful....
https://rachel53461.wordpress.com/category/mvvm/
and for ValueConverter take a look at this.....
http://www.wpftutorial.net/ValueConverters.html
I apologize, my original question wasn't all that clear, but I've found a solution. It turns out the UserControl (MyControl in my original example) was already watching the Model for changes:
_myModel.PropertyChanged += Model_PropertyChanged;
In the existing callback (Model_PropertyChanged()), I just looked for the property I was interested in and added everything else I needed:
void Model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "StatusEnabled")
{
// do stuff
}
else if (e.PropertyName == "ImportantThing")
{
// my stuff
}
}
Thanks for everyone's input!
I'm new on Caliburn Micro and want some advice on which path to take to devolop my app interface and navigation between views.
My idea is to have a MainWindow which will contain a menu of buttons, each one related with a specific view. Each view will be stored in a separated WPF UserControl. The mainWindow will also contain a TabControl bound to an ObservableCollection of tabs on viewmodel. Everytime a button on menu is clicked, I want to add a new tab with a ContentPresenter inside that will dynamically load a view and its corresponding viewmodel.
So my questions:
1) Should I use a Screen Collection here?
2) Should the UserControl implement Screen interface?
3) How do I tell MainWindow ViewModel which view to load on the new added tab maintaining viewmodels decoupled?
Thanks to everyone in advance.
UPDATE
After a lot of reading and some help of the community I managed to resolve this. This is the resultant AppViewModel:
class AppViewModel : Conductor<IScreen>.Collection.OneActive
{
public void OpenTab(Type TipoVista)
{
bool bFound = false;
Screen myScreen = (Screen)Activator.CreateInstance(TipoVista as Type);
myScreen.DisplayName = myScreen.ToString();
foreach(Screen miItem in Items)
{
if (miItem.ToString() == myScreen.ToString())
{
bFound = true;
ActivateItem(miItem);
}
}
if (!bFound) ActivateItem(myScreen);
}
public ObservableCollection<MenuItem> myMenu { get; set; }
public ObservableCollection<LinksItem> myDirectLinks { get; set; }
public ICommand OpenTabCommand
{
get
{
return new RelayCommand(param => this.OpenTab((Type) param), null);
}
}
public AppViewModel()
{
OpenTab(typeof(ClientsViewModel));
MenuModel menu = new MenuModel();
myMenu = menu.getMenu();
myDirectLinks = menu.getLinks();
}
public void CloseTab(Screen param)
{
DeactivateItem(param, true);
}
}
I have to keep the ICommand from OpenTabCommand because the name convention of Caliburn.micro doesn't seems to work inside DataTemplate. Hope it could help someone else. Thanks to all
I've done something very similar using Caliburn.Micro, and based it on the SimpleMDI example included with the examples, with a few tweaks to fit my needs.
Much like in the example, I had a main ShellViewModel:
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
{
}
with a corresponding ShellView containing a TabControl - <TabControl x:Name="Items">, binding it to the Items property of the the Conductor.
In this particular case, I also had a ContextMenu on my ShellView, bound (using the Caliburn.Micro conventions), to a series of commands which instantiated and Activated various other ViewModels (usually with a corresponding UserControl, using the ActivateItem method on the Conductor.
public class YourViewModel: Conductor<IScreen>.Collection.OneActive
{
// ...
public void OpenItemBrowser()
{
// Create your new ViewModel instance here, or obtain existing instance.
// ActivateItem(instance)
}
}
In that case, I didn't require the ViewModels to be created with any particular dependency, or from any other locations in the program.
At other times, when I've needed to trigger ViewModel from elsewhere in the application, I've used the Caliburn.Micro EventAggregator to publish custom events (e.g. OpenNewBrowser), which can be handled by classes implementing the corresponding interface (e.g. IHandle<OpenNewBrowser>), so your main ViewModel could have a simple Handle method responsible for opening the required View:
public class YourViewModel: Conductor<IScreen>.Collection.OneActive, IHandle<OpenNewBrowser>
{
// ...
public void Handle(OpenNewBrowser myEvent)
{
// Create your new ViewModel instance here, or obtain existing instance.
// ActivateItem(instance)
}
}
This section of the documentation will probably be useful, especially the Simple MDI section.
Additional code I mentioned in the comments:
I sometimes use a generic method along these lines ensure that if I have an existing instance of a screen of a particular type, switch to it, or create a new instance if not.
public void ActivateOrOpen<T>() where T : Screen
{
var currentItem = this.Items.FirstOrDefault(x => x.GetType() == typeof(T));
if (currentItem != null)
{
ActivateItem(currentItem);
}
else
{
ActivateItem(Activator.CreateInstance<T>());
}
}
Used like:
public void OpenBrowser()
{
this.ActivateOrOpen<BrowserViewModel>();
}
I hope you guys can help me out as I can't find anything useful that helps with the understanding of my problem:
I'm trying to realize a passive MVP approach on my C# WinForms application which has list views and corresponding detail views.
So far I've got the following structure (pseudo code):
ListPresenter(new Repository(), new ListView(), new DetailPresenter(new DetailView());
Implementation:
public class UserDetailPresenter : IPresenter<IUserDetailView>
{
private IDetailView _view;
public UserDetailPresenter(IDetailView detailView)
{
_view = detailView;
}
public void Show(IUser user)
{
InitializeView(user);
_view.Show();
}
}
public class UserListPresenter
{
//private members (_userRepo, _listView, _detailPresenter)
public UserListView(IUserRepository userRepo, IListView listView, IDetailPresenter detailPresenter)
{
//wire up private members..
_listView.EditCommandFired += this.ShowEditForm;
}
private void OnListViewEditCommandFired(object sender, EventArgs args)
{
_detailPresenter.LoadUser(_listView.SelectedUser);
_detailPresenter.Show(); //modal
}
}
public class UserListForm : Form, IUserListView
{
public event EventHandler EditCommandFired;
public IUser SelectedUser { get { return gridView.FocusedRowHandle as IUser; } }
public void LoadUsers(List<IUser> users)
{
gridView.DataSource = users;
}
// other UI stuff
}
My problem is: I can only show the edit form once. As soon as I try to open it for a second time my View (the form) is disposed (System.ObjectDisposedException).
How do I fix that? Do I have the wrong approach here? Do I either cancel the form's close and just hide it and trust the garbage collector to collect it once the DetailPresenter is disposed? Do I create (new() up) a new presenter each time the Edit event is fired? I would then have to introduce some kind of factory as I somehow lose dependency injection. I'd appreaciate if someone could point out how the best practice in this case would look like and what I may be doing wrong here..
I was doing Winforms MVP a while ago so not sure if I can help, but the case my be as follows.
In my approach, the view was owning presenter, pseudo code:
MyForm form = new MyForm(new PresenterX);
form.Show(); //or showdialog
In this case instance is still there after closing.
In your case since presenter owns the view, its possible that once presenter is not used, GC disposes presenter and contained view.
Or even if presenter is still in use, since view is private GC may collect it once closed.
Try to debug in Release mode and see what happens with closed form instance.
EDIT:
Other idea is:
Create instance of view first and then pass to presenter
So approach that may fail (I don' see full code so guessing)
UserDetailPresenter p = new UserDetailPresenter(new YourView());
Try
YourForm view = new YourForm(); //as global variable, view should be reusable anyway
Somewhere in code
UserDetailPresenter p = new UserDetailPresenter(view);
p.Show(userInstance);
You're using one instance of DetailPresenter to show details for different objects. So you'll have to initialize the view of the DetailPresenter each time you want to show it, in your current implementation. This could be one way of doing it, the ListPresenter can inject a new instance of DetailsView everytime it asks the DetailPresenter to show it.
public class UserDetailPresenter : IPresenter<IUserDetailView>
{
private IDetailView _view;
public UserDetailPresenter()
{
}
public void Show(IUser user, IDetailView detailView)
{
_view = detailView;
InitializeView(user);
_view.Show();
}
}
Or another cleaner way could be some sort of ViewFactory to get a new instance of the view before showing it.
private IDetailViewFactory _detailViewFactory;
public UserDetailPresenter(IDetailViewFactory detailViewFactory)
{
_detailViewFactory = detailViewFactory;
}
public void Show(IUser user )
{
_view = _detailViewFactory.Resolve();//Some method to get a new view
InitializeView(user);
_view.Show();
}
But if you want to do it a bit differently, this is more passive view way.
In the ListPresenter:
private void OnListViewEditCommandFired(object sender, EventArgs args)
{
_listView.Show(_listView.SelectedUser);//tells view to show another view
}
In the ListView:
public ListView()
{
new ListPresenter(this); // initializes presenter
}
public void Show(IUser user)
{
new DetailsView(user); // creates a new view
}
In the DetailsView:
public DetailsView(IUser user)
{
new DetailsPresenter(this, user); //creates presenter
}
Finally:
public class UserDetailPresenter : IPresenter<IUserDetailView>
{
private IDetailView _view;
public UserDetailPresenter(IDetailView detailView, IUser user)
{
_view = detailView;
LoadUser(user);
_view.SomeProperty = _userData;//to populate view with data
_view.Show(); // tells the view to show data
}
}
I am having a problem understanding how to propagate a property changed event in a Model class up through the ViewModel and into the view. I am trying to conform to the MVVM pattern so please keep that in mind.
I have a Model that I am trying to expose by the ViewModel. My Model class queries an Api call to get the server status and exposes that status in public properties. Ex:
public class ServerStatusRequest : ApiRequest
{
//Exposable properties by request
public ServerStatusHelperClass Status { get; set; }
Where ServerStatusHelperClass is just a wrapper for the combined results in the query:
public class ServerStatusHelperClass
{
public bool ServerStatus { get; set; }
public int OnlinePlayers { get; set; }
The cool thing about my ApiRequest base class is that it checks the cache time of a particular Api call and updates the Results by using a System.Timers.Timer. So, for example, the ServerStatus Api call is cached for 3 minutes on the Api, so every 3 minutes my ServerStatusApiRequest object will have fresh data for it. I expose a UpdatedResults event in all ApiRequest classes to notify when new data comes in.
Now I want my ViewModel to have an instance of ServerStatusApiRequest and bind to its ServerStatusHelperClass Status property and stay up to date with the changes every time the information is updated, but my view (for binding) can't know about my model, and thus, doesn't know about my UpdatedResults event in my ApiRequest class. How can I reflect that out to the View through my ViewModel? Am I doing something completely weird here?
Here is what I have that is semi-working but I feel is a very hacky solution:
In my ViewModel:
public const string EveServerStatusPropertyName = "EveServerStatus";
private ServerStatusRequest _eveServerStatus = new ServerStatusRequest();
public ServerStatusRequest EveServerStatus
{
get
{
return _eveServerStatus;
}
set
{
//if (_eveServerStatus == value)
//{
// return;
//}
//RaisePropertyChanging(EveServerStatusPropertyName);
_eveServerStatus = value;
RaisePropertyChanged(EveServerStatusPropertyName);
}
}
public void UpdateEveServerStatus(object sender, EventArgs e)
{
EveServerStatus = (ServerStatusRequest)sender;
}
And in the ViewModels constructor I subscribe to the Model's event:
EveServerStatus.UpdatedResults += new UpdatedResultsEventHandler(UpdateEveServerStatus);
As you can see, this seems extremely redundant. And I also ran into a problem where I had to comment out the check in the setter for EveServerStatus because at that point the _eveServerStatus was already updated to value just without it knowing and I wanted to fire the event anyway.
I fell like I'm missing a key concept here to link this all together much more easily.
Thanks for any input.
I have come across a much better way to implement the behavior I was looking for. Here is the code in my ViewModel:
private ServerStatusRequest _eveServerStatus = new ServerStatusRequest();
public ServerStatusRequest EveServerStatus
{
get
{
return _eveServerStatus;
}
}
No setter as my ViewModel nor my View should be changing this data. And Inside my ServerStatusRequest class I have a property exposing the ServerStatusHelperClass object as shown in the Question. I have changed the ServerStatusHelperClass and made it implement INotifyPropertyChanged as so:
public class ServerStatusHelperClass : ObservableObject
{
private bool _serverStatus;
public bool ServerStatus
{
get
{
return _serverStatus;
}
set
{
_serverStatus = value;
RaisePropertyChanged("ServerStatus");
}
}
...
ObservableObject is just a simple class that implements INotifyPropertyChanged for me from mvvmlight.
By doing this my View is automatically updated when my ApiRequest class modifies it's ServerStatusHelperClass object.
Input on this solution is welcome.
I'm trying to use MVP and I notice that my View must know Model that should not happen in MVP I presume.
here is example:
public partial class TestForm : Form, ITestView
{
public void LoadList(IEnumerable<AppSignature> data)
{
testPresenterBindingSource.DataSource = data;
}
}
public interface ITestView
{
event EventHandler<EventArgs> Load;
void LoadList(IEnumerable<AppSignature> data);
}
public class TestPresenter
{
private ITestView view;
public TestPresenter(ITestView view)
{
this.view = view;
view.Load += View_Load;
}
private void View_Load(object sender, EventArgs e)
{
var data = // get from model
view.LoadList(data);
}
}
and the problem is that in TestForm I need reference to AppSignature.
In all tutorials I saw, there are some simple examples like
public void LoadList(IEnumerable<String> data) where there is no need reference to model. But how i.e DataGridView can publish current row data?
Your form is a View, it is not a Presenter. Thus it should implement interface ITestView:
public interface ITestView
{
event EventHandler Load;
void LoadList(IEnumerable<AppSignatureDto> data);
}
And your Presenter is someone, who subscribes to view's events and uses view properties to read and update view:
public class TestPresenter
{
private ITestView view;
public TestPresenter(ITestView view)
{
this.view = view;
view.Load += View_Load;
}
private void View_Load(object sender, EventArgs e)
{
List<AppSignature> signatures = // get from model
List<AppSignatureDto> signatureDtos = // map domain class to dto
view.LoadList(signatureDtos);
}
}
And you form, as I already said, is a view, it does not know anything about presenter and model:
public partial class TestForm : Form, ITestView
{
public event EventHandler Load;
private void ButtonLoad_Click(object sender, EventArgs e)
{
if (Load != null)
Load(this, EventArgs.Empty);
}
public void LoadList(IEnumerable<AppSignatureDto> data)
{
// populate grid view here
}
}
How to deal with reference to domain classes? Usually I provide to view only simple data (strings, integers, dates, etc), or I create data transfer objects, which are passed to view (you can name them FooView, FooDto, etc). You can easily map them with something like AtoMapper:
List<AppSignatureDto> signatureDtos =
Mapper.Map<List<AppSignature>, List<AppSignatureDto>>(signatures);
The View may have knowledge of Model as long as the interaction is limited to data binding only. i.e. View should not try to manipulate Model directly. View will always redirect user input to Presenter and Presenter will take care of further actions. If any action performed by Presenter results in a change in state of Model, Model will notify View via data binding. Model will be completely unaware of existence of View.
Is it OK to get DataSource in Presenter and there set its DataSource ?
e.g.
Presenter code:
Public void LoadData()
{
_view.Data.DataSource = Business.GetData().ToList();
}
Form code:
Public BindingSource Data
{
get
{
return this.bsData;
}
}
Thanks to that I dont need to add any references to the View, but I didn't see that solution in any other sources.