C# using MVVM - Difficult Behavior [closed] - c#

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
After spending hours (maybe days) grinding trying to solve, I decided to put my faith in your knowledge. I want to implement this behavior (blue):
So in the loginButton I fire a command that is handled in the loginViewModel in this handler I do some verifications to a webservice and using that answer I want to open a new Window sending an User (class). How can I do this?
I've tried messaging, I've tried a lot of things. In code behind is something like this that I want to do using MVVM architecture.
LoginView Code-Behind
button_OnClick(){
// Checking stuff
var u = //from the server;
PrincipalView pv = new PrincipalView(u);
pv.Show();
this.Close()
}
In the PrincipalView Code-Behind:
public PrincipalView(User u){
// Yey, I have the user
}

I have done that by using a custom window service class like following:
class WindowsService
{
private static LoginWindow loginWindow{ get; set; }
private static UserWindow UserWindow{ get; set; }
public void ShowLoginWindow(LoginViewModel loginViewModel)
{
LoginWindow = new LoginWindow
{
DataContext = loginViewModel
};
LoginWindow.Show();
}
public void ShowUserWindow(UserViewModel userViewModel)
{
UserWindow = new UserWindow
{
DataContext = userViewModel
};
LoginWindow .Hide();
UserWindow.Show();
}
}
So you declare an instance of WindowsService in your LoginViewModel, and when your logic find the user you create an instance of your UserViewModel and call windowsService.ShowUserWindow(userViewModel). In order to use this properly, you have to modify your App.xaml.cs file like this:
public partial class App : Application
{
private void App_OnStartup(object sender, StartupEventArgs e)
{
var loginViewModel = new LoginViewModel();
loginViewModel.StartLoginWindowService();
}
}
The method StartLoginWindowService() may look like this:
public void StartLoginWindowService()
{
WindowsService.ShowLoginWindow(this);
}
Please let me know if this helps
Explaining where to create UserWindowViemodel
Suppose the following method in your loginViewmodel
public void Loging(string name, string pass){
var isAllowed(name, pass); //Check if user exists and if pass is correct
if(!isAllowed) return; //we return if user is invalid
var wService = new WindowsService();
var myUser = new UserWindowViewModel(name){
//you set all proeperties you need here
}
wService.ShowUserWindow(myUser);
}

You probably need a service which will open a window when you pass it a ViewModel.
Basically, you will have a ViewLocator which will find the specified View for you when you give it a ViewModel. The UIService have methods such as Show and ShowDialog to which you can pass any ViewModel. These methods will then open the registered view and also assign a new UIService for the newly created ViewModel.
I have described the process in detail in this answer.

Related

Method, Delegate and Event Subscription Help in Autodesk Inventor Add-In

I'm creating an add-in for Autodesk Inventor. Basically, you define the buttons you want to add, and tell the app to add the button definitions. The problem I'm having is that when I define the 'OnExecute' method for the button definition, the button doesn't execute. I think the way I'm trying to organize the code is what's creating the problem.
I have a CustomButton class that has a delegate property that looks like this (the signature is void with an input of a NameValueMap Interface)
public class CustomButton
{
// … properties and methods that don't matter here
public ButtonDefinitionSink_OnExecuteEventHandler Execute { get; set; }
}
In the main Activate() method (what's called when Inventor starts) I create an instance of the following class to set all the button definitions and the methods that fire when they are clicked. That class looks like this:
public class CustomButtonDefinitions
{
public CustomButtonDefinitions(ref Application app)
{
_inventorApp = app;
InitializeButtonDefinitions();
}
public List<CustomButton> CustomButtons { get; set; } = new List<CustomButton>();
private void InitializeButtonDefinitions()
{
AddTestButton();
}
private void AddTestButton()
{
var testButton = new CustomButton
{
DisplayName = "test",
InternalName = "testCommand1",
Ribbon = "Assembly",
RibbonPanel = "Simplification",
IconSource = "./Assets/test.jpg",
Classification = CommandTypesEnum.kFileOperationsCmdType,
ShowText = true,
UseLargeIcon = true,
};
testButton.Execute = TestButton_Execute;
CustomButtons.Add(testButton);
}
private void TestButton_Execute(NameValueMap Context)
{
// This is where the logic of the button would go.
// For now, just something that gives me an indication it worked.
System.Windows.Forms.MessageBox.Show("Hello");
_inventorApp.ActiveDocument.Close();
}
}
Where I think the source of the error comes from is the next code (this is in the Activate():
CustomButtonDefinitions customButtonDefinitions = new CustomButtonDefinitions(ref _InventorApp);
foreach (var button in customButtonDefinitions.CustomButtons)
{
// this creates the button in Inventor
var buttonDef = button.CreateButtonDefinition(ref controlDefs);
// and this subscribes the button click event to my method
buttonDef.OnExecute += button.Execute;
}
There must be something un-subscribing my method from the button click event.
I'll be posting this in the Inventor forums as well, but wanted to check here too since I'm new to delegates and event handlers. I'm either not understanding something about delegates/events or it's something Inventor specific that I'll need some other help with.
Hopefully this is enough to give some context. Thanks in advance.
The problem was the fact that I wasn't creating the button definition in a high enough scope. I needed to create a variable above the scope of the Activate() method so the app could see it when needed.

how to implement ICommand on a login page [duplicate]

This question already has answers here:
How can I use the RelayCommand in wpf?
(2 answers)
Closed 8 years ago.
I'm fairly new to MVVM and I recently discovered that buttons could be bound to functions in classes as well.
So what I've been doing basically is authenticating usernames and passwords using Linq using the click event in the code behind like this:
private void Login_Click(object sender, RoutedEventArgs e)
{
var user = from u in MyDBDataSet.logins
where u.username == usernameTextBox.Text
&& u.password == passwordPasswordBox.Password
select u;
if (user.Any())
{
MessageBox.Show("Success, Credentials Found");
}
else
{
MessageBox.Show("Error, Nothing Found");
}
}
In order for me to use the MVVM pattern I'd end up having to do something like this right?
<!-- Create the binding in the xaml />
<Button Content="Login" Command="{Binding login}"/>
Set the data context in the code behind
public login()
{
InitializeComponent();
// LoginSQL is the View Model class
LoginSql = new SQL.content.authentication.login.loginSQL();
DataContext = LoginSql;
}
And in the View Model, create the property to bind the button to.
private ICommand _login;
public ICommand login
{
get;
set;
}
See this is where my knowledge of it is very limited. From looking around here, I got the understanding that I need to tell it under which conditions it can execute or the button will be greyed out when the app is run, and I should also tell it what to do when it executes.
At the moment that's what's failing me though. How do I hook up the method to log the user in to the ICommand property?
You will need to create/obtain a common MVVM pattern base-class called DelegateCommand. Basically this class implements ICommand allows you to specify delegates for the Execute() and CanExecute() methods in the ICommand interface.
The PRISM Library contains an implementation of it and so does Kent Boogaart's blog

Best way to store user's input in a multi-step application [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
My program consists of a number of steps. Say each step is in a tabpage on which upon completion, the user selects "Next".
What is the best/fastest way of storing these information?
I would generally say that this would be one Model to keep all the data - which then gets forwarded to the processing method once the user says "ok" at the last page. BUt then this depends a lot on actual usage scenarios, so that may not be the best way all the time.
I created a separate class that had properties for the 'answers' on the individual pages. Not sure if this is the best way to do it, but it worked perfectly for me. The user could go back and forth between the pages and the answers were stored. To Prescott's point, if they need to come back later perhaps you could save their partial form out as XML.
Create a viewmodel that represents the wizard, and in that viewmodel all the data related to your steps. You can also keep track of current step, and implement some methods to enable/disable next and previous buttons etc.
Your actual wizard window and tabs (the views) then all work with the wizard viewmodel (the data).
I would go with In-Memory Repository if you just want to retain this information for program execution, For example
namespace Repository {
public class InMemoryRepository : IRepository{
static readonly List<Item> items = new List<Item> {
new Item("CurrentTaxRate", 20m),
new Item("CurrentAmount", 100m)
};
public Item Get(string name) {
return items.Single(x => x.Name == name);
}
}
}
public class Item {
public Item(string name, decimal value) {
Name = name;
Value = value;
}
public string Name { get; private set; }
public decimal Value { get; private set; }
}
You can bolt on Add, Delete or Edit functionality easily to this and replace the "Item" here with your own "UserData" type.
Use dependency injection for getting this information back
public class TaxRate : ITaxRate {
private readonly IRepository repository;
public TaxRate(IRepository repository) {
this.repository = repository;
}
public decimal Get() {
var item = repository.Get("CurrentTaxRate");
return item.Value;
}
}

How to pass to a Generic and Pass from a Generic to another class?

When I log into my application, I pass the cUserEntity class which holds all the details of the logged in user, including UserID and Username, to the Dashboard form. From here, I can continue to pass the details around from class to class and form to form. For example (Ignore the generic bit in this example):
Login:
xamlDashboard Manager = new xamlDashboard(_loggedInUser);
Generic Gen = new Generic(_loggedInUser);
Manager.Show();
Dashboard:
cUserEntity _loggedInUser;
public xamlDashboard(cUserEntity loggedInUser)
{
InitializeComponent();
_loggedInUser = loggedInUser;
}
However, I have a Generic.Xaml page which creates a button at the top of every window. Behind the Generic.xaml is a Generic class, which holds the click_event for the created button. The click_event opens a new window to another form.
Now, I need that other form to have the logged in user details, and to do that, I assume I need to pass to the Generic.Xaml and then pass from there to the new form via the click_event. However, as I've read up and noticed, it doesn't seem to be possible as you can't pass a type to a Generic during runtime.
What I hoped to achieved (which failed):
public partial class Generic
{
cUserEntity _loggedInUser;
public Generic(cUserEntity loggedInUser)
{
_loggedInUser = loggedInUser;
}
private void btnHelp_Click(object sender, RoutedEventArgs e)
{
xamlHelp help = new xamlHelp(_loggedInUser);
help.Show();
}
}
Therefore, what is the best and most efficient method to be able to do this, and would appreciate examples, if possible.
It would be alot simpler to create a singleton object to store your logged in user...
public class UserAccount
{
private static User _currentUser;
private UserAccount() {}
public static User CurrentUser
{
set
{
_currentUser = value;
}
get
{
return _currentUser;
}
}
}
Then after login you would do this...
// Set the current User
UserAccount.CurrentUser = user;
Then in any class you need the currently logged in user... you could do...
var user = UserAccount.CurrentUser;
Obviously you would need to implement your own business rules around this but the concept is what I am trying to get across here, a static single instance of the user that can be accessed from anywhere.

List+Detail - Best Approach?

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

Categories

Resources