I have a function in my MainPage.xaml.cs, and when it is called I would like to change the text on a label in another page called Dashboard.xaml.cs
How do I change variables and call functions between files in Xamarin Forms?
Pages are just classes, and you can communicate between them like any class: using public methods, public properties, public events, etc
However, using Form's built in MessagingCenter might be the best method:
// send a message TO an instance of MyPage
MessagingCenter.Send<MyPage, string> (this, "MessageName", some_string_arg);
// in MyPage, listen for the Message
MessagingCenter.Subscribe<MyPage> (this, "MessageName", (sender, args) => {
// args will contain the value passed in Send
});
Provided that you have a reference to the instance of the Page, you can invoke methods or set properties on that instance.
In 2.3.6, you'll even be able to set a x:FieldModifier and modify the field values directly.
But don't do any of that. Use a Mvvm pattern, Bind your Pages, and let the ViewModels communicate between each other. And your Pages will be modified accordingly.
Every view in Xamarin is a class, you can instantiate a variable of the type View, where View is the page you want to access. For example:
I have a view called Works. To access (public) functions and variables contained within the view, I write code like this:
Works MyTestVariable;
var SomeResult = MyTestVariable.FunctionInWorksClass(aParameter);
The function contained in the Works view is executed and the value is returned to the var in the calling 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.
Basically I have a page in my app where there's an async function that receives data via bluetooth. I want to execute functions on my main page based on the data I receive via bluetooth on the other page. I searched around a bit and mostly people suggest creating a base class and inheriting both pages from the same class, but that isn't what I'm trying to do: I don't want to execute a generic instance of the main page's function, I want to execute the specific main page function that belongs to the main page that my app is displaying.
I thought of two possible solutions to this:
Update a static variable in the main page class based on the received data, and use a property to execute the required functions every time the variable changes value, but the problem is that the functions I want to execute are non-static.
If I could know the object name of the main page class which my app instantiates, I could simply make the functions public and access them using syntax like MainPageObjectName.myFunction(), but I don't know the name of the object instantiated from the MainPage class by my app.
Any advice? Thanks in advance.
Ali, I assume here that you need to update the contents of the MainPage.xaml while some background (async) task has finished.
The best way to do that is have a DataModel (MVVM) in your application. More info here
and here.
By doing this when the async task finishes a function will be called to update the DataStructure that holds your data. Since that Model implements the INotifyPropertyChanged class the control on your UI (even though they are on another page) will be updated automatically.
I have a method in a class like below:
public class ActionHelper
{
[DirectMethod]
public string DeleteComment()
{
...
return "Delete";
}
}
and I want to call it from grid command like this:
<Command Handler="Ext.net.DirectMethod.request({url: '/Classes/ActionHelper/DeleteComment', cleanRequest: true}});" />
but it's not working! how can i do that??? I use ext.net 2.2 and .netframework 4.5
look at this example
http://examples.ext.net/#/Events/DirectMethods/ID_Mode/
it can be helpful
Put a [DirectMethod] in your code behind wich calls that class and use
App.direct.<Method>(); instead
You can't, you either define the method inside the Page, User Control or any Custom Control, or you define a static method inside the Page class.
Here is a quote defining direct methods from a post in the Ext.net forums:
DirectMethod (none static, must be public): server side handler is
raised when you call special javascript method (basically, proxy
method is generated by Ext.Net toolkit). None static direct method can
be defined inside Page, User Control or any Custom Control. Please
note, if direct method is defined inside user control (master page
placeholders are user controls also) or custom control then ClientID
of that control will be added to proxy method 1
Ext.net.DirectMethods.ClientIDOfTheControl.DirectMethodName(); You can
use DirectMethodProxyID attribute for the class to define own alias or
completely remove ClientID prefix Really, none static direct method is
direct event. Single difference, that direct method has no relation
with any widget (and its events) and can be raised by developer from
javascript (as javascript method)
Static DirectMethod (must be public): similar ASP.NET PageMethods, can be defined inside Page class only. With static page method the
Page life cyle is not execued therefore access to ASP.NET control is
not possible but response time much better (depends from your method
logic only)
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.
Is there any definitive way of determining from which View (UserControl) a command was called?
For instance, if I have two Views, each tied to the same ViewModel, how might I determine from which View a particular command was called? Having a separate command for each View would not make a difference in this scenario, as it is an external service which ought to be notified of the particular active View.
I am assuming that you call the command from a usercontrol. So if you call the method someMethod for example then require that method to take a UserControl object as a parameter. In other words you should have somethin like:
Public void someMethod(UserControl u, other params...){
If( u is SomeUsercontrol){
\\do somethin
} else if....
}
And on your user conrtoll call that method as:
someMethod(this, .....) \\ the this keyword will send a UserControl object if you call that method within a UserControl class. In other words it will pass itself as a parameter