Bound string value from ViewModel not updating in UI element - xaml

I have this TextBlock in XAML that its text property is bound to a viewmodel command.
<TextBlock Text="{Binding SomeText}"></TextBlock>
Meanwhile, the viewmodel looks like this:
\\ the text property
private string _someText = "";
public const string SomeTextPropertyName = "SomeText";
public string SomeText
{
get
{
return _someText;
}
set
{
Set(SomeTextPropertyName, ref _someText, value);
}
}
\\ the command that changes the string
private RelayCommand<string> _theCommand;
public RelayCommand<string> TheCommand
{
get
{
return _theCommand
?? (_theCommand = new RelayCommand<string>(ExecuteTheCommand));
}
}
private void ExecuteTheCommand(string somestring)
{
_someText = "Please Change";
\\ MessageBox.Show(SomeText);
}
I can successfully call the TheCommand as I did able to call a MessageBox using that command from the triggering element. The SomeText value also change as shown in the commented MessageBox line. What am I doing wrong here, is there any silly mistake?

You're setting the field _someText directly, which means you're by-passing the setter of the SomeText property. But that setter is calling the Set(SomeTextPropertyName, ref _someText, value); method that is internally raising the PropertyChanged event.
That PropertyChanged event is necessary for the Data Binding, so that it knows the SomeText property was updated.
That means, instead of doing this:
private void ExecuteTheCommand(string somestring)
{
_someText = "Please Change";
\\ MessageBox.Show(SomeText);
}
Just do this and it should work:
private void ExecuteTheCommand(string somestring)
{
SomeText = "Please Change";
\\ MessageBox.Show(SomeText);
}

Related

How to prepopulate Autocomplete SelectedItem

From my previous post, it helped be to determine how to bind to selecteditems, How to bind to autocomplete selecteditem with ObservableCollection But now I'm trying to enhance that logic.
I'm trying to have items preselected when my View is initialized. I've tried multiple options, but I can't seem to get items preselected. May I get some assistance. My current code below
Keyword Class
public class Keyword : ObservableObject
{
private string _value;
public string Value
{
get { return _value; }
set { SetProperty(ref _value, value); }
}
}
ViewModel
private ObservableCollection<object> _selectedKeywords = new ObservableCollection<object>();
private ObservableCollection<Keyword> _keywords = new ObservableCollection<Keyword>();
public TestViewModel()
{
Keywords = new ObservableCollection<Keyword>()
{
new Keyword { Value = "Apples" },
new Keyword { Value = "Bananas" },
new Keyword { Value = "Celery" }
};
SelectedKeywords = new ObservableCollection<object>(Keywords.Where(x => x.Value == "Apples"));
}
public ObservableCollection<object> SelectedKeywords
{
get { return _selectedKeywords; }
set { SetProperty(ref _selectedKeywords, value); }
}
public ObservableCollection<Keyword> Keywords
{
get { return _keywords; }
set { SetProperty(ref _keywords, value); }
}
View
<autocomplete:SfAutoComplete MultiSelectMode="Token"
HorizontalOptions="FillAndExpand"
VerticalOptions="EndAndExpand"
TokensWrapMode="Wrap"
Text="{Binding Keyword, Mode=TwoWay }"
IsSelectedItemsVisibleInDropDown="false"
Watermark="Add..."
HeightRequest="120"
SelectedItem="{Binding SelectedKeywords}"
DataSource="{Binding Keywords}">
</autocomplete:SfAutoComplete>
We have prepared sample from your code snippet and you have missed to add DisplayMemberPath property in the code snippet. Please find the sample from below location.
http://www.syncfusion.com/downloads/support/directtrac/general/ze/AutoCompleteSample-270923957.zip
Note: I work for Syncfusion.
Regards,
Dhanasekar
To make it preselected in your View Model set a value to the binding that you have binded on your View basically assign a value to SelectedKeywords
Something like:
SelectedKeywords = Keywords.FirstOrDefault();
You might need two-way binding not sure cause never used this control:
SelectedItem="{Binding SelectedKeywords, Mode=TwoWay}"

Make object sent useable in xamarin.forms cs file

I have a list of news, when a news is clicked it sends the user to a new page with the specific newsitem.
On the news item page I want to manipulate the object sent so I can change values within this object.
My ItemTappedEvent looks like this
public void goToEvent(object sender, ItemTappedEventArgs e)
{
if (e.Item == null)
{
return;
}
var selectedItem = e.Item; // model
Navigation.PushAsync(new eventItem(selectedItem)); // pass the selected whole item from list to DetaiPage 'selectedItem' using constructor
((ListView)sender).SelectedItem = null;
}
My NewsItem page handles this as a c# object like this
public eventItem(object selectedItem)
{
this.BindingContext = selectedItem;
InitializeComponent();
}
Within my "selectedItem" is a value named "product_wheelchair"
If this has the value "true"
Do I wan't to change it to "Ja". How can i convert my object "selectedItem" so this is possible.
Thanks in advance!
You need to cast the object to your class like:
var mySelectedItem = selectedItem as myClass
After that you can access the properties/fields available in myClass. Obviously if those are private you need to make them public to be accessible.

intellij plugin development show input dialog with multiple text boxes

I'm creating intelliJ plugin and registering my action , inside my action i want to show an input dialog with multiple text boxes, how do I do that ?
I have an example of showing only one text box -
String txt= Messages.showInputDialog(project, "What is your name?",
"Input your name", Messages.getQuestionIcon());
I agree with #AKT with extending the DialogWrapper but suggest overriding doOKAction:
#Override
protected void doOKAction() {
if (getOKAction().isEnabled()) {
// custom logic
System.out.println("custom ok action logic");
close(OK_EXIT_CODE);
}
}
Or, if you just want your data out without the Action mess, add a custom method:
public class SearchDialog extends DialogWrapper {
...
public String getQuery() {
return "my custom query";
}
}
You can use it like:
SearchDialog dialog = new SearchDialog();
dialog.showAndGet(); // Maybe check if ok or cancel was pressed
String myQuery = dialog.getQuery();
System.out.println("my query: " + myQuery);
Create a new GUI Form (form + class). Class should extend DialogWrapper and override methods.
Inside createCenterPanel() return your root JPanel. You can set any default values, add event listeners to text box, etc., before returning JPanel.
Implement an Action interface where you want to get the value when OK button is clicked. Pass this action to your form class.
getOKAction() should return this action.
Following code is from a plugin i'm currently working on. Hopefully this will give you some idea but will have to adapt it to your need.
public class ReleaseNoteDialog extends DialogWrapper implements Action {
private JTextArea txtReleaseNote;
private JPanel panelWrapper;
.......
protected JComponent createCenterPanel() {
......
return panelWrapper;
}
......
#Override
protected Action getOKAction() {
return this;
}
.......
#Override
public void actionPerformed(ActionEvent e) {
// save value to project state
super.doOKAction();
}

Updating GUI from another class which implements SerialPortEventListener (Java FX, FXML)

I am making an application which uses serial communication. In SerialEvent method of that class, I am awaiting for a input from COM port, and then I want to pass it to the controller class of an .fxml screen.
Input will always be 8 bytes, and it works correctly inside that thread (I read the input and by printing it to the output, I see that the String is correct). However, when I try to pass it "in real time" to the controller class, I have a problem.
If I pass it directly, it does receieve it, but I can't invoke anything later (Not on FX Application Thread exception), I know that I can't do it that way, that I need to use Platform.runLater or similair solution, but if I use it that way, my controller class never receives that input, textField which I am trying to update stays blank.
I will copy part of the code here, and I am hoping that someone tell me what I'm doing wrong.
SERIALEVENT METHOD OF ANOTHER CLASS
#Override
public void serialEvent(SerialPortEvent spe) {
if (spe.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
byte singleData = (byte) input.read();
logText = new String(new byte[]{singleData});
bytes.add(logText);
if(bytes.size() == 8) {
for (int i = 0; i < bytes.size(); i++) {
inputText += bytes.get(i);
}
if(inputText.length() == 8) {
Platform.runLater(new Runnable() {
#Override
public void run() {
controller.getInputString(inputText);
}
});
}
bytes.clear();
inputText = "";
}
} catch (Exception e) {
logText = "Failed to read data. (" + e.toString() + ")";
controller.getInputString(logText);
}
}
}
GETINPUT METHOD OF THE CONTROLLER CLASS
#Override
public void getInputString(String input) {
firstSerialNumberField.setText(input);
}
When using it this way, my firstSerialNumberField never gets that input.
---EDIT---
SETCONTROLLER METHOD OF THE SERIALPORTLISTENER CLASS
public void setController(SerialController controller) {
this.controller = controller;
}
INITIALIZE SCREEN IN SCREEN HANDLER CLASS
serialCommunication = new SerialCommunication(this);
loader = new FXMLLoader();
loader.setLocation(getClass().getResource(path));
pane = loader.load(getClass().getResource(path).openStream());
serialController = (SerialController) loader.getController();
serialController.setScreenHandler(this);
serialController.setSerialCommunication(serialCommunication);
serialCommunication.setController(serialController);
parent = loader.getRoot();
stage = new Stage();
stage.setScene(new Scene(parent));
stage.setTitle(title);
stage.setResizable(false);
stage.sizeToScene();
stage.centerOnScreen();
stage.initModality(Modality.APPLICATION_MODAL);
stage.showAndWait();
You are passing a reference to inputText to the (inappropriately-named) getInputText() method in the controller. inputText is presumably a field in the class implementing the port listener. However, as soon as you pass it, you then set it back to an empty string:
if(inputText.length() == 8) {
Platform.runLater(new Runnable() {
#Override
public void run() {
controller.getInputString(inputText);
}
});
}
bytes.clear();
inputText = "";
Since inputText is being accessed from multiple threads, there is no guarantee as to which order things will happen: whether controller.getInputText(inputText) will execute first, or whether inputText = ""; will execute first. So you may end up setting the text field to an empty string.
What I think you intend to do is:
if(inputText.length() == 8) {
final String numberFieldText = inputText ;
Platform.runLater(new Runnable() {
#Override
public void run() {
controller.getInputString(numberFieldText);
}
});
}
or more succinctly:
if(inputText.length() == 8) {
final String numberFieldText = inputText ;
Platform.runLater(() -> controller.getInputString(numberFieldText));
}

How to display a Labels 'Error on ErrorProvider1'

Goal
I want to display the text that I put in the Label's "Error on ErrorProvider1" attribute whenever I get an error. See the following label's attributes below.
I try to display the text in the red rectangle into my ErrorProvider1 SetError(control, value) function.
If TextBox1.Text.Trim.Contains("'") Then
ErrorProvider1.SetError(lblErr, ErrorProvider1.GetError(lblErr))
Else
ErrorProvider1.SetError(lblErr, "")
End If
How can I retrieve the 'Error on ErrorProvider1' text from the lblErr to display it in the ErrorProvider1 SetError value?
The ErrorProvider component is very awkward to use effectively. It is fixable however, I'll give an example in C# that extends the component with some new capabilities:
ShowError(Control ctl, bool enable) displays the text that you entered at design-time when the enable argument is true. The easier-to-use version of SetError().
HasErrors returns true if the any active warning icons are displayed. Handy in your OK button's Click event handler.
FocusError() sets the focus to the first control that has a warning icon, if any. It returns false if no warnings are remaining.
SetError() is a replacement of ErrorProvider.SetError(). You only need it if you add any controls after the form's Load event fired or if you need to modify the warning text.
Add a new class to your project and paste the code shown below. Compile. Drop it from the top of the toolbox onto the form. The design-time behavior is identical. Modestly tested.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.ComponentModel.Design;
class MyErrorProvider : ErrorProvider {
public void ShowError(Control ctl, bool enable) {
// Easy to use version of SetError(), uses design-time text
if (!enable) base.SetError(ctl, "");
else {
if (errors.ContainsKey(ctl)) base.SetError(ctl, errors[ctl]);
else base.SetError(ctl, "No error text available");
}
}
public bool HasErrors {
// True if any errors are present
get {
foreach (var err in errors)
if (!string.IsNullOrEmpty(base.GetError(err.Key))) return true;
return false;
}
}
public bool FocusError() {
// Set the focus to the first control with an active error
foreach (var err in errors) {
if (!string.IsNullOrEmpty(base.GetError(err.Key))) {
err.Key.Focus();
return true;
}
}
return false;
}
public new void SetError(Control ctl, string text) {
// Use this only to add/modify error text after the form's Load event
if (!string.IsNullOrEmpty(text)) {
if (errors.ContainsKey(ctl)) errors[ctl] = text;
else errors.Add(ctl, text);
}
base.SetError(ctl, text);
}
private void initialize(object sender, EventArgs e) {
// Preserve error text
copyErrors(((Form)sender).Controls);
}
private void copyErrors(Control.ControlCollection ctls) {
foreach (Control ctl in ctls) {
var text = this.GetError(ctl);
if (!string.IsNullOrEmpty(text)) {
errors.Add(ctl, text);
base.SetError(ctl, "");
}
copyErrors(ctl.Controls);
}
}
private Dictionary<Control, string> errors = new Dictionary<Control, string>();
// Plumbing to hook the form's Load event
[Browsable(false)]
public new ContainerControl ContainerControl {
get { return base.ContainerControl; }
set {
if (base.ContainerControl == null) {
var form = value.FindForm();
if (form != null) form.Load += initialize;
}
base.ContainerControl = value;
}
}
public override ISite Site {
set {
// Runs at design time, ensures designer initializes ContainerControl
base.Site = value;
if (value == null) return;
IDesignerHost service = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (service == null) return;
IComponent rootComponent = service.RootComponent;
this.ContainerControl = rootComponent as ContainerControl;
}
}
}
Your issue is that you are replacing the error message when nothing is wrong. As noted in your comment below, you are storing the localized error message in the label's Tag, so you can do the following:
If TextBox1.Text.Trim.Contains("'") Then
ErrorProvider1.SetError(lblErr, lblErr.Tag)
Else
ErrorProvider1.SetError(lblErr, "")
End If
You were correct to use ErrorProvider1.GetError(Control) to get the value. It's just that you're more than likely replacing it with an empty string before you were retrieving it.