I have a method within the code behind of my View (this method does something to my UI).
Anyway, I'd like to trigger this method from my ViewModel. How could this be done?
My (and maybe others?) difficulty with MVVM was to understand a simple thing: View knows about ViewModel. I was using bindings and commands, but they are simple strings in xaml. Because of safe resolving at run-time (safe means you can do a typo, but software will not crash) this makes view decoupled from view-model (at compile time at least). And I was always looking for solution to keep this decoupling, to example, behaviors.
Truth is, you can get access directly to view model, which is typically a DataContext of window/user control:
var vm = (MyViewModel)this.DataContext;
Knowing that, using events probably the best way to call view method from view model, because view model don't know if there is subscriber, it just firing that event and event can be used by view or another view model.
// define in the view model
public delegate void MyEventAction(string someParameter, ...);
public event MyEventAction MyEvent;
// rise event when you need to
MyEvent?.Invoke("123", ...);
// in the view
var vm = (MyViewModel)DataContext;
vm.MyEvent += (someParameter, ...) => ... // do something
You can do it like this in View (code behind).
It casts to an interface to be implemented by the ViewModel, so that you are not constrained to one specific ViewModel type.
// CONSTRUCTOR
public SomeView()
{
InitializeComponent();
DataContextChanged += DataContextChangedHandler;
}
void DataContextChangedHandler(object sender, DependencyPropertyChangedEventArgs e)
{
var viewModel = e.NewValue as IInterfaceToBeImplementedByViewModel;
if (viewModel != null)
{
viewModel.SomeEvent += (sender, args) => { someMethod(); }
}
}
According to MVVM pattern ViewModel is not aware of View, so this is not acceptable. To interact with ViewModel View could trigger a command, also you can use bindings. Moreover, you should not move UI-specific things like BusyIndicator to ViewModel level.
Please provide more details regardign your concrete use case - when you want to call a View's method and what this method does.
Let's say you have a method within the code behind of my Login View, that updates UI by bringing Focus to the PasswordEntry if login fails, then the easiest & most universal way to trigger this method from your ViewModel is using Action delegates.
As you can see in this sample, all you need to add, where your services determine that the login has failed and you want the Password Entry to get the focus, is two lines of code in your ViewModel and an action handler in your View.
ViewModel code:
Declaration of the event: public Action<bool> OnLoginFailed { get; set; } &
Then simply, when needed, executing this OnLoginFailed?.Invoke(true);
View code:
ViewModel.OnLoginFailed = ((obj) =>
{
PasswordEntry.Focus();
});
Update: I wrote an article to explain this in a lot more detail
I saw youre reply to the answer above, you are saying that you want your ViewModel to retrieve data and then tell your view to stop the busy indicator.
I'm not sure if my solution would be the best solution, but you can give it a try, and maybe someone can correct if I'm wrong.
So from your view, you would call a method from ViewModel to start reading the dataset, am I right? In this method, you can pass a delegate (pointing to a method that exists in your view) and when your ViewModel finishes reading the dataset from the server, trigger the delegate (from your viewmodel) that is linked to your method in your view that can stop the busy indicator.
so in your view you have
void StopBusyIndicator()
{
this.BusyIndicator.IsBusy = false;
}
and when you call your ViewModel to read dataset,
call it like this:
ViewModel.ReadDataSet( ()= >StopBusyIndicator)
which will pass the StopBusyIndicator method as a delegate, which you can call at the end of your ReadDataSet.
HTH
You could write an action class that accepts a Data Transfer object. Within the DTO, add a property called "View" and assign it the current view. Call the action via the controller from within your view's codebehind, unbox the DTO and now you have full control of the view within the action class.
If you truely want to do this in your model, just create the method with a "View" type parameter in your Model and execute it, passing in the current view.
Related
I am struggling with ReactiveUI routing for my UWP Navigation View. Since Navigation View Item does not implement command I use ItemInvoked event and execute my command in my view model. Unfortunately, I am unable to show another page in the view. I was using the official tutorial and also Reactive UI UWP Example. When using breakpoint I can see that my command is executed but nothing happens. I have no clue how to debug this more. Did anyone implement Navigation View wit ReactiveUI Routing?
My code: My repo
#Edit
POCOObservableForProperty: The class InwentarzRzeczowy.UWP.Views.MainView property ViewModel is a POCO type and won't send change notifications, WhenAny will only return a single value!
POCOObservableForProperty: The class InwentarzRzeczowy.UWP.Views.MainView property RoutedViewHost is a POCO type and won't send change notifications, WhenAny will only return a single value!
I believe you need a .Subscribe() after the AddPage.Execute() command in your event handler. I'm doing this from memory though and I remember something like that tripping me up.
Based on the conversation we had in Slack, posting the solution here as well. With ReactiveUI routing, you have to either register the views into Splat.Locator.CurrentMutable before using the router (see View Location) or write a custom view locator that matches your view models and returns the views (see Routing). The latter option expects you to implement the IViewLocator interface and assign it to RoutedViewHost.ViewLocator property. So, in C# code we generally have this:
public class YourViewLocator : IViewLocator
{
public IViewFor ResolveView<T>(T viewModel, string contract = null) => viewModel switch
{
NewEntryViewModel _ => new NewEntryView(),
// Also match other routable view models...
// The RoutedViewHost will initialize the ViewModel
// properties of your views automatically.
_ => throw new System.NotImplementedException()
};
}
And in XAML markup we have this:
<reactiveUi:RoutedViewHost Router="{Binding Router}">
<reactiveUi:RoutedViewHost.ViewLocator>
<yourAssembly:YourViewLocator />
</reactiveUi:RoutedViewHost.ViewLocator>
</reactiveUi:RoutedViewHost>
Also, it is important to initialize the IViewFor.ViewModel property for the root view. This could be done in the constructor of your view right after a call to this.InitializeComponent(), or in the composition root of your application.
I am making a Winforms program, using and MVVM-like pattern. All my data is connected to a SessionModel which is passed to all ViewModels (I know this probably isn't the best way to do things, but thats what I got).
My issue is this: Inside a ViewModel I have an event listener, which connects an event to a method, like this:
// Base constructor
public MyViewModel(SessionModel session)
{
this.session = session;
MyUserControl.MyEvent_OnActivate += AddItem;
}
// This method should be called, whenever the event is invoked
private void AddItem()
{
session.Items.AddItem();
}
A new instance of the ViewModel is generated each time the corresponding View is shown. And here arises my problem. Because everything is fine the first time I load the View and ViewModel. But the second time I visit it, the first instance still exists (although not referenced anywhere) and thereby picks up the event as well. The result is that the method AddItem() is called twice (once from each instance of the ViewModel. If I navigate to the View once again, it would be called three time, and so on.
My Viewand ViewModel is loaded from the MainForm like this:
// When called, this method loads the view and view model
private void ShowMyView()
{
MyViewModel viewModel = new MyViewModel(session);
MyView view = new MyView(viewModel);
ShowContent(view);
}
// Clears content panel and shows new view
private void ShowContent(UserControl view)
{
while (pnlContent.Controls.Count > 0)
{
pnlContent.Controls[0].Dispose();
}
pnlContent.Controls.Add(view);
}
Is there some clever way of clearing old instances of view models, since the garbage collection apparently isn't fast enough?
although not referenced anywhere...
Event subscriptions do keep the subscribers to be referenced. The old viewmodel should unsubscribe from MyUserControl.MyEvent_OnActivate whenever a new one is initialized. Make your ViewModel disposable and perform the cleanup in the Dispose method.
I found a solution, by getting rid of the event and simply listening directly to the delegate, as described in this post.
The code then looks like this:
public class MyUserControl
{
internal delegate void MyDelegate();
(...)
}
public class MyViewModel
{
public MyVieWModel(SessionModel session)
{
this.session = session;
MyUserControl.MyDelegate = AddItem;
}
public void AddItem()
{
(...)
}
}
Your problem that MyControl still keeping references to the previous viewmodels.
Winforms perfectly suitable for MVVM pattern. It supports data-binding (not so powerful as in WPF)
I know that I am slaughtering MVVM a bit, using it the way I am. It
was a work around, since my views contained user controls, which I had
to get to "talk to" the view model, which they did not know
By referencing view control in the viewmodel you break MVVM pattern. If you continue to "follow" MVVM - you are on your own, different kind of problems start gathering and after few weeks you decide that MVVM pattern not doable ;)
Simple suggestion for your particular case, without knowing much about application context, user control should now about viewmodels it affect on.
You can bind collection of viewmodels to the user control where it simple calls it's methods
private UserControl_OnActivate()
{
foreach (var viewmodel in ViewModels)
{
viewmodel.AddItem()
}
}
Now user control know about viewmodel and viewmodel know nothing about user control(view) - MVVM pattern.
Old viewmodel's references will be garbage collected when you update ViewModels collection with new ones.
I'm writing a ViewModel-first MVVM application using Caliburn.Micro
My View contains a 3rd party UserControl that implements a Method I want/need to call from the associated ViewModel. How do I do that while still upholding the MVVM principles?
There exists an old thread here on SO where a similar question is asked in a more specific context. I would appreciate it if someone could flesh-out the approaches suggested there a bit.
Approach one suggests that the View could subscribe to an IEventAggregator message. But wouldn't I have to use the code behind file to do that? (I thought that was a big no no in MVVM)
Regarding approach two, I have no idea how to do that. And regarding approach three, thats what I tried first but somehow I didn't quite get it to work.
Let me clarify your understanding:
Yes code in the code-behind is generally avoided, but only because MVVM makes it so easy to bind to viewmodel properties and commands in order to wire up your visual element with the functionality behind the scenes
Code that is view-specific in the code-behind of the view is perfectly acceptable assuming it doesn't cross the boundary of concern. For instance, I have a view in my application that does some visual processing of the page, and to do so I require that there is code in the view. This code may also interact with the viewmodel layer, but it will not directly reference the viewmodel, therefore keeping my components loosely coupled
If you have controls that need particular methods calling, then creating an event aggregator message to propagate the notification to the view is perfectly fine since you are still maintaining the separation of concern between the viewmodel and view (and the application components remain encapsulated and testable)
Example View (I've left all event aggregator wire up code and potential dependency injection stuff out for clarity):
public class MyView : IHandle<SomeNotificationMessageType>
{
// Handler for event aggregator messages of type SomeNotificationMessageType
public void Handle(SomeNotificationMessageType message)
{
// Call a method on one of the page controls
SomePageControl.SomeMethod();
}
}
Obviously, what you wouldn't do is something like this in the ViewModel:
public class MyViewModel : IViewAware
{
public void DoSomethingThatAffectsView()
{
var view = this.GetView() as MyView;
view.SomePageControl.SomeMethod();
}
}
Which violates the MVVM principles since you are tightly coupling MyViewModel and MyView.
What if you wanted to use the Context property in caliburn micro which allows multiple views over the same view model? The code above would break - even if you checked the View type, you would still end up with spaghetti code e.g.
public class MyViewModel : IViewAware
{
public void DoSomethingThatAffectsView()
{
var myview = this.GetView() as MyView;
if(myview != null)
myview.SomePageControl.SomeMethod();
var myotherview = this.GetView() as MyOtherView;
if(myotherview != null)
myotherview.SomePageControl.SomeMethod();
// ad infinitum...
}
}
Of course this is subjective: it may be that your usercontrol affects the viewmodel and the view in a complex way, in which case you might want to consider looking at the architecture and working out how that usercontrol can better fit
Have you got any background on what the UC is and what the method on it does?
Greetings my smart programming friends!
I have created an OnResponseEvent for an object that is injected into my viewmodel via UnityContainer.
From my injected object class:
public delegate void ResponseEventHandler(AbstractResponse response);
public event ResponseEventHandler OnResponseEvent;
Constructor for MainWindowViewModel:
public MainWindowViewModel(ITrack track)
{
this._track = Track;
track.OnResponseEvent += UpdateTrackResponseWindow;
}
Created delegate to handle the OnResponseEvent in my MainWindowViewModel:
private delegate void HandleTrackResponseCallback(AbstractResponse message);
Since the OnResponseEvent is sending a message, I need to interpret the message in a separate thread and display in a listBox.
Here is where I get confused. If I were using WinForms to write this application, I could use the following code because WinForms knows about my listBox:
if (ListBox.InvokeRequired)
{
var d = new HandleTrackResponseCallback(UpdateTrackResponseWindow);
Invoke(d, new object[] { message });
}
else
lstTrackResponse.Text = Interpret(message); //Interpret is a separate method
However, I am using WPF, and my MainWindowViewModel class does not know anything about my listBox which is located in a view.
Can anyone provide example code how I might handle the OnResponseEvent on a different thread in my MainWindowViewModel class?
Thanks so much, any help is greatly appreciated.
Manipulating the view from the view-model goes against the basic architectural principles of MVVM. Even if it weren't a being called from a different thread it would still not be a wise thing to do.
In MVVM, you would handle this like you do any other data that needs to be displayed in the view:
process and store the data in the view-model itself (using your event handler)
expose it as property including change notification
access that property from the view using traditional data binding
Something as simple as:
<TextBox Text="{Binding Response}"/>
This approach even takes care of the threading problem because now you handle the concurrency issues in the view-model, instead of in the view. Just lock access to the property that TextBox.Text is bound to while you are modifying it.
In general, MVVM avoids ever having a direct dependency of the view-model on the view for several reasons:
the big picture goal of loose-coupling between the view-model and the view
the ability to unit test the view-model without a view
At first this separation might seem clumsy and requires extra work (see above) but it really is worth it and it's what we need to do to get the benefits of the MVVM approach.
What's the right way to get my viewmodel to trigger a custom lookup control to throw up a modal dialog that essentially represents that lookup viewmodel? The custom lookup control's data context is that of the parent record view model. The lookup control also has another DependencyProperty that has it bound to a lookupviewmodel property on the parent record view model and this represents a sub lookupviewmodel.
METHOD 1) I currrently use an event on the lookupviewmodel that the custom control knows to listen for.
METHOD 2) I tried throwing a validation exception within the setter of the property on the lookupviewmodel that the lookup control's text propery is bound too. Then I hooked the ErrorEvent in the custom lookup control. But it seems that if the user "corrects" the value from within the dialog while in this event, the original value sticks. And worse, even after I call Validation.ClearInvalid, another ErrorEvent still fires that somehow adds the error back. So everything works here in the sense that all the viewmodels have the correct data, it's just that it seems like the textbox is ignoring that the bound text property has changed on the underlying data source when inside an ErrorEvent. So it seems like I can't correct an error while inside the processing of that error?
Another sub issue within method 2 is that Validation.ClearInvalid doesn't remove the red error border. I had to manually clear the ErrorTemplate too. Is that right?
I'd like to find a way to use natural error handling within the control to get it to throw up the modal dialog.
This isn't what you use events for. Events exist to facilitate decoupling: the object raising the event shouldn't know or care what the object(s) listening to it are doing. You're expecting an event to be able to change the value of a property from inside the property's setter - or worse, your event handler is calling the very property setter that's raising the event that it's handling, which means that you have to do something pretty hackish to avoid a stack overflow.
Your description isn't very clear (you're describing both the problem you're having and the non-working solutions you're trying at the same time, which is confusing), but it sounds like what you're trying to do is something more like:
if (IsValid(value))
{
_Property = value;
}
else
{
_Property = GetValueFromDialog();
}
The problem is that you don't want to have code in your view model that throws up a dialog, since that creates a view model that can't be tested outside of your WPF application.
The answer in this case is to use dependency injection. Create an interface called IDialogService:
interface IDialogService
{
object GetValueFromDialog();
}
Now add this property to your view model:
public IDialogService DialogService { get; set; }
The above code becomes:
if (IsValid(value))
{
_Property = value;
}
else
{
_Property = DialogService.GetValueFromDialog();
}
Create a dialog service for use in your WPF application that actually throws up the dialog and gets the result. When you instantiate your view model in your application, do this:
MyViewModel vm = new MyViewModel { DialogService = new WpfDialogService(); }
Thus, in your application, the property setter will put up the dialog and get the result exactly as you expect it to.
For your unit tests, create a mock dialog that looks like this:
public class MockDialogService : IDialogService
{
private object Result;
public MockDialogService(object result)
{
Result = result;
}
public object GetValueFromDialog() { return Result; }
}
You can then write a test like:
MyViewModel vm = new MyViewModel { DialogService = MockDialogService(ExpectedResult) };
vm.Property = InvalidValue;
Assert.AreEqual(ExpectedResult, vm.Property);
The above is really more a sketch of a solution than a solution - depending on how your application uses dialogs, you may need a lot more features than what are sketched out here. If you take a look at MVVM frameworks you'll find that a lot of them implement dialog services of one kind or another.
You can use a framework like MVVMLight or Prism which allow you to pass payloads between different entities in totally decoupled ways. MVVMLight is very lightweight compared to Prism. It has a concept of Messanger which acts as a system wide event bus. Similarly you have EventAggregator in Prism.