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
Related
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.
I am designing a base class for TextForms and derived classes for Labels , Fields, Dialogboxes... etc.
i am using the below code
public class TextForm
{
public void Refresh()
{
}
}
public class Label : TextForms
{
public void Refresh()
{
}
}
and in my program i am instanciating many Labels .. and somewhere i need to call TextForms.Refresh() which must execute all derived classes instances Refresh() method .
i can't imagine how to do?
You can't find all the instances by default. Though, there are a few design patterns that will make it easier for you to do so. There is the Composite Design Pattern Which gives you the ability to add sub components to your components. For example, a Form/Window component will be a container of sub components like Labels and TextFields for example. Then, when you will call Refresh on the container (Form/Window - for example) it can call the Refresh on all of it's sub components.
There is also the Observable Design Pattern which let components register for 'events' (not necessarily implemented via .Net's Events). Then when you call the Refresh method on the observer, it will call the Refresh methods of all the observable's that are registered.
In your question, you write TextForms so I am assuming that this is an object that is an enumerable of some sort.
Liskows substitution principle dictates that if you have a base class of any kind you can always substitute derived classes for the base class
var textForms = new List<TextForm>();
textForms.Add(new Label());
textForms.Add(new TextBox());
so now you can simply iterate through this list as such:
foreach(var textForm in textForms){
((TextForm) textForm).Refresh();
}
You may have to tweak the example a little to get it working, but that is the general answer to your question.
Like m102 said it is unpractical if not impossible to find all instances. However, assuming you use a canvas or page to display your labels on, it is possible to get all the labels in that canvas.
TextForm tf = new TextForm();
foreach (Control ctrl in yourCanvas.Children.OfType<Label>())
{
tf.Refresh();
}
This will retrieve all the controls of type label from the canvas. This will not refresh them all at once.
Note: I do not recommend changing labels that are not visible/onscreen. It would require them to be kept in memory and this is performancewise not advised. Oh and your refresh function has a capital R (This is usually reserved for classes).
I have an object called Campaign in my program. I have a User Control that will use the properties of this object.
But at the beginning of my program, this object is set to null. It's only when the user clicks a "Next" button that I would like my user control to receive this object.
But in my main form's InitializeComponent() method, a new instance of my user control is generated before I want it to. This instance takes no arguments.
this.userControlFileCampaign1 = new Postbag.UserControlFileCampaign();
So what I want to do is to send this Campaign object to my user control's constructor. But upon changing my constructor, the compiler tells me that:
'Postbag.UserControlFileCampaign' does not contain a constructor that
takes 0 arguments
Having two constructors does not help me because it is always the one with zero arguments that is run.
So how can I send this Campaign object to my user control?
Create an overload of the constructor that takes zero parameters and simply calls the one that does:
public UserControlFileCampaign()
: this(null) { }
public UserControlFileCampaign(Campaign camp)
{
}
You will not be able to avoid having the designer's code call a constructor...it must exist to be on the form. If you want to send the Campaign object later, just expose a property or method that you can use to do so at any point post-construction.
Empty your UserControl's constructor, and move the code to some other method, like SetCampaignAndStart(). Add this new method to your UserControl:
public void SetCompaignAndStart(Campaign campObject)
{
// Move constructor code here,
// where the campaign object is needed
}
When the user click Next, run this usercontrol's method to "start" it:
private void Button1_Click(object sender, EventArgs e)
{
myUserControl.SetCompaignAndStart(campaignObject);
}
I have a WPF application that has two combo boxes, one with what I'm calling a FileType and one with Actions (add, edit, delete, etc.). Every FileType has a corresponding class. What I'm trying to do is for example if FileTypeA is selected and the action is Add then I want to do something along these lines:
void myMethod(string FileType, string Action)
{
//using reflection find class from the FileType string, and call method Action
var x = new FileTypeA();
x.Add();
}
if the user selects FileTypeB and Delete then the object created and the method called will change accordingly. I think this might be possible using reflection, but I'm trying to find out if there is even a better way using perhaps a generic class? since all these FileType classes are very similar.
try to create an interface IFileType with basic methods Add,Delete,Save
Implement this interface on all fileTypes classes e.g DocFile,PdfFile etc.
Bind the List to ComboBox and use its selected Item object as cast it as IFileType and call its method via interface(Delete/Save) without doing any reflection.
I would recommend using MVVM pattern so that you can easily create separation of layer and have benefits of OOP.
If you aim to call a certain action for item according to the selected option from ComboBox, get familiar with delegates. Then use the approach of binding a collection of options (with the delegate included) to ComboBox
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.