Cannot get input string from textarea in field - javafx

Trying to grab user's input from textfield when they click on submit button but I am unable to either set the variable within the textfield function (it gets set to blank because there's no updating), and am unable to access the textarea from outside the field node
form {
//other code
var textField = field("Short description of action") {
var textArea = textarea {
prefRowCount = 1
maxWidth = 400.0
hgrow = Priority.ALWAYS
//actionDescription = this.text //does not update value
}
}
buttonbar {
var statusText = field("Save status will appear here")
button("Save baby action") {
actionDescription = (textField.children[1] as TextArea).text
//actionDescription = textField.textArea //does not recognize
//actionDescription = textField.text //wrong, "Short description of..."
action {
if (actionDescription.isEmpty()) {
statusText.text = ("Must enter short description")
} else {
//do some work with inputText
}
}
...
Compiler knows the cast will never work but using debugger it seems that the textarea is child #2 or [1] of the textfield, but then I can't call the getText method on this or any of the children.Please let me know why this simple access is giving me trouble--thank you!

Got it by binding the textArea's textProperty to a string property in the scope of the whole view or of the form, then check that value as being blank!

Related

xamarin shell: dynamically generate pages for routes

I was trying out the newly released xamarin shell. Basically, I am trying out how to map the route to a dynamically generated page. I noticed the RegisterRoute has an overload that requires an object of type RouteFactory. I created a demo class for this:
class NavMan : RouteFactory
{
private readonly string title;
public NavMan(string title) : base()
{
this.title = title;
}
public override Element GetOrCreate()
{
return new ContentPage
{
Title = "tit: " + title,
Content = new Label { Text = title }
};
}
}
Now, in the App.cs I create a register a demo route:
Routing.RegisterRoute("batman", new NavMan("I am batman"));
I have tried several variations of setting up the Shell object. I mostly get blunt null pointers and need to guess what to change.
As of now, my App class's constructor has the following code:
var sea = new Shell();
var theItem = new FlyoutItem
{
IsEnabled = true,
Route = "batman"
};
sea.Items.Add(theItem);
sea.CurrentItem = theItem;
MainPage = sea;
This gives me a blunt null pointer too. All I am trying for now is to display the page of route "batman". Even a flyout or tab isn't mandatory.
Update
While not the aim, I at least got the app opening with the following:
var sea = new Shell();
Routing.RegisterRoute("batman", new NavMan("I am batman"));
var theItem = new ShellContent {
Title = "hello 20",
Route = "batman2",
Content = new ContentPage {
Content = new Button {
Text = "Something shown",
Command = new Command(async () => await Shell.Current.GoToAsync("//batman"))
}
}
};
sea.Items.Add(theItem);
sea.CurrentItem = theItem;
MainPage = sea;
On clicking the button, it now shows me the following exception and never calls the GetOrCreate function.
System.Exception: <Timeout exceeded getting exception details>
Update 3
Basically, I am looking for a way to bind a route to ShellContent in a way that it simply displays I mention the route property and it displays a page in the route. It doesn't make sense that I need to mention a route AND mention a content template for the page. The route is already mapped to a page.

How do I print the content of a TextArea?

So currently I'm trying to make a print feature for my Notepad application. I already have a kind of working Print feature, but it prints the full TextArea not only the string that is written into it.
I already tried to make it just print a string, but the PrintJob is not able to handle it, because it needs the actual TextArea, where the Text is written into.
My current Print Stuff:
public void doPrint() {
String toPrint = writeArea.getText();
printSetup(writeArea, Main.primaryStage);
}
private final Label jobStatus = new Label();
private void printSetup(Node node, Stage owner)
{
// Create the PrinterJob
PrinterJob job = PrinterJob.createPrinterJob();
if (job == null)
{
return;
}
// Show the print setup dialog
boolean proceed = job.showPrintDialog(owner);
if (proceed)
{
print(job, node);
}
}
private void print(PrinterJob job, Node node)
{
// Set the Job Status Message
jobStatus.textProperty().bind(job.jobStatusProperty().asString());
// Print the node
boolean printed = job.printPage(node);
if (printed)
{
job.endJob();
}
}
What I want to have:
A print that only shows the String, just like any other notepad application does if you try to print something
What I currently get:
The full textarea with frame.
As I mentioned in the comment you can wrap it into a Text, but then the first line for some reason isn't displayed correctly.
The solution would be to use a Label instead like:
printSetup(new Label(toPrint), Main.primaryStage);

Bound properties not working

I'm using TornadoFX 1.7.5 and I can't seem to get bound properties to work. I have the below ItemViewModels
class DynamicMenuViewModel : ItemViewModel<DynamicMenu>(DynamicMenu()) {
val title = bind { item?.title?.toProperty() }
val isBold = bind { item?.isBold?.toProperty() }
val routes = bind { item?.routes?.toProperty() }
}
data class DynamicMenu(var title: String = "", var isBold: Boolean = false, var routes: MutableList<MenuRouteViewModel> = mutableListOf())
class MenuRouteViewModel : ItemViewModel<MenuRoute>(MenuRoute()) {
val url = bind { item?.url?.toProperty() }
val title = bind { item?.title?.toProperty() }
val isBold = bind { item?.isBold?.toProperty() }
val showNew = bind { item?.showNew?.toProperty() }
}
data class MenuRoute(var url: String = "", var title: String = "", var showNew: Boolean = false, var isBold: Boolean = false)
Which are bound like this:
//routesController.dynamicMenu is an instance of DynamicMenuViewModel()
textfield(property = routesController.dynamicMenu.title) {
prefWidth = formWidth * .5
gridpaneConstraints {
columnRowIndex(0, 1)
marginLeft = 10.0
columnSpan = 2
marginBottom = 20.0
}
}
checkbox(property = routesController.dynamicMenu.isBold){
gridpaneConstraints {
columnRowIndex(2, 1)
marginLeft = 15.0
marginBottom = 20.0
}
}
Then the following functions commit the models and prints them to the screen when I click a button:
fun onClick(){
commitModel()
println(dynamicMenu.item.toString())
dynamicMenu.item.routes.forEach {
println(it.item.toString())
}
}
fun commitModel(){
dynamicMenu.item.routes.forEach {
it.commit()
}
dynamicMenu.commit()
}
The problem is that when I run the program and edit the textfields and checkboxes then click the button that runs onClick(), the backing item doesn't seem to be getting updated. So none of the updated values are printed to the console.
What am I doing wrong here?
The ViewModel can as you probably know only bind bidirectionally against JavaFX Properties. Your domain objects doesn't contain JavaFX properties, so you need to convert them. However, the toProperty() function you are using only operates on a value, and turns it into a Property. This property has no way of knowing about it's field owner, and hence cannot write back into the domain object.
Luckily, you can use the observable function to make your domain object properties writable as well:
val url = bind { item?.observable(MenuRoute::url) }
Since the observable function operates on a specific instance of a MenuRoute object, it now has enough information to write back into that instance when you commit() the model.
If you would consider changing the properties in your domain objects to be observable, you could write:
val url = bind(MenuRoute::url)
You can use the TornadoFX IDEA plugin inspection "Convert all properties to TornadoFX Properties" to automatically rework your properties. This would transform your MenuRoute object into:
class MenuRoute {
val isBoldProperty = SimpleBooleanProperty(false)
var isBold by isBoldProperty
val showNewProperty = SimpleBooleanProperty(false)
var showNew by showNewProperty
val urlProperty = SimpleStringProperty("")
var url by urlProperty
val titleProperty = SimpleStringProperty("")
var title by titleProperty
}
(You have to manually remove the data modifier on your class. Also beware that the current version of the plugin has a bug in the conversion function that would leave the old parameters - a new version will be released shortly).
If you don't want to do that for various reasons, I was just able to support that nice syntax even for mutable vars like you have, so from TornadoFX 1.7.6 you can use this syntax in your binding statements even if you don't want to change your data classes:
val url = bind(MenuRoute::url)

Get Selected Text asp.net custom server control

I need to get the latest text set in the custom control by javascript. When i tried to get the selected text from server control, it is always returning the default text & not the modified text. How to retain the latest value set by the javascript in servercontrol? Below is the complete code for your reference..
ServerControl1.cs
[assembly: WebResource("ServerControl1.Scripts.JScript1.js", "text/javascript")]
namespace ServerControl1
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:ServerControl1 runat=server></{0}:ServerControl1>")]
public class ServerControl1 : WebControl
{
public List<string> ListItems
{
get
{
return ViewState["items"] as List<string>;
}
set
{
ViewState["items"] = value;
}
}
public string Text
{
get
{
return (FindControl("middleDiv").FindControl("anchorID") as HtmlAnchor).InnerText;
}
set
{
((FindControl("middleDiv").FindControl("anchorID") as HtmlAnchor)).InnerText = value;
}
}
protected override void CreateChildControls()
{
base.CreateChildControls();
HtmlGenericControl selectedTextContainer = new HtmlGenericControl("div");
selectedTextContainer.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextContainer.ID = "middleDiv";
HtmlAnchor selectedTextAnchor = new HtmlAnchor();
selectedTextAnchor.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextAnchor.ID = "anchorID";
selectedTextAnchor.HRef = "";
selectedTextContainer.Controls.Add(selectedTextAnchor);
HtmlGenericControl unList = new HtmlGenericControl("ul");
foreach (string item in ListItems)
{
HtmlGenericControl li = new HtmlGenericControl("li");
HtmlAnchor anchor = new HtmlAnchor();
anchor.HRef = "";
anchor.Attributes.Add("onclick", "updateData()");
anchor.InnerText = item;
li.Controls.Add(anchor);
unList.Controls.Add(li);
}
selectedTextContainer.Controls.Add(unList);
Controls.Add(selectedTextContainer);
ChildControlsCreated = true;
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
string resourceName = "ServerControl1.Scripts.JScript1.js";
ClientScriptManager cs = this.Page.ClientScript;
cs.RegisterClientScriptResource(typeof(ServerControl1), resourceName);
}
}
}
JScript1.js
function updateData() {
var evt = window.event || arguments.callee.caller.arguments[0];
var target = evt.target || evt.srcElement;
var anchor = document.getElementById("anchorID");
anchor.innerText = target.innerText;
return false;
}
TestPage Codebehind
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
List<string> items = GetDataSource();
ServerControl1.ListItems = items;
ServerControl1.Text = "Select ..";
}
}
protected void ClientButton_Click(object sender, EventArgs e)
{
string selectedText = ServerControl1.Text;
}
The server won't get your client changes unless you POST the changes to him. Your HtmlAnchors are being rendered in HTML as <a> controls, and these type of controls won't POST anything to the server.
You're going to need an <input> control to input the changes into the server (that's why they're called input controls after all). I suggest an <input type=hidden> to hold the value of the anchor.innerText and keeps its state.
Your Javascript function needs to be modified so it updates the anchor.innerText AND updates the hidden input value as well. This way when the page gets posted back to the server you can retrieve the updated and client-modified value from the hidden field.
First you need to define as private fields your selectedTextAnchor and the hiddenField you are going to insert. This is because you need to access them in your CreateChildControls method as well as in the getter and setter of yout Text property. Much in the way the partial designer classes define the controls you want to have available in code-behind.
ServerControl.cs
private HtmlAnchor selectedTextAnchor;
private HtmlInputHidden hiddenField;
In the CreateChildControls method you need to insert the hidden field.
You'll notice I removed the use of ClientIDMode.Static. Using that mode would make your client controls to have the same fixed IDs and Javascript might get confused when you have multiple copies of your ServerControl in a page, and thus losing the reusable purpose of a custom control.
Instead, you need to provide your Javascript function with the ClientID's of the controls it needs to modify. The key here is that you need to attach your controls to the Control's hierarchy BEFORE you try to get their ClientID's.
As soon as you do this.Controls.Add(dummyControl), you're making dummyControl to become a part of the Page and its dummyControl.ClientID will be suddenly changed to reflect the hierarchy of the page you're attaching it into.
I changed the order at which your controls are attached to the Control's collection so we can grab their ClientID's at the time we build the onclick attribute and pass the parameters so your Javascript function knows which anchor and hiddenField to affect.
ServerControl.cs
protected override void CreateChildControls()
{
base.CreateChildControls();
// Instantiate the hidden input field to include
hiddenField = new HtmlInputHidden();
hiddenField.ID = "ANCHORSTATE";
// Insert the hiddenfield into the Control's Collection hierarchy
// to ensure that hiddenField.ClientID contains all parent's NamingContainers
Controls.Add(hiddenField);
HtmlGenericControl selectedTextContainer = new HtmlGenericControl("div");
// REMOVED: selectedTextContainer.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextContainer.ID = "middleDiv";
selectedTextAnchor = new HtmlAnchor();
// REMOVED: selectedTextAnchor.ClientIDMode = System.Web.UI.ClientIDMode.Static;
selectedTextAnchor.ID = "anchorID";
selectedTextAnchor.HRef = "";
selectedTextContainer.Controls.Add(selectedTextAnchor);
// Insert the selectedTextContainer (and its already attached selectedTextAnchor child)
// into the Control's Collection hierarchy
// to ensure that selectedTextAnchor.ClientID contains all parent's NamingContainers
Controls.Add(selectedTextContainer);
HtmlGenericControl unList = new HtmlGenericControl("ul");
foreach (string item in ListItems)
{
HtmlGenericControl li = new HtmlGenericControl("li");
HtmlAnchor anchor = new HtmlAnchor();
anchor.HRef = "";
// The updateData function is provided with parameters that will help
// to know who's triggering and to find the anchor and the hidden field.
// ClientID's are now all set and resolved at this point.
anchor.Attributes.Add("onclick", "updateData(this, '" + selectedTextAnchor.ClientID + "', '" + hiddenField.ClientID + "')");
anchor.InnerText = item;
li.Controls.Add(anchor);
unList.Controls.Add(li);
}
selectedTextContainer.Controls.Add(unList);
}
Note the use of the keyword this in the updateData function, it'll help us to grab the object that is triggering the action. Also note that both Id's are passed as strings (with single quotes)
The Javascript function would need to be modified so it updates the anchor and the hidden input field.
JScript1.js
function updateData(sender, anchorId, hidFieldId) {
// Update the anchor
var anchor = document.getElementById(anchorId);
anchor.innerText = sender.innerText;
// Update the hidden Input Field
var hidField = document.getElementById(hidFieldId);
hidField.value = sender.innerText;
return false;
}
The last thing to do is change the way you are setting and getting your Text property.
When you GET the property you need to check if it's a Postback, and if it is, then you want to check if among all the info that comes from the browser there is your HiddenInputField. You can grab all the info coming from the client right at the Request object, more specifically, in the Request.Form.
All enabled input controls on your page will be part of the Request.Form collection, and you can get their values by using Request.Form[anyInputControl.UniqueID]. Note that the key used for this object is the UniqueID, NOT ClientID.
Once you get your client-modified value from the hidden input, you assign its value to the selectedTextAnchor, otherwise it'll go back to the original "Select..." text.
When you SET the property, you just need to assign it to the selectedTextAnchor.
In both GET and SET you need to call EnsureChildControls(), which will actually call your CreateChildControls() to make sure that your selectedTextAnchor and hiddenField controls are instantiated before you try to get some of their properties. Pretty much the same way that it's done in Composite Controls.
ServerControl.cs
public string Text
{
get
{
EnsureChildControls();
if (this.Page.IsPostBack)
{
string HiddenFieldPostedValue = Context.Request.Form[hiddenField.UniqueID];
// Assign the value recovered from hidden field to the Anchor
selectedTextAnchor.InnerText = HiddenFieldPostedValue;
return HiddenFieldPostedValue;
}
else
{
return selectedTextAnchor.InnerText;
}
}
set
{
EnsureChildControls();
selectedTextAnchor.InnerText = value;
}
}
This way you can have a control that recognizes the changes made in client. Remember that server won't know any change in client unless you notice him.
Another approach would be to notice the server everytime you click a link through an ajax request, but this would require a whole new different code.
Good luck!

Get old text from change event?

Can I somehow find out what was the change in the textfield? I would want to compare the old text with the new text ... the problem is, that I have multiple textAreas in a tab-editor, and all the textAreas are watched by one eventListener. I want to get a value calculated by the next formula:
globalChangeCount += thisTextArea.currentCharacterCount - thisTextArea.oldtCharacterCount
where the globalChangeCount is a value modified by all changes in any of the textAreas.
I am searching for these values through the event variable, but can't seam to find the old text of the textArea.
This may or may not be what you're looking to do:
package
{
import mx.controls.TextArea;
public class CountingTextArea extends TextArea
{
public var staleText : String = "";
[Bindable("textChanged")]
[NonCommittingChangeEvent("change")]
public function get charDiff() : int
{
var diff : int = staleText.length - text.length;
staleText = text;
return diff;
}
public function CountingTextArea()
{
super();
}
}
}
I made it so that you can use it as a source for binding. Instead of subscribing to the event on each TextArea, you can use:
function addWatchers():void
{
ChangeWatcher.watch(countingTextArea1, ["charDiff"], charDiffChangeHandler );
...
ChangeWatcher.watch(countingTextArea5, ["charDiff"], charDiffChangeHandler );
}
With the event handler somewhere too:
function charDiffChangeHandler( event : PropertyChangeEvent ) : void
{
trace(event.currentTarget.charDiff);
// or
trace(event.newValue);
}
You can use event.currentTarget to get a reference to the TextArea that fired the event, and use the focusIn event to execute a function to populate a variable with the old text value.
Maybe you should just subclass the TextArea and create an oldText field variable you update internally after all the external listeners have been notified.

Resources