Another subject and a difficulty along with it surfaced while playing with WPF and it lies in an attempt to call a function from within a dynamically added user control passing it a value.
For current example I have a user control that has a property "Secret" and a button in it named "PublishSecret", constructor sets the value of the Secret to a random number. In MainWindow I have another button named "AddSecretContainer" that adds to the window an additional instance of this user control, next to it I have a textbox named "PublishedSecret". A simple method in MainWindow also was defined, here it is:
public void PublishSecret(int secret)
{
this.PublishedSecret = secret;
}
Now without passing this Secret I could use routed commands with their bubbling effect. But how can I call PublishSecret providing it with the local value of Secret from user control when PublishSecret button was clicked?
If your UserControl was added dynamically, then I assume that you have access to a reference of it in your main view model/code behind. This makes your situation nice and simple. You said there is a Button named PublishSecret, well I also assume that clicking it initiates some functionality.
My first suggestion would be the simplest. Put that functionality into a public method which would be called from your Click or ICommand handler and then simply call that method from your main view model:
childUserControl.PublishSecret();
Please let me know if I have misunderstood your problem.
UPDATE >>>
Ok sorry, I've got you now. You can declare a delegate, add a property of that type to your child UserControl and attach a handler in the main view model. Then you can call the delegate and in the handler in the parent view model, you can call your method. For more information on this technique, please see my answer to the Passing parameters between viewmodels question.
Related
I am quite new to MVVM, so sorry for probably simple question. However, I can not understand which mechanism from MVVVM (I am using MVVMLight if that is of any consequence) to use in order to program the simple following scenario:
I have textbox TB, where user can fill in URL. Than I have a button B and webview WV. If user clicks on button, the app should take the text from TB and display it in the WV.
I knwo that I can create a property in viewmodel and bound it to TB.Text. I understand probably also that I should create command which will be boudn from button B, but what should I do in the command. How I can call WV.navigate(url), when I do not have reference to WV. Should this be solved by something, which I did not grasp correctly like behaviours? What is the best way to do this?
You should use the messenger pattern for this problem:
http://msdn.microsoft.com/en-us/magazine/dn745866.aspx
http://www.codeproject.com/Tips/696340/Thinking-in-MVVMLight-Messenger
http://mytoolkit.codeplex.com/wikipage?title=Messenger
The idea is that the view can register for specific message classes (in this case for example an own NavigateToUriMessage class) and the view model can send an instance of this message class to whoever listens to the message type. In the command implementation you simply send this message, the view receives the message and changes the URI of the web view.
BTW: The idea of this messenger pattern is that you can better write Unit Tests and use the view model for other platforms (where the reaction to the message may differ).
Another way is to create an attached property for the WebView class where you can bind an Uri property to. The attached property calls Navigate when the bound value changes.
Check out this blog:
http://blogs.msdn.com/b/wsdevsol/archive/2013/09/26/binding-html-to-a-webview-with-attached-properties.aspx
I am trying to create a simple onscreen keypad created using buttons (currently a User-control), on those buttons i have a click event, when i click/touch a button i want the value of that button sent to a Text-block in my Main-window.
I can't/don't understand how to make the User-control (keypad) see the Text-block (in Main-window) to add in the value that i need.
I have seen solutions that use command Bindings and solutions that use the visual tree traversing but all of them are the main window accessing the user control, not the other way around.
All the examples are the other way around because that is how a UserControl is supposed to work.
A UserControl is a packaged piece of re-usable functionality. It should not know anything about the code that is using it.
Instead you should expose routed events in your UserControl for things like a when number was selected, and subscribe to them in your main window.
There are many ways to achieve what you want. If your MainWindow.xaml has a UserControl and you want to react to a change from the control in the MainWindow.xaml.cs file, then you could add a delegate to the UserControl code behind and register a handler for it in the MainWindow.xaml.cs file. Implementing new delegates are generally somewhat simpler than implementing RoutedEvents, which is another way that you could handle this situation.
Using a delegate like this will enable you to effectively pass a signal to the main view from the child UserControl code behind, which you can react to in any way you want to. Rather than explain the whole story again here, please see my answers from the Passing parameters between viewmodels and How to call functions in a main view model from other view models? posts here on Stack Overflow for full details on how to achieve this.
I've been searching for and trying to figure out how to link a variable to a UI control, so that I can use the variable to perform that particular's UI's actions in code.
For instance, I have a button named NextButton in my MainPage.xaml. I want to define a variable, say nextButton of type Button, so that I can perform a button action, say nextButton.Content in my c# class. It seems this can only be done in the MainPage.xaml and even with that I can 't figure out how to link the button variable to a particular button.
To make myself even clearer, let me just say that you can do something like this in android by using findViewById.
I'm not sure if I understood your question, but assuming you have a Button with a specified name on a Window. The only way you can access that button in another class is by having access to that window instance in that class.
I believe the window can be casted as a frameworkelement and you can use the method FindName
Here to find an element inside that window. However this only works is you have access to the instantiated Window inside that class.
In XAML:
<Button x:Name="nextButton" Text="Next" />
In the corresponding .cs file the member variable nextButton will be part of the class:
nextButton.Content = SomethingAmazing;
This might be slightly off what you're after, but if you just want to alter the content of a button, you could use data binding and then provide access point for other classes to the data directly.
To me, it sounds like you want to directly alter another view from another view and that doesn't sound very "MVCey". Have a common underlying data model instead and alter that and have the buttons figure out what they should be doing. If your button performs methods or it's state is handled by methods.
If you really want to provide an access point to the buttons directly, you could just have something like
public abstract class CommonUIControls {
public static Button nextButton = null;
}
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
CommonUIControls.nextButton = nextButton;
}
}
However, I would not suggest thsi.
I'm having a problem understanding something about MVVM. My application relies on dialogs for certain things. The question is, where should these childwindows originate from? According to MVVM, viewmodels should contain only businesslogic and have zero actual knowledge about UI. However, what other place should I call my childwindows from, considering they're UI elements?
Doesn't this create tight coupling between elements?
Since you tagged the question with Prism, I'll suggest the way I've done it in the past using Prism. Have the IEventAggregator injected into your ViewModel, and then when you want to pop open the dialog, publish a "ShowDialogEvent" or something like that. Then, have another Module called "DialogModule" or whatever, which upon initialization subscribes to that event, and shows the dialog. Furthermore, if you want to pass data back to the original ViewModel, have the ViewModel of the dialog publish a "DialogCloseEvent" or something like that with a payload of the data you need. You can then subscribe to that event back in your main ViewModel.
See Handling Dialogs in WPF with MVVM
In the past, I have accomplished this by using Unity to resolve a custom interface that has a Show() method and a completed event. Then in the ViewModel I would call IScreen screen = container.Resolve<IScreen>(Resources.EditorWindowKey); and then just call screen.Show();.
The big advantage of this is that I can then just simply change my Unity configuration to remove the view when I'm testing my VM's.
The primary route I've been using to do this is to create a command inside your View layer. That command object accepts a parameter that is the ViewModel object that you want to display. The command then finds the appropriate ChildWindow, creates it and displays it with the parameter set as the content or however you will set it up. This way you can just bind a button's command property to that command, and its commandparameter to the object you want to show in the popup and your ViewModel objects never have to care how it's being displayed.
Prompting for user input (like saving a dirty file or something) doesn't work in this scheme. But for simple popups where you manipulate some data and then move on, this works very well.
The ViewModel sample application of the WPF Application Framework (WAF) demonstrates how to show a Modal Dialog.
I would suggest to use a controller in this scenario, say DI'ed dialogController backed up with a dialog shell. The source viewmodel(ie from where the request to open a dialog is originating) will make a call to dialogController.ShowDialog(<<ViewNameToHostInRegion>>,<<RegionName>>).
In Order to transfer the data to and from the dialog and sourceview you can use MessageBus. So essentially when you invoke the ShowDialog() you populate the messagebus, and when the close command of target View(The view hosted in Dialog shell) invoked - say in "Select" button -- Let the target view add/update the messagebus. So that source view model can work with updated data.
It has got many advantages :
1) Your source view works with dialog controller as BlackBox. ie it doesnt aware of what exactly the Dialog view is doing.
2) The view is being hosted in Dialog Shell -- so you can reuse the dialog again and again
3) Unit testing of source view is limited to test the actual functionality of the current viewmodel, and not to test the dialog view\view model.
One heads-up which I came across with this one is, while creating the test cases you may need to write testable Dialog controller which do not show the actual dialog while running the testcases in bunch. So you will need to write a TestableDialogController in which ShowDialog does nothing (Apart from deriving from IDialogController and provide blank implementation of ShowDialog().
Following is the psudeo code :
LocalMessageBus.AddMessage(<MessageKey>,<MessageActualContentAsObject>);
dialogController.ShowDialog(<TargetViewName_SayEmployeeList>);
Employee selectedEmployee = LocalMessageBus.GetMessage(<MessageKey>) as Employee;
if (selectedEmployee != null)
{
//doSomework with selected employee
}
I have a simple user control with a text box and label in it. I created public properties to access the text in the textbox when I use the user control in another form.
My problem is the property is returning null value when I call it in the form. Am i missing anything?
My property is as follows::
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public string rtnTxtMake
{
get
{
return txtMake.Text;
}
set
{
txtMake.Text = value;
}
}
}
and in the next forms button click event i call the property as follows
UserControl1 Usc = new UserControl1();
string Make = Usc.rtnTxtMake;
MessageBox.Show(Make)
UserControl1 Usc = new UserControl1();
string Make = Usc.rtnTxtMake;
If your user control has by default an empty textbox field, then it seems correct that the above two lines of code would return either null or String.Empty (check via String.IsNullOrEmpty), since you explicitly create a new instance of your user control.
I suppose what you really want is this:
You have inserted a user control into a form in the Designer. Let's call this user control instance ctlUser.
You have a button with a Click event handler. The last few lines of code in your question are from that handler method.
In the handler, you wouldn't create a new instance of your user control (Usc) but refer to the one that you previously inserted into your form, ctlUser. Then things should work as expected.
Your UserControl must be added to the Controls collection of a parent Form/Control before it can be properly initialized. Normally you would not write the code yourself that creates and adds the UserControl.
Instead, first build your project, then go to the Deisgner view of your main form and look at the Toolbox.
Your UserControl name (and an icon) should appear towards the top of the toolbox, and you can simply drag it to the main form. The Windows Forms designer will automatically generate the needed initialization code for you.
You should not create a new instance of your control in your button click event handler. Using the Designer approach to create your control you can simply access the existing instance of your control as follows:
public void button_Click(object sender, EventArgs e)
{
// myUserControl1 has already been created and initialized by the Deisgner generated code
// Note the name 'myUserControl1' is just an example, yours may be different.
string controlText=myUserControl1.rtnTxtMake;
// Or to change the UserControl textbox value
myUserControl1.rtnTxtMake="Testing";
}
What exactly to you mean when you say that the property is returning a null value? Is it actually null, or is your MessageBox simple showing empty?
I quickly duplicated your code and it behaves exactly as expected - the MessageBox shows, but it is empty because the default value of the Text property of the TextBox control is an empty string.
Also, the way you are approaching this is a little unusual.
Firstly, the line:
UserControl1 Usc = new UserControl1();
You do not generally need to instantiate a user control like this. Instead you can drag the control from the toolbox onto the design surface of your form. This will then take care of instantiating and initialising your control for you.
I think that this is actually your problem - when you include the line of code above, you are creating a new instance of the user control, and this is is no way realted to the user control that you have dragged onto the designer.
If you go to the designer view of your form and click on the user control, you should see a properties window somehere. If you do no, then either select it from the View menu, or press F4. In the list of properties, there should be one "Name" this is the programatic name generated for your user control. You can change this here if you want, but when you refer to this control in the rest of the form, this is what you must use.
Secondly, the next two lines:
string Make = Usc.rtnTxtMake;
MessageBox.Show(Make)
You can access the property rtnTxtMake directly. Unless you later need to access the Make string in the rest of your code, then directly accessing the property would usually be considered better style.
MessageBox.Show(userControl.rtnTxtMake);