How to call functions in a main view model from other view models?
The
delegate
method used in this and the linked answer can be used in any parent-child relationship and in either direction. That includes from a child view model to a parent view model, aWindow
code behind to the code behind of a childWindow
, or even pure data relationships without any UI involved. You can find out more about usingdelegate
objects from the Delegates (C# Programming Guide) page on MSDN.
I just answered a similar question to this earlier today. If you take a look at the Passing parameters between viewmodels post, you'll see that the answer involves using delegate
objects. You can simply replace these delegate
s (from the answer) with your method(s) and it will work in the same way.
Please let me know if you have any questions.
UPDATE >>>
Yes, sorry I completely forgot you wanted to call methods instead... I've been working on too many posts tonight. So still using the example from the other post, just call your method in the ParameterViewModel_OnParameterChange
handler:
public void ParameterViewModel_OnParameterChange(string parameter)
{
// Call your method here
}
Think of the delegate
as being your path back to the parent view model... it's like raising an event called ReadyForYouToCallMethodNow.
In fact, you don't even need to have an input parameter. You could define your delegate
like this:
public delegate void ReadyForUpdate();
public ReadyForUpdate OnReadyForUpdate { get; set; }
Then in the parent view model (after attaching the handler like in the other example):
public void ChildViewModel_OnReadyForUpdate()
{
// Call your method here
UpdateDisplay();
}
As you have multiple child view models, you could define the delegate
in another class that they both have access to. Let me know if you have any more questions.
UPDATE 2 >>>
After reading your last comment again, I've just thought of a much simpler method that might achieve what you want... at least, if I understand you correctly. It is possible for you to Bind
directly from your child views to your parent view model. For instance, this would allow you to Bind
a Button.Command
property in a child view to an ICommand
property in your parent view model:
In TreeViewView
:
<Button Content="Click Me" Command="{Binding DataContext.ParentCommand,
RelativeSource={RelativeSource AncestorType={x:Type MainWindow}}}" />
This of course assumes that an instance of the parent view model in question is set as the DataContext
of the MainWindow
.
Call a function from another ViewModel
There are multiple ways to accomplish what you want. I'll post two approaches that are very common when working in an MVVM pattern
Event based approach:
public MainViewModel()
{
LoginVM = new LoginViewModel(SharedData);
LoginVM.PropertyChanged += LoginVM_PropertyChanged;
InitialVM = new InitialViewModel(SharedData);
FirstVM = new FirstViewModel(SharedData);
SecondVM = new SecondViewModel(SharedData);
//ActualView = InitialVM;
ActualView = LoginVM;
}
private void LoginVM_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(sender.GetType() == typeof(LoginViewModel) && e.PropertyName == "MessageInfo")
{
var loginVM = (LoginViewModel)sender;
if (loginVM.MessageInfo == "OK")
{
ActualView = InitialVM;
}
}
}
Cunstructor Injection:
private Action _loginAction;
public LoginViewModel(DataManager sharedData, Action loginAction )
{
_DataManager = sharedData;
DoLoginCmd = new RelayCommand(param => DoLogin(), canExec => (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password)));
_loginAction = loginAction;
}
public void DoLogin()
{
if (Username == "admin" && Password == "password")
{
_DataManager.User.Name = "Administrator";
_DataManager.User.Mail = "admin@company.com";
MessageInfo = "Login OK!... How to redirect??";
_loginAction.Invoke();
}
else
{
MessageInfo = "Username or Password incorrect!";
}
}
and
public MainViewModel()
{
LoginVM = new LoginViewModel(SharedData, () => ActualView = InitialVM);
InitialVM = new InitialViewModel(SharedData);
FirstVM = new FirstViewModel(SharedData);
SecondVM = new SecondViewModel(SharedData);
//ActualView = InitialVM;
ActualView = LoginVM;
}
Side note: to put a property "ActualView" on the BaseViewModel is a rather odd choice
How to access a function from a viewModel in another viewModel
This is usually a symptom of bad architecture.
If StorageViewModel
is acting like a Repository
it should not extend ViewModel
. If it doesn't have connections to UI you can convert it to a repository class and that would solve your problem because it would just become an injectable singleton.
If StorageViewModel
is connected to a Fragment (for example) you should take a reference to both viewmodels and pass data between them from the UI layer.
Something like:
class StorageFragment : Fragment {
private val storageViewModel: StorageViewModel by viewModels()
private val mainActivityViewModel: MainViewModel by activityViewModels()
//....
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//you can do this if the song saving is a UI related thing
//just have playOrToggleSong accept a function as parameter
//as success callback
button.setOnClickListener {
mainActivityViewModel.playOrToggleSong(...) {
storageViewModel.saveLastPlayedSong(param)
}
}
}
}
Calling a method from a different ViewModel
Using Ed's idea in the question comments I did the following.
class ShellViewModel : Conductor<object>
{
public void Open_ToolInventory()
{
ActivateItem(new TIViewModel());
}
public void ViewProblemReport()
{
WindowManager wm = new WindowManager();
VPRViewModel vprvm = new VPRViewModel();
wm.ShowDialog(vprvm);
}
}
Was changed to:
class ShellViewModel : Conductor<object>
{
TIViewModel tivm = new TIViewModel();
VPRViewModel vprvm = new VPRViewModel();
public void OpenToolInventory()
{
ActivateItem(tivm);
}
public void ViewProblemReport()
{
WindowManager wm = new WindowManager();
wm.ShowDialog(vprvm);
tivm.PopulateToolInventory();
}
}
This runs the targeted method after the dialog is closed updating the tool inventory to reflect all the solved problems at once. You're the best, Ed!
Calling a method in one viewmodel from a different viewmodel
If you use MVVM Light, I assume that you have ViewModelLocator instance in your App.xaml resources defined like below.
<vm:ViewModelLocator xmlns:vm="clr-namespace:WPApp.ViewModel" x:Key="Locator" />
In your settings view code behind:
private async void ContactsSortParametersListPicker_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
await ((ViewModelLocator)App.Current.Resources["Locator"]).OtherViewModel.LoadDirectory();
}
how to call method defined in view from view model
As @tabby suggested i would use public property in viewmodel and bind
toCircleProgressBar
using visibility converter. For binding opactiy you can directly bind it.
Viewmodel . Assuming you have used INotifyPropertyChanged Interface. Wrap the property in INPC.
public bool IsBusy;
public int Opacity;
SomeAction()
{
IsBusy = true;
Thread.Sleep(5000);
IsBusy = false;
Opactiy = 1;
}
Converter
public BooleanVisibiltyConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool flag = false;
if (value is bool)
{
flag = (bool)value;
}
else if (value is bool?)
{
bool? nullable = (bool?)value;
flag = nullable.HasValue ? nullable.Value : false;
}
return (flag ? Visibility.Visible : Visibility.Collapsed);
}
}
XAML
Add the converter to the window resource before consuming it.
<CircularProgressBar Opacity="{Binding Opactiy}" Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"
Calling a function in a Window from a different Window's View Model
Use MVVM Light Messenger
> http://dotnetpattern.com/mvvm-light-messenger
public class ViewModelA : ViewModelBase
{
public void SearchCommandMethod()
{
MessengerInstance.Send<NotificationMessage>(new NotificationMessage("notification message"));
}
}
Jesse Liberty of Microsoft has a great concrete walk through on how to make use of the messaging within MVVM Light. The premise is to create a class which will act as your message type, subscribe, then publish.
public class GoToPageMessage
{
public string PageName { get; set; }
}
This will essentially send the message based on the above type/class...
private object GoToPage2()
{
var msg = new GoToPageMessage() { PageName = "Page2" };
Messenger.Default.Send<GoToPageMessage>( msg );
return null;
}
Now you can register for the given message type, which is the same class defined above and provide the method which will get called when the message is received, in this instance ReceiveMessage.
Messenger.Default.Register<GoToPageMessage>
(
this,
( action ) => ReceiveMessage( action )
);
private object ReceiveMessage( GoToPageMessage action )
{
StringBuilder sb = new StringBuilder( "/Views/" );
sb.Append( action.PageName );
sb.Append( ".xaml" );
NavigationService.Navigate(
new System.Uri( sb.ToString(),
System.UriKind.Relative ) );
return null;
}
Related Topics
How to Get Client Ip Address in ASP.NET Core
Switch Statement Fallthrough in C#
.Net/C# - Convert Char[] to String
Difference Between Events and Delegates and Its Respective Applications
How to Read a Pem Rsa Private Key from .Net
How to Use Webrequest to Access an Ssl Encrypted Site Using Https
Using Stringwriter for Xml Serialization
What Does the '=>' Syntax in C# Mean
Cursor.Current VS. This.Cursor
Dot Character '.' in MVC Web API 2 for Request Such as API/People/Staff.45287
The Entity Type <Type> Is Not Part of the Model for the Current Context
How to Convert JSON Text into Objects Using C#
Have a Set of Tasks with Only X Running at a Time
Combining N Datatables into a Single Datatable