Pass parameter from viewmodels to another - c#

When I searched,I found,how to bind values from viewmodel to view but not viewmodel to viewmodel.can anyone help me to do that. what i need is to pass Authentication to other viewmodel.I am new in the MVVM world so please give me more detail.
my ViewModel look like this
public class ModelView_Authentication : INotifyPropertyChanged
{
//Binding authentication
private Authentication _authentication;
public Authentication authentication
{
get { return _authentication; }
set
{
_authentication = value;
NotifayPropertyChanged("_authentication");
}
}
//Command Button
public ModelView_Authentication()
{
authentication = new Authentication();
ButtonCommand = new ViewModdelCommand(exeMethode, canexeMethode);
}
public ICommand ButtonCommand { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private bool canexeMethode(Object param)
{
return true;
}
//run this Command Onclick Button
private void exeMethode(Object param)
{
}
protected void NotifayPropertyChanged(string s)
{
PropertyChangedEventHandler pc = PropertyChanged;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(s));
}
}
//Run Assync Login
public static async Task<string> main(Authentication authentication)
{
var tocken = await Login.GetConnection(authentication);
return tocken.ToString();
}
}

need is to pass Authentication to other viewmodel
Your main ViewModel adheres to INotifyPropertyChanged, you can have your other VMs subscribe to the notification process of the main VM and acquire changes to specific properties as needed.
Just have a reference to the main VM, it is as easy as that. Where the VMs get their references, that process is up to you.
A good place is on App class. Since the App class is known throughout each of the namespaces, setup up a static property on it, set it after the main VM is created, and then access the it as needed.
public static ModelView_Authentication AuthVM { get; set; }
the access such as
var mainVM = App.AuthVM;

Related

Access ViewModel in ObservableCollection by Underlying Model

I have an ObservableCollection that I bind to a ListView. The ObservableCollection contains ClientViewModels which are ViewModel wrappers for my Client model that contain UI-only properties.
The problem I am having, is let's say a Client connects/disconnects and an event is raised. If I want to update or remove the ClientViewModel from the ObservableCollection, I'd have to do some sort of LINQ hackery to search each ViewModel in the ObservableCollection, and then check if the Client inside the ClientViewModel is equal to the Client that connects/disconnected, and then modify the ViewModel.
Is there a more efficient way? Like having some sort of ObservableDictionary that can be accessed O(1)?
public ObservableCollection<ClientViewModel> ClientViewModels { get; } = new();
public void OnClientConnected(object sender, Client e)
{
ClientViewModels[...]?
}
public class ClientViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public Client Client { get; }
private bool _online;
public bool Online
{
get { return this._online; }
set
{
this._online = value;
this.NotifyPropertyChanged(nameof(Online));
}
}
public ClientViewModel(Client client)
{
Client = client;
}
public void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

How to communicate backend status messages to frontend?

I have a WPF project and a Console one, the point of the WPF is to be the frontend UI and the console application is the logic that does the actual work.
In my backend I have a class with a method that does the work.
public static class BackendClass
{
public static void DoWork(ref string output)
{
//actual work
}
}
From the MVVM frontend my view model starts a task for this method and I want to be able to show status messages on the frontend about it. Things like "Started work.", "Doing so-and-so.", "Finished." and etc.
The code in my view model is:
class ViewModel : INotifyPropertyChanged
{
static string backendOutput;
public string BackendOutput
{
get => backendOutput;
set
{
if (backendOutput != value)
{
backendOutput = value;
OnPropertyChanged("BackendOutput");
}
}
}
public RelayCommand ExecuteCommand { get; private set; }
Task executionTask;
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
executionTask = new Task(() => BackendClass.DoWork(ref BackendOutput));
}
void OnExecute()
{
executionTask.Start();
ExecuteCommand.RaiseCanExecuteChanged();
}
bool CanExecute()
{
return (executionTask.Status != TaskStatus.Running &&
executionTask.Status != TaskStatus.WaitingToRun);
}
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The "BackendOutput" property is data binded to a text block in the WPF window.
I was thinking of passing the "BackendOutput" property so the "DoWork" method can append its status messages to it, thus raising the changed event, updating the frontend.
However if I try to assign it outside of the constructor I get the error that a property can't be a field initializer or something like that and in this case I get "property can't be passed as a ref parameter".
So how should I alert the frontend of what status messages the back is pumping?
ViewModel communicates with View via PropertyChanged event. So Model also can have an event. ViewModel subscribes to that event, updates property with event data, View gets updated.
Events are kind of protected delegates. So as a first step try to introduce a delegate:
public static void DoWork(Action<string> notifier)
{
notifier("output value");
}
and
executionTask = new Task(() => BackendClass.DoWork(str => { BackendOutput = str; }));

Update ICommand after ViewModel creation

I am writing a WPF application to learn the MVVM Design Pattern. I am fairly new to C# and WPF.
I am trying to pass some context when switching ViewModels, that then gets used in an ICommand implementation to call a method. But the ICommand won't update after receiving the context.
Basically I create an instance of an ICommand, which a button binds to and then (when passing the context) I create another instance that replaces it.
My question then: is there a way to rebind a command binding or is the state it had at the time of intialization unmodifiable.
What I'm trying to accomplish in code:
Command.cs
public class Command : ICommand
{
public Command(Action action) => this.action = action;
public event EventHandler CanExecuteChanged;
public virtual bool CanExecute(object parameter) => true;
public virtual void Execute(object parameter) => action();
Action action;
}
ObservableObject.cs
public abstract class ObservableObject : INotifyPropertyChanged
{
protected virtual void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public event PropertyChangedEventHandler PropertyChanged;
}
FooModel.cs
public class FooModel
{
public int Number => 10;
}
BarModel.cs
public class BarModel
{
public int Number { get; set; }
}
BarViewModel.cs
public class BarViewModel : ObservableObject
{
public BarViewModel()
{
Bar = new BarModel();
BtnCommand = new Command(Reset);
}
public void Receive(object state)
{
if (state is FooModel foo)
{
Counter = Bar.Number = foo.Number;
// this won't reset the number to 10
BtnCommand = new Command(Reset);
// neither will this, why?
Reset();
}
}
public void Reset() => Counter = Bar.Number;
int counter;
public int Counter
{
get => counter;
set
{
counter = value;
OnNotifyPropertyChanged(nameof(Counter));
}
}
Command btnCommand;
public Command BtnCommand
{
get => btnCommand;
set
{
btnCommand = value;
OnNotifyPropertyChanged(nameof(BtnCommand));
}
}
BarModel Bar { get; private set; }
}
BarView.xaml
<UserControl
<! ... namespaces and such -->
>
<UserControl.DataContext>
<local:BarViewModel />
</UserControl.DataContext>
<Grid>
<Button Content="Click" Command="{Binding BtnCommand}"/>
</Grid>
</UserControl>
The Receive Method is invoked after creating the BarViewModel and it passes an instance of FooModel. When I set a breakpoint inside the if (state ...) block it says that the Bar.Number field is 10, but when it leaves the scope it's back to 0. I get a feeling that this is how it's supposed to work, but how can I accomplish the update of the Command context?
I tried to create a MCVE of my project, here's the link to dropbox. It's a VS 2017 Project using .NET 4.5.2
Screenshot of code in MCVE:
EDIT: changed fileupload URL
EDIT2: added screenshot
EDIT3: updated code
EDIT4: changed fileupload to dropbox
You are creating a new instance of the BarViewModel in your Bar view. Remove this XAML markup:
<UserControl.DataContext>
<local:BarViewModel />
</UserControl.DataContext>
Then the commands that you create in your Receive method should be invoked as expected.

DataBinding and UpdateSourceTrigger on Content

Okay, so I've been attempting to learn a bit of MVVM. It's been only three days and have come to a dead end. The application simply retrieves the client's external ip address and updates the new ip address in a label found in my UI. However, rather than just going the old-fashioned way of simply doing,
ipAdd.Content = getNewIp();
and ending the story end there. Rather, I decided to use some MVVM to achieve the following:
Retrieve ip address, then display it to the user in a label
Query the database and update the user's ip address
Rinse, repeat every five minutes.
The reason I chose to go more MVVM is to use UpdateSourceTrigger and UpdateCommand. Essentially, when the ip address gets updated, I want to do something with it (as mentioned above). I've seen in many tutorials where UpdateCommand is used on buttons and UpdateSourceTrigger used on input boxes, but nothing that shows how to do it with labels, yet it seems that it should be tied in to the same concept; it might not be explicity a button that is being clicked, but some back code that executes and performs the same task as a button. For starters, here's what I have so far:
Model
namespace IPdevices
{
class IP : INotifyPropertyChanged
{
private string _add;
public string add
{
get
{
return _add;
}
set
{
_add = value;
OnPropertyChange("add");
}
}
private void OnPropertyChange(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
ViewModel
namespace IPdevices
{
class IPViewModel
{
private IP _ip;
public IPViewModel()
{
UpdateCommand = new IPUpdateCommand(this);
}
public ICommand UpdateCommand
{
get; private set;
}
public bool CanUpdate
{
get
{
return true; // just say yes for now
}
}
public IP IP
{
get
{
return _ip;
}
set
{
_ip = value;
}
}
public void updateClientIp()
{
Console.WriteLine("_updating client ip and query database");
}
}
}
ICommand
namespace IPdevices
{
class IPUpdateCommand : ICommand
{
private IPViewModel _viewModel;
public IPUpdateCommand(IPViewModel vm)
{
_viewModel = vm;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public bool CanExecute(object parameter)
{
return _viewModel.CanUpdate;
}
public void Execute(object parameter)
{
_viewModel.updateClientIp();
}
}
}
MainWindow
public Main()
{
InitializeComponent();
ipAdd.DataContext = new IPViewModel();
}
View
<Label x:Name="ipAdd" Content="{Binding Path=IP.add}"/>
What I'm trying to achieve here can easily be done without going MVVM (perhaps overkill as well) and simply calling a few helper classes to update the user's ip on the db side, but for the sake of getting used to doing things MVVM, I tried incorporating the way I did, though with no luck.
Is there any way to do this, or even better, should it be done this way?

Communication between two viewmodels

I'm newbie in MVVM design pattern, and I have these viewmodels :
ClassAViewModel
public class ClassAViewModel : INotifyPropertyChanged
{
private int _nbre = 0;
public int Nbre
{
get
{
return _nbre;
}
set
{
_nbre = value;
PropertyChanged(this, new PropertyChangedEventArgs("Nbre"));
}
}
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
And ClassBViewModel
PUBLIC class ClassBViewModel: INotifyPropertyChanged
{
private Boolean _IsBiggerthanFive = false;
public bool IsBiggerthanFive
{
get
{
return _IsBiggerthanFive;
}
set
{
_IsBiggerthanFive = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsBiggerthanFive"));
}
}
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
I need to know if a mecanism of notification between two viewmodels exists , ie in my case if _nbre > 5 in the first viewmodel, the second viewmodel will be notified and the value of _IsBiggerthanFive will be changed. So:
How can two viewmodels communicate between them without instanciate one in the other ?
What is the best way to accomplish this task?
I agree with other commenters that the mediator/pub-sub/event aggregator/messenger is a good way to go. If you're not using an MVVM framework with a built-in solution, then I recommend this simple approach that takes advantage of the Reactive extensions:
public class EventPublisher : IEventPublisher
{
private readonly ConcurrentDictionary<Type, object> subjects
= new ConcurrentDictionary<Type, object>();
public IObservable<TEvent> GetEvent<TEvent>()
{
var subject =
(ISubject<TEvent>) subjects.GetOrAdd(typeof (TEvent),
t => new Subject<TEvent>());
return subject.AsObservable();
}
public void Publish<TEvent>(TEvent sampleEvent)
{
object subject;
if (subjects.TryGetValue(typeof(TEvent), out subject))
{
((ISubject<TEvent>)subject)
.OnNext(sampleEvent);
}
}
}
That's your whole event aggregator. Pass an instance of it into each view model, and store it as a reference. Then create a class to store your event details, let's say "ValueChangedEvent":
public class ValueChangedEvent
{
public int Value
{
get { return _value; }
}
private readonly int _value;
public ValueChangedEvent(int value)
{
_value = value;
}
}
Publish like this from the first view model:
set
{
_nbre = value;
PropertyChanged(this, new PropertyChangedEventArgs("Nbre"));
_eventPublisher.Publish(new ValueChangedEvent(value));
}
Subscribe in the other class using GetEvent:
public class ClassBViewModel: INotifyPropertyChanged, IDisposable
{
private readonly IDisposable _subscriber;
public ClassBViewModel(IEventPublisher eventPublisher)
{
_subscriber = eventPublisher.Subscribe<ValueChangedEvent>(next =>
{
IsBiggerthanFive = next.Value > 5;
});
}
public void Dispose()
{
_subscriber.Dispose();
}
}
A messenger service is a solution. MVVM Light Toolkit has an implementation of this. What you can do with it, is listen to a specific type of message in your viewmodel and handle it through the messenger. http://www.mvvmlight.net/

Categories