How to define implementation of event to viewmodel? - c#

I want to centralized all bussiness logic into viewmodel. but i have problem with implemetation event. so i think i create delegate in code behind for reference. and the implementation in view model by pass view object to view model. how to do this?
<UserControl x:Class="Project.Views.IndexView">
<PasswordBox PasswordChanged="PasswordChangedHandler"/>
</UserControl>
C#
public partial class IndexView
{
public IndexView()
{
InitializeComponent();
DataContext = new IndexViewModel(this);
}
private delegate void PasswordChangedHandler(object sender, RoutedEventArgs args);
}
public class IndexViewModel
{
public IndexViewModel(UserControl view)
{
view.PasswordChangedHandler = this.PasswordChangedHandler;
}
public string Password { get; set; }
private void PasswordChangedHandler(object sender, RoutedEventArgs args)
{
var passwordBox = (PasswordBox)sender;
Password = passwordBox.Password;
}
}

If you want to stay out of code behind you are going to have to look into implementing EventToCommand: Support in Framework, and Core as long as you reference Windows.Interactivity.WPF Nuget
Bind command to Loaded event of view
MVVM Light: Adding EventToCommand in XAML without Blend, easier way or snippet?
https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/may/mvvm-commands-relaycommands-and-eventtocommand

"I want to centralized all bussiness logic into viewmodel." - Business logic belongs to the Model (but I guess you know this).
"by pass view object to view model. how to do this?" - You should never pass an object of the View to the View Model.
If your real problem is "How to pass the password to the view model", then you should know that you need to pass it explicitly. This is how the control is designed to be used.
The reason for this is, that the password is a very critical information. Passing the password around the application always introduces a privacy or security issue. Strings are stored in the memory as their simple byte representation/plaintext. The memory is public. This means, everybody can read a string from the memory. That's why the PasswordBox exposes the password as SecureString.
The general recommendation is to avoid this classic authentication system where the user enters a plaintext password.
The recommendation is to use existing authentication flows.
For example, you can use Windows authentication. By storing application data in the user domain, you can be sure that only the user that is currently logged in is using your application: Windows authentication ensures that a user of a instance-per-user desktop application is properly authenticated.
Alternatively, use OAuth authentication.
Modern web applications ask the user to login with their Google or Facebook account etc. Those authentication flows are very secure. The password handling is completely external as it is delegated to a 3rd party service (on behalf of the user that must have a related account). This service will maintain the implemented authentication procedure to keep it secure up to the latest security standards.
Although I highly recommend against implementing a custom authentication flow, the following example is meant to show you how PasswordBox is intended to be used:
MainWindow.xaml
<Window>
<PasswordBox PasswordChanged="OnPasswordChanged" />
<Window>
MainWindow.xaml.cs
partial class MainWindow : Window
{
private ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
this.ViewModel = new ViewModel();
this.DataContext = this.ViewModel;
}
private void OnPasswordChanged(object sender, RoutedEventArgs args)
{
var passwordBox = sender as PasswordBox;
this.ViewModel.HandlePassword(passwordBoc.SecurePassword);
}
}
The general practice is to ensure that the desktop application is installed as instance-per-user (so that we can rely on the OS authentication system) or to use a OAuth service. OAuth is very popular for web applications where an URL can be visited from any system (which declares local OS level authentication as meaningless).

Related

How can I add user's name in WPF?

I'm creating a UI App on C# using .NET Core .
I have to register users and let them login. So, the question is: how may I welcome logged user? I want the following: "Welcome back, {UserName}".
I create new window with input of class User object, so I know where to get his/her name, but I'm not sure how to program adding the name in .xaml.
Would be grateful for any possible help!
From your description, you may created a wpf Application(net core 3.0)?
When you logged from the log form. You can show the "Welcome back, {UserName}" in the main form. Like the following simple code.
We can define a User object property in App class.
public partial class App : Application
{
public static Users usersd { get; set; }
}
And assign the User object when logging. Then, in the Main Windows/others form, you can show the user information:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (App.usersd != null)
{
label.Content = "Welcome back, {" + App.usersd.Name + "}";
}
}
In order to welcome users back, you need to store the user data in a database or in a file(.txt,.json,..). One way to do this is storing the user data as JSON when they first open the program. link
Then, you can check if the user has already logged in before. (perhaps use a userID property in User class)
Don't forget you need to add a NuGet package called Newtonsoft.Json in order to serialize and deserialize the data. Install NuGet package

WPF Prism User Object

I am new to WPF and MVVM Prism. I have been an ASP.NET developer for more than 5 years and recently switched to a WPF project.
I am currently using Prism 5.0 with Unity. The main purpose of following the pattern is to implement modularity and loose coupling.
My question is this: I would like to make my User Object universal and accessible across all modules.
This is what I've done so far. Upon start up, users are greeted with a login screen (LoginView.xaml) in Login project. LoginViewModel will then validate credentials. Upon successful validation, LoginViewModel will then pass this retrieved object to a static class in Infrastructure project. Since user login is only single / universal instance, I have created a static class under Infrastructure project to hold the user object.
I have tried GenericPrincipal, while it does persist data across views, it's not sophisticated enough to hold data that I need. Hence I went for static class instead.
Does anyone have a better suggestion around it?
Instead of registering your User object in a static class, I suggest you to register the User instance in the Unity container itself.
In your LoginViewModel, you should get an instance of your IUnityContainer class.
public LoginViewModel(IUnityContainer container)
{
Container = container;
}
In your Login method, you register your user object:
private void Login(object obj)
{
...
if (user.Authenticated)
{
Container.RegisterInstance("CurrentUser", user);
}
...
}
To access your object, you use the following code snippet:
Container.Resolve<YourUserClassHere>("CurrentUser");
For more details see:
Persisting user credentials in WPF w/Unity and MVVM

How can I use identical model data in two different view models?

I'm prototyping a WPF application making use of the MVVM pattern. The application shall have two windows: MainWindow and LoginWindow.
The Model contains two properties: Username and Password.
The LoginWindow is responsible for handling the username and password entered by the user, so the corresponding view model updates these properties. However, the MainWindow also needs access to the username and password for later usage with a client object.
How should I handle this?
Passing the instance of the Model created in the LoginViewModel to the MainWindowViewModel's constructor?
What you need is a Messenger/Event Aggregator. An event aggregator is a broker object that you can take a reference to and specify what type of events you want to receive, without having to take a reference or even be aware of the objects generating the events.
Prism's EventAggregator is the most common one. See: Event Aggregator
So:
ViewModel 1:
public ViewModel1(IEventAggregator eventAggregator)
{
_eventAggregator=eventAggregator;
}
private void SendMessage()
{
_eventAggregator.GetEvent<UserLogin>().Publish(new UserLogin(_userName,_password);
}
ViewModel 2:
public ViewModel2(IEventAggregator eventAggregator)
{
_eventAggregator=eventAggregator;
_eventAggregator.GetEvent<UserLogin>().Subscribe(UserLoginReceived,ThreadOption.BackgroundThread,true);
}
private void UserLoginReceived(UserLogin login)
{
//do what you like here
}
What's happening is that the eventaggregator is passed to both the viewmodels. ViewModel1 publishes a message but doesn't know who (if anyone) is listening to it. ViewModel2 has subscribed to the event and is listening for a publisher to send it a message.
Using this approach you can have your viewmodels communicate without them taking references out on each other.

Controlling access to methods

Is there a way to control access to methods to certain roles in .net. Like
class A
{
//should only be called by Admins**
public void Method1() { }
//should only be called by Admins and PM's**
public void Method2() { }
}
I'm using windows authentication only for retrieving user names and nothing more.User roles are maintained in a different application. I think it's possible through attributes but I'm not really sure how
It it possible, I have used it on an web project that used asp.net and AzMan as the authentication.
Take a look at Code Access Security
From memory all of our methods looked something like
[Permission(SecurityAction.Demand, "Permission")]
public void Method1
It's been a while though so that might not be actually 100% correct.
I'd also highly suggest if you are going to put protection down to this level to look at a task orientated permission approach as this is much more flexible than role based permissions
You can do this as follows:
class A
{
//should only be called by Admins**
[PrincipalPermission(SecurityAction.Demand, Role="Admin")]
public void Method1()
{
}
//should only be called by Admins and PM's**
[PrincipalPermission(SecurityAction.Demand, Role="Admin")]
[PrincipalPermission(SecurityAction.Demand, Role="PM")]
public void Method2()
{
}
}
To do this Thread.CurrentPrincipal must be set to a principal that has the required roles. For example, if you enable roleManager in an ASP.NET application, Thread.CurrentPrincipal will be set to a RolePrincipal with roles from your configured RoleProvider. See this MSDN article for more info.
You can do it using custom validation.
1- Make a method in another public class which take login id as parameter and return roles in form of bits.
2- Call this method on the page_Load event of the required class and save returned bits in the view state.
3- Now validate required method on the basis of roles bits.

Will the static public variables in my app get shared with other users in the same app?

For reasons I would rather not discuss, I need to create a custom authentication system for my app. I was just reviewing the system and am having some doubts if my solution is thread safe. My goal was to create a solution that would allow my app to authenticate a user one time and that users authentication info would be shared by all master pages, pages, classes, user controls, etc that are used. (But not share the same info between users)
Here is my setup:
PageHttpModule.cs - this is added to the web.config as a httpModule.
public class PageHttpModule : IHttpModule
{
public void Init(HttpApplication app)
{
app.AuthenticateRequest += new EventHandler(OnAuthenticateRequest);
}
public void OnAuthenticateRequest(Object s, EventArgs e)
{
CurrentUser.Initialize();
}
public void Dispose() { }
}
CurrentUser.cs
public static class CurrentUser
{
public static bool IsAuthenticated { get; private set; }
public static string Email {get; set;}
public static string RealName {get; set;
public static string UserId {get; set;}
public static void Initialize()
{
CurrentUser.AuthenticateUser();
}
Note: this is a scaled down version of my authentication code.
public static void AuthenticateUser()
{
UserAuthentication user = new UserAuthentication();
user.AuthenticateUser();
if (user.IsAuthenticated)
{
CurrentUser.IsAuthenticated = true;
CurrentUser.UserId = user.UserId;
CurrentUser.Email = user.Email;
CurrentUser.RealName = user.RealName;
}
}
}
UserAuthentication.cs
public class UserAuthentication
{
public string Email { get; set; }
public string RealName { get; set; }
public string UserId { get; set; }
public bool IsAuthenticated { get; private set; }
public UserAuthentication()
{
IsAuthenticated = false;
Email = String.Empty;
RealName = String.Empty;
UserId = String.Empty;
}
public void AuthenticateUser()
{
//do some logic here.. if the user is ok then
IsAuthenticated = true
Email = address from db
UserId = userid from db;
Realname = name from db;
}
}
I have tested between 3 different browsers and it seems to work fine, but I am still learning and don't want to make a huge mistake.
If my logic is totally wrong, then how should I do it so I dont have to put user lookups on every page directly?
No, this is not thread-safe. For instances of the application living in separate processes or AppDomains, this will be just fine. But if your ASP.NET server is going to serve multiple requests at once using threading, you are going to have some very bad side effects if two people try to use the application at the same time.
In the Init method, the HttpApplication parameter is described as:
An HttpApplication that provides access to the methods, properties, and events common to all application objects within an ASP.NET application
The key here is that there is one PageHttpModule for the lifetime of the app, and all static objects that exist in the lifetime of the app will share those variables.
BUT... the lifetime of CurrentUser is only within the scope of the OnAuthenticateRequest event, unless some other reference keeps the object alive. If it were a PageHttpModule member-level variable, you'd have issues that you would have noticed immediately. In your situation, however, you'll work fine so long as you don't get more than one simultaneously-processed OnAuthenticateRequest call.
The answer to your question is no, you're not guaranteed to be thread-safe. If two authentication requests come in simultaneously, you're not guaranteed to have one event complete before the other begins, in which case the second user can appear authenticated, when it's really the first user that was logged on.
Update
I think part of the problem is coming from a misunderstanding of AuthenticateRequest... By the time this event is called, the user has already been authenticated by either Windows or Forms authentication... you're just getting notified that it's happened. In fact, the property User.Identity.IsAuthenticated has already been set (I believe this event fires even if the user fails authentication, but I won't swear to that without double-checking).
If I understand what you are after, you're really trying to write your own custom membership provider. If you take this approach, you will have all the benefits of the built-in authentication... all of the standard properties related to authentication will be set and accessible, and will be isolated to a user's session in the manner you want.
Writing a custom provider is not a small feat, but it is doable, and you should be able to reuse a lot of the logic and code you're currently using for your classes.
Trying to completely re-write the authentication mechanism would be jumping through painful, complicated hoops.
Some links:
http://www.devx.com/asp/Article/29256/0/page/3
http://www.codeproject.com/KB/aspnet/WSSecurityProvider.aspx
http://msdn.microsoft.com/en-us/library/f1kyba5e%28v=VS.90%29.aspx
The properties you must implement may look daunting, but unless you need a specific functionality (such as ResetPassword), you can simply throw a NotImplementedException. Code only what you'll use.
Why not just do it the way microsoft recommends?
http://msdn.microsoft.com/en-us/library/9wff0kyh.aspx
I've done custom authentication this way and it works fine.
Here is another link which should prove useful:
Link
What you have done with IHttpModule seems like a good path to tackle this kind of issue. One of the purposes of the http module as stated by microsoft is to enable for any kind of special authentication. When http module intializes it uses the same instance for new requests. Since you dont have any global variables I am not so sure how to address your thread safe question. It seems like you are onlu reading some data out, so please elaborate!

Categories