How can I write to a form textbox from another object without having to return - windows

I am working on a Windows Application form and I have a multi-line textbox that I would like to write output from another object. I saw there was a similar posting about getting text from one textbox to another form's textbox. However, I am working with the mainform and can't new up another one.
So how can I assign values to the mainform's .Text from another class?

You can create public properties on the form that get or set control properties e.g.
public partial class Form1 : Form
{
...
public string OperatorId
{
get { return OperatorIdField.Text.Trim().ToLower(); }
}
public string Password
{
get { return PasswordField.Text.Trim().ToLower(); }
}
...
}
(OperatorIdField and PasswordField are textboxes. In my example, the properties are read only i.e. they only have "get" methods. In your case you'd have to add "set" methods as well).
To expose the main form to other objects, you can create a static member that exposes the main form e.g.
static class Program
{
public static Form MainForm;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm = new Form1();
Application.Run(MainForm);
}
}
When another object needs to get the password, the code would be something like:
string password = Program.MainForm.Password;

Second form should have some event, that you call once you need change text box value in main form. And main form should be subscribed to this event and change text in at some eventHandler.

Related

Xamarin MVVM Databinding not refreshing

i am struggling with the mvvm data binding. I am not using any framework for the mvvm, I got a very basic base class for my view models. I uploaded my example-app with my problem to GitHub, find the link below.
My problem:
I got a simple app with an tab menu. there are 2 tabs called "TabA" and "TabB". Both views have a simple view model. The view models are referencing a manager class which holds the data. The Manager class has to objects (objects of my datamodel-class which just contains a string and implements INotifyPropertyChanged) in an observablecollection. There is also a Property in the Manager which references the current choosen object (its just one of the 2 objects from the list).
There are 2 actions which can be done by "TabB". The first one works as expected. If you enter some new string into the entry an hit the first button, it updates the string of the current choosen object and updates the label in TabA.
What is not working? With the second Button in my "TabB" class you switch the value of the current choosen object in the Manager. In the debugger I can see that the value is changed, but the Label in "TabA" does not recognize that it has to update the value.
Can you help me?
https://github.com/dercdev/MVVM-Xamarin
With the help of Jason I came to something like this:
In my TabAViewModel I subscribed the event of the Manager:
public TabAViewModel()
{
_mgr = Manager.Instance;
_mgr.PropertyChanged += new PropertyChangedEventHandler(obj_PropertyChanged);
}
Then I raise the event:
private void obj_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("CurrentData");
}
Which updates the label of the view.
Is that okay or is there a "better" way to do it?
As far as I know, the better way is to use INotifyPropertyChanged. If you want to implement Notify, I think you need to implement INotifyPropertyChanged interface, you can create one class name ViewModelBase that inheriting INotifyPropertyChanged, like this:
public class ViewModelBase:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then you can call RaisePropertyChanged method to inotify when property changed,
private string _text;
public string Text
{
get
{
return _text;
}
set
{
_text = value;
RaisePropertyChanged("Text");
}
}
ObservableCollection implements INotifyPropertyChanged, allowing the collection to notify the user when the contents of the collection have changed - and specifically, what changed within the collection. For example, if you add an item to the collection, the CollectionChanged event will be raised with properties that tell you the index of the new item as well as including the item in a list.
So ObservableCollection _list don't need to call RaisePropertyChanged method.
https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1.system-componentmodel-inotifypropertychanged-propertychanged?view=netframework-4.7.2

Tapestry 5.3: Component with form inside t:zone

I have a component for editing db entity. It has #CommitAfter on onSuccess(). When it is on the separate page it works fine. I click update button, it saves data and redirects to view page.
Now I want to reuse it on the list page. By clicking on item it should appear. After editing and clicking update button on the component it should save item to db and hide itself.
To achieve this I modified component so I can set zone id for its form. Put component inside zone on the list page, added links for each item with event onSelectItem, which sets zone id for component and returns body of the zone.
It did show component and I could edit fields and hit update button. It updated item but redirected whole page to view page. I tried to return null in onSuccess() – but in this case it didn’t save data to db and zone also wasn’t refreshed. I also tried call page class from a component by using #InjectPage and return page.zone.getBody() – this does reload zone but still doesn’t save data though all methods passed w/o exception. Also it too bad to call page specific code inside a component. My other custom ajax calls do save data to db in methods with #CommitAfter.
So my question what is the correct way of doing this in Tapestry?
ContactsList.tml
<html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd" xmlns:p="tapestry:parameter">
<t:form>
... list of contacts linked to onSelectContact()
</t:form>
<t:zone id="editContact" t:id="editContactZone">
<t:if test="selectedContact">
<t:contactform t:id="contactForm" />
</t:if>
</t:zone>
</html>
ListPage.java
public class ContactsList{
#InjectComponent
private Zone editContactZone;
#InjectComponent("contactForm")
private ContactForm contactForm;
#Property
private Contact selectedContact;
public Object onSelectContact(#RequestParameter(value = "id", allowBlank = false) Integer id) {
selectedContact = getContactById(id);
if (selectedContact != null) {
contactForm.setContact(selectedContact);
contactForm.setFormZone(editContactZone.getClientId());
}
return editContactZone.getBody();
}
}
ContactForm.tml
<div xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" xmlns:p="tapestry:parameter">
<t:form t:id="editContactForm" zone="prop:zone">
.... contact fields
</t:form>
</div>
ContactForm.java
public class ContactForm{
#InjectComponent("editContactForm")
private Form editContactForm;
#Property
private String zone;
#InjectPage
private ContactList listPage;
#InjectPage
private ViewContact viewContact;
#Property
protected Contact contact;
public void setContact(Contact contact) {
this.contact = contact;
}
public void setFormZone(String zone){
this.zone = zone;
}
#CommitAfter
public Object onSuccess() {
parseContact();
storeContact(); // calls DAO.store(contact)
return zone == null ? viewContact : listPage.onSelectContact(0); //don't like this in component code but at least it returns zone's body
}
}
You could pass a Block component parameter to the ContactForm containing the markup to render after #OnSuccess.
Or perhaps a better separation of concerns is to fire an event in the ContactForm which bubbles up to the page? Eg:
ContactForm.java
#Inject ComponentResources resources;
#CommitAfter
public void onSuccess() {
storeContact();
resources.triggerEvent("contactSaved", new Object[] { contact }, null);
}
ContactsList.java
#Inject AjaxResponseRenderer ajaxRenderer;
#Inject Zone someZone;
#Property Contact contact;
public void onContactSaved(Contact contact) {
this.contact = contact;
ajaxRenderer.addRender(someZone);
}
Also, why are you using #RequestParameter? Why not use event context?
ContactsList.tml
<t:loop source="contacts" item="contact">
<t:eventlink event="selectContact" context="contact">Edit ${contact.name}</t:eventlink>
</t:loop>
ContactList.java
Block onSelectContact(Contact contact) {
// doStuff
}

Validation for items in ObservableCollection bound to DataGrid when validation of one item of collection depends on other items

I am using MVVM and displaying some items on a DataGrid. My model is RecordingInfo and looks like:
public class RecordingInfo : IDataErrorInfo
{
public RecordingInfo(string fullDirectoryName, string recordingName, int recordingNumber)
{
FullDirectoryName = fullDirectoryName;
RecordingName = recordingName;
RecordingNumber = recordingNumber;
}
public string FullDirectoryName { get; internal set; }
public string RecordingName { get; set; }
public int RecordingNumber { get; internal set; }
public string Error
{
get { throw new NotImplementedException(); }
}
public string this[string propertyName]
{
get {
if (propertyName == "RecordingName")
{
if (this.RecordingName.Length < 2)
return "Recording Name must be at least two characters";
}
return null;
}
}
}
I end up with a collection of these RecordingInfo programmatically. The user is not allowed to do much with these but he/she can change the RecordingName subject to the name being 2 characters or more AND that the RecordingName must be unique. I.e. no changing it to match another RecordingName. I have taken care of the first requirement. It's the second one that is giving me grief.
For my ViewModel, I have
public class RecordingListViewModel : ViewModelBase//, IDataErrorInfo
{
private ObservableCollection<RecordingInfo> _recordings = null;
public RecordingListViewModel()
{
}
public ObservableCollection<RecordingInfo> Recordings
{
get
{
return _recordings;
}
}
// more stuff left off for brevity
In my view I bind the collection to a DataGrid and have:
<DataGrid ItemsSource="{Binding Path=Recordings}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Recording" IsReadOnly="False" EditingElementStyle="{StaticResource CellEditStyle}" ElementStyle="{StaticResource CellNonEditStyle}" Binding="{Binding RecordingName, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" >
</DataGridTextColumn>
...
</DataGrid.Columns>
My way of checking for 2 or more characters works great. But this doesn't work for checking that the user is not trying to give a recording an existing name. Presumably, I need to somehow handle this at the ViewModel layer since the ViewModel knows about all Recordings. I tried playing with having my ViewModel derive from IDataErrorInfo but the property indexer never gets called, which makes sense as it's the Observable collection and therefore the individual RecordingInfos that are bound. I also thought about doing something with a "Lost Focus" event, but DataGridTextColumn doesn't seem to have that. I would think this is a somewhat common problem: validation must take into account relationships between the items of the collection.
By the way, I'm not wedded to the IDataErrorInfo and I am not opposed to other changes in architecture. Please let me know if I can provide more details. I have tried to provide a minimal amount of code. Clearly, this is part of a much bigger project. Any advice is appreciated.
Thanks,
Dave
I would do the following
1) Make RecordingInfo implement INotifyPropertyChanged
2) Use a BindingList<> instead of ObservableCollection<>
In your viewmodel, subscribe to the BindingList.ListChanged Event. This event will fire when items are added and removed, but also when the top level properties on RecordingInfo changes. In the case of a property being changed, the ListChangedEventArgs.PropertyDescriptor property will contain the name of the property, if you want to run validation for just that property (be careful though, this can be null when the item as added/removed). You'll need to use the ListChangedType property to determine the reason of the event (E.x.: Reset means everything changed, ItemAdded means the item was added, but the ItemChanged means a property changed as occurred on an existing item.
You can have the parent ViewModel (that contains and creates your RecordingInfos) pass a name validation Func in their constructors for them to call when validating their name changes.

Instantly Update Property in MVVM Light?

I have a property in my View Model
public const string WelcomeTitlePropertyName = "WelcomeTitle";
private string _welcomeTitle = string.Empty;
/// <summary>
/// Gets the WelcomeTitle property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public string WelcomeTitle
{
get
{
return _welcomeTitle;
}
set
{
RaisePropertyChanging(WelcomeTitlePropertyName);
_welcomeTitle = value;
RaisePropertyChanged(WelcomeTitlePropertyName);
}
}
This is hooked up to a textbox and has 2 way binding.
Now I have a KeyDown event that I need to get the current length of the "WelcomeTitle" property
public ICommand AutoComplete
{
get
{
return new RelayCommand<KeyEventArgs>(e =>
{
var length = WelcomeTitle.Length;
});
}
}
Yep what I am finding is that WelcomeTitle property does not get updated till the user leaves the textbox. This does not work for me as I need to know the length(and later the current value in WelcomeTitle) and keydown.
How can I get around this? In codebehind this is no problem.
In WPF it would be easily achieved by setting UpdateSourceTrigger="PropertyChanged" in the binding; unfortunately, this is not possible with Windows Phone, so you need a workaround. A few options are described in this question.

How do I get access to the QueryString in Windows Phone 7 from a user control

I have a simple user control in Windows Phone 7 and I want to get access to the querystring collection from the user controls Constructor. I have tried many ways and cannot seem to get acess to the containing XAML's querystring collection.
Essentially I am navigating to the page and the my user control is going to access the querystring value to write the value back to the interface.
Am I missing adding an assembly or reference or something?
I am not sure you should be trying to get at the information from the page's constructor, as it won't necessairly get called every time you land on this page. A better approach is to override the OnNavigatedTo method inherited from PhoneApplicationPage. Looking a little more carefully at your question, you may be trying to do this within a control embedded in the page, in which case you need to get at the Page in order to obtain the navigation information.
Regardless, the NavigationContext property from the page has a QueryString parameter that you can use to access the information you're after.
The following example assumes I have a parameter named "Message" in the query string when navigating to this page:
public partial class MyPage : PhoneApplicationPage
{
// Constructor
public MyPage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
String navigationMessage;
if (NavigationContext.QueryString.TryGetValue("Message", out navigationMessage))
{
this.textBlock1.Text = navigationMessage;
}
}
}
Sorry about that - I started to get there, and thanks for the clarification. Your best bet then is to walk up the visual tree from your control to find the Page, then you can have at the NavigationContext. In my sample below, I have a button on a custom control within the page, whose click event finds the nav context and looks for a certain navigation parameter - I couldn't tell from the question or your follow-up what would drive the control to "want" to find the content of the query string.
(Note about getting info from the ctor follows the code below)
public partial class WindowsPhoneControl1 : UserControl
{
public WindowsPhoneControl1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
GetTheQueryString();
}
private void GetTheQueryString()
{
var result = "No Joy";
var page = FindRootPage(this);
if (page != null)
{
if (page.NavigationContext.QueryString.ContainsKey("Param"))
{
result = page.NavigationContext.QueryString["Param"];
}
}
queryStringText.Text = result;
}
private static PhoneApplicationPage FindRootPage(FrameworkElement item)
{
if (item != null && !(item is PhoneApplicationPage))
{
item = FindRootPage(item.Parent as FrameworkElement);
}
return item as PhoneApplicationPage;
}
}
Note that this won't work from the ctor because of how Xaml works...the Xml tag drives the ctor to be called, then properties are set as indicated, then it is added as a child/item/etc in its container. If you do need to get at the context ASAP using this "walk up the tree" technique, handle the Control's Loaded event, by which time the control does have a parent and a tree that can be walked...
public WindowsPhoneControl1()
{
InitializeComponent();
Loaded += WindowsPhoneControl1_Loaded;
}
private void WindowsPhoneControl1_Loaded(Object sender, RoutedEventArgs e)
{
GetTheQueryString();
}
I would add a property to the UserControl subclass that would be set by the page in its OnNavigatedTo() method.

Resources