Multiple FXML variables to 1 controller handle - email

How can I make a handle that applies to each of the buttons, so I don't have to make a near-identical handle for each button?
So I have a FXML file with 9 contacts, each looks like this:
<Button fx:id="id1" mnemonicParsing="false" onAction="#handleContactEmailButtonAction" prefHeight="60.0" prefWidth="380.0" style="-fx-background-color: white;">
<graphic>
<VBox style="-fx-background-color: white; -fx-border-color: white;">
<children>
<Label prefHeight="30.0" prefWidth="334.0" text="Jane Doe" />
<Label prefHeight="17.0" prefWidth="349.0" text="jdoe#britishschool.sch.ae" textAlignment="CENTER" />
</children>
</VBox>
</graphic>
</Button>
And this is the controller:
#FXML
private Button id1;
#FXML
private Button id2;
#FXML
private Button id3;
#FXML
private Button id4;
#FXML
private Button id5;
#FXML
private Button id6;
#FXML
private Button id7;
#FXML
private Button id8;
#FXML
private Button id9;
#FXML
protected void handleContactEmailButtonAction(ActionEvent event) {
try {
Desktop desktop = Desktop.getDesktop();
String message = "mailto:"+emailvariable+"?subject=Music%20Bookings";
URI uri = URI.create(message);
desktop.mail(uri);
} catch (Exception e) {
e.printStackTrace();
}
}
So if button with fx:id="id1" is clicked, then email variable changes to the email in the relevant label, and all with one controller?
Any and all help is appreciated, thank you!

Create a Map where you can get the button from event source like:
#FXML
private Button id1;
#FXML
private Button id2;
#FXML
private Button id3;
#FXML
private Button id4;
#FXML
private Button id5;
#FXML
private Button id6;
#FXML
private Button id7;
#FXML
private Button id8;
#FXML
private Button id9;
private Map<Button, String> emailVariables;
#Override
public void initialize(URL location, ResourceBundle resources)
{
emailVariables = new HashMap<Button, String>();
emailVariables.put(id1, "id1#example.com");
emailVariables.put(id2, "id2#example.com");
emailVariables.put(id3, "id3#example.com");
emailVariables.put(id4, "id4#example.com");
emailVariables.put(id5, "id5#example.com");
emailVariables.put(id6, "id6#example.com");
emailVariables.put(id7, "id7#example.com");
emailVariables.put(id8, "id8#example.com");
emailVariables.put(id9, "i91#example.com");
}
#FXML
protected void handleContactEmailButtonAction(ActionEvent event)
{
try
{
Desktop desktop = Desktop.getDesktop();
String message = "mailto:" + emailvariables.get((Button) event.getSource()) + "?subject=Music%20Bookings";
URI uri = URI.create(message);
desktop.mail(uri);
}
catch (Exception e)
{
e.printStackTrace();
}
}
Code is not tested.

Consider creating a custom component:
package application;
import java.awt.Desktop;
import java.io.IOException;
import java.net.URI;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
public class EmailButton extends Button {
#FXML
private Label nameLabel ;
#FXML
private Label addressLabel ;
private final StringProperty name = new SimpleStringProperty();
private final StringProperty emailAddress = new SimpleStringProperty();
public EmailButton() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("emailButton.fxml"));
loader.setRoot(this);
loader.setController(this);
loader.load();
}
public void initialize() {
nameLabel.textProperty().bind(name);
addressLabel.textProperty().bind(emailAddress);
}
#FXML
private void sendEmail() {
try {
Desktop desktop = Desktop.getDesktop();
String message = "mailto:"+getEmailAddress()+"?subject=Music%20Bookings";
URI uri = URI.create(message);
desktop.mail(uri);
} catch (Exception e) {
e.printStackTrace();
}
}
public StringProperty nameProperty() {
return name ;
}
public final String getName() {
return nameProperty().get();
}
public final void setName(String name) {
nameProperty().set(name);
}
public StringProperty emailAddressProperty() {
return emailAddress ;
}
public final String getEmailAddress() {
return emailAddressProperty().get();
}
public final void setEmailAddress(String emailAddress) {
emailAddressProperty().set(emailAddress);
}
}
and emailButton.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<fx:root xmlns:fx="http://javafx.com/fxml/1" type="Button" mnemonicParsing="false" onAction="#sendEmail" prefHeight="60.0" prefWidth="380.0" style="-fx-background-color: white;">
<graphic>
<VBox style="-fx-background-color: white; -fx-border-color: white;">
<children>
<Label fx:id="nameLabel" prefHeight="30.0" prefWidth="334.0" />
<Label fx:id="addressLabel" prefHeight="17.0" prefWidth="349.0" textAlignment="CENTER" />
</children>
</VBox>
</graphic>
</fx:root>
Now in your main FXML you can just do:
<?import application.EmailButton?>
<!-- ... -->
<EmailButton name="Jane Doe" emailAddress="jdoe#britishschool.sch.ae"/>
<EmailButton name="..." emailAddress="..." />
<EmailButton name="..." emailAddress="..."/>
etc

Related

How can I pass object while navigating? [duplicate]

This question already has an answer here:
How pass parameter when navigate to another page (Shell)
(1 answer)
Closed 4 months ago.
I have main page on which I have collection view. I want to navigate to next page after I click submit button .I am able to navigate to the next page but I also want total list of items which I have in my collection how can I achieve that?
I don't know the detail of your code, but you can try to pass the total list of items as the parameter of the next page.(suppose it's name is SecondPage)
You can refer to the following code:
MainPage.cs
public partial class MainPage : ContentPage
{
MyViewModel myViewModel;
public MainPage()
      {
            InitializeComponent();
myViewModel = new MyViewModel();
BindingContext = myViewModel;
}
      private void mCollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
      {
string previous = (e.PreviousSelection.FirstOrDefault() as MyModel)?.Name;
string current = (e.CurrentSelection.FirstOrDefault() as MyModel)?.Name;
}
private async void Button_Clicked(object sender, EventArgs e)
{
// here we can pass the data we need.
var secondPage = new SecondPage(myViewModel.Data);
await Navigation.PushAsync(secondPage);
}
}
MainPage.xaml.cs
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiCollectionApp.MainPage"
xmlns:local="clr-namespace:MauiCollectionApp"
x:Name="myPage"
>
<VerticalStackLayout>
<CollectionView ItemsSource="{ Binding Data}" x:Name="mCollectionView"
SelectionChanged="mCollectionView_SelectionChanged"
SelectionMode="Single"
>
<CollectionView.ItemTemplate>
<DataTemplate>
<HorizontalStackLayout Margin="3" >
<Label Text="{Binding Name}" BackgroundColor="Gray"/>
<Label Text="{Binding Car.Make}" Margin="5,0,5,0" />
<Button Text="delete" Margin="10,0,0,0"
BackgroundColor="Red"
Command="{Binding Path= BindingContext.RemoveEquipmentCommand,Source={Reference mCollectionView }}" CommandParameter="{Binding .}"
/>
</HorizontalStackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button Text="submit" Clicked="Button_Clicked" Margin="10"/>
</VerticalStackLayout>
</ContentPage>
MyViewModel.cs
public class MyViewModel: INotifyPropertyChanged
{
public ObservableCollection<MyModel> Data { get; set; }
public ICommand RemoveEquipmentCommand => new Command<MyModel>(ReMoveItem);
private void ReMoveItem(MyModel obj)
{
System.Diagnostics.Debug.WriteLine(" the selected item's name is: " + obj.Name );
Data.Remove(obj);
}
public MyViewModel() {
Data = new ObservableCollection<MyModel>();
Data.Add(new MyModel { Name ="model_1", Car= new Vehicle {Make="Make1" } });
Data.Add(new MyModel { Name = "model_2", Car = new Vehicle { Make = "Make2" } });
Data.Add(new MyModel { Name = "model_3", Car = new Vehicle { Make = "Make3" } });
Data.Add(new MyModel { Name = "model_4", Car = new Vehicle { Make = "Make4" } });
}
bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Object.Equals(storage, value))
return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
SecondPage.cs
public partial class SecondPage : ContentPage
{
public ObservableCollection<MyModel> Items { get; set; }
public SecondPage(ObservableCollection<MyModel> data )
      { // we can get the passed data here
            InitializeComponent();
            this.Items = data;
      }
}

Popup in .Net Maui application

I use Xamarin community toolkit mauicompat to implement popup for .net maui but when I call to open the popup, it had an error like the image below
and my code here:
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
}
private void BtPop_Clicked(object sender, System.EventArgs e)
{
Navigation.ShowPopup(new PopupServicess()
{
IsLightDismissEnabled = true,
});
}
private void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
}
here my xaml file:
<xct:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
Size="300,300"
x:Class="NailsApp.Views.PopupServicess">
<StackLayout>
<Label Text="Hello from Popup!"/>
</StackLayout>
</xct:Popup>
public partial class PopupServicess : Popup
{
public PopupServicess()
{
}
}

Prism Button click command

Im try to open Drawer menu (Syncfusion control) via Behaviors command
Xaml
<Button x:Name="hamburgerButton"
HeightRequest="50" WidthRequest="50"
HorizontalOptions="Start" FontSize="20"
BackgroundColor="#1aa1d6" >
<Button.Behaviors>
<b:EventToCommandBehavior EventName="Clicked"
Command="{Binding HamburgerButton}" />
</Button.Behaviors>
VM cs
public DelegateCommand HamburgerButton { get; private set; }
public QuickPartViewModel(INavigationService navigationService)
{
HamburgerButton = new DelegateCommand(HamburgerButton_Clicked);
}
public void HamburgerButton_Clicked(object sender, EventArgs e)
{
navigationDrawer.ToggleDrawer();
}
If HamburgerButton_Clicked(object sender, EventArgs e) with arguments then i have error -// Argumment: cannot convert from 'method group' to Action
if I'm remove Argumments it does't work
You're overthinking things. Just bind the command to the button.
in the page:
<Button Command="{Binding HamburgerCommand}" />
in the view model:
public QuickPartViewModel()
{
HamburgerCommand = new DelegateCommand(OnHamburger);
}
public DelegateCommand HamburgerCommand { get; }
private void OnHamburger()
{
navigationDrawer.ToggleDrawer();
}
Give the docs a try...

JavaFX how to update label text value every time?

I am using IntellijIDEA. I have ui.fxml,Controller.java and GUI.java. Here is code:
ui.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-1.0" minWidth="-1.0" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="sample.Controller">
<children>
<Button fx:id="exitBtn" cancelButton="true" layoutX="530.0" layoutY="366.0" mnemonicParsing="false" text="Exit" />
<Button fx:id="stopBtn" layoutX="460.0" layoutY="367.0" mnemonicParsing="false" text="Stop" />
<Button fx:id="startBtn" layoutX="387.0" layoutY="368.0" mnemonicParsing="false" text="Start" />
<Label fx:id="threadsLabel" layoutX="19.0" layoutY="23.0" prefWidth="45.0" text="Threads" />
<Label fx:id="threadsValue" layoutX="160.0" layoutY="26.0" prefWidth="45.0" text="0" />
<Label fx:id="smLabel" layoutX="19.0" layoutY="42.0" prefWidth="108.0" text="submit_sm sended" />
<Label fx:id="smValue" layoutX="160.0" layoutY="42.0" prefWidth="45.0" text="0" />
<Label fx:id="deliverLabel" layoutX="19.0" layoutY="61.0" prefWidth="115.0" text="deliver_sm received" />
<Label fx:id="deliverValue" layoutX="160.0" layoutY="61.0" prefWidth="45.0" text="0" />
<Label fx:id="tpsLabel" layoutX="19.0" layoutY="77.0" prefWidth="78.0" text="Current TPS" />
<Label fx:id="tpsValue" layoutX="160.0" layoutY="77.0" prefWidth="45.0" text="0" />
<Slider fx:id="speed" layoutX="213.0" layoutY="28.0" max="1000.0" min="10.0" prefWidth="317.0" showTickLabels="true" showTickMarks="false" />
</children>
</AnchorPane>
GUI.java
public class GUI extends Application {
public Logger log = Main.getLogger("log");
#Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(getClass().getResource("ui.fxml"));
primaryStage.setTitle("ESME Emulator");
Scene mainScene = new Scene(root, 600, 400);
primaryStage.setScene(mainScene);
primaryStage.show();
}
public void show(String[] args) {
launch(args);
}
}
Controller.java
public class Controller implements Initializable {
public Logger log=Main.getLogger("log");
#FXML
public ResourceBundle resources;
#FXML
public URL location;
#FXML
public Label deliverLabel;
#FXML
public Label deliverValue;
#FXML
public Button exitBtn;
#FXML
public Label smLabel;
#FXML
public Label smValue;
#FXML
public Slider speed;
#FXML
public Button startBtn;
#FXML
public Button stopBtn;
#FXML
public Label threadsLabel;
#FXML
public Label threadsValue;
#FXML
public Label tpsLabel;
#FXML
public Label tpsValue;
public Sample sample;
public Controller()
{
this.sample=new Sample(this);
}
public void initialize(URL location, ResourceBundle resources) {
stopBtn.setDisable(true);
startBtn.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
try {
Main.startEmulation();
startBtn.setDisable(true);
stopBtn.setDisable(false);
}catch(Exception e)
{
log.error("Exception while \"Start\" button click event: "+e.getMessage());
}
}
});
stopBtn.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
try {
Main.stopEmulation();
startBtn.setDisable(false);
stopBtn.setDisable(true);
}catch(Exception e)
{
log.error("Exception while \"Stop\" button click event: "+e.getMessage());
}
}
});
exitBtn.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
try {
Main.stopEmulation();
System.exit(0);
}catch(Exception e)
{
log.error("Exception while \"Exit\" button click event: "+e.getMessage());
}
}
});
}
}
and i have another java class which provides a while endless loop, and i need every iteration the threadsValue to be changed via thredsValue.setText("some text"), how can i do this?
p.s.excuse me for my english...
I did this, and still no result, whats wrong?
UpdateTask updTask = new UpdateTask();
Label.textProperty().bind(updTask.valueProperty());
Thread changes = new Thread(updTask);
changes.setDaemon(true);
changes.setName("UPDATE");
changes.start();
class UpdateTask extends Task<String> {
#Override
protected String call() throws Exception {
while(isRunning()) {
updateValue(Integer.toString(Main.getThreadsCount()));
}
return null;
}
}

How to listen for WindowEvent.WINDOW_SHOWN in the nodes of the scene graph?

It seems WindowEvent.WINDOW_SHOWN never gets dispatched on any of the nodes in the scene graph, nor is there anyway (that I could find) to know when a node is visible/rendered/shown. For example:
TestLauncher.java
package com.example.javafx.event;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TestLauncher extends Application
{
public static void main(String[] args)
{
Application.launch(TestLauncher.class, args);
}
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(TestController.class.getResource("TestView.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
TestController.java
package com.example.javafx.event;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.control.TextField;
import javafx.stage.WindowEvent;
public class TestController implements Initializable
{
#FXML private Parent root;
#FXML private TextField serverAddressInput;
#FXML private TextField usernameInput;
#Override
public void initialize(URL url, ResourceBundle rb)
{
serverAddressInput.setText("127.0.0.1");
//won't work because stage isn't visible yet
trySetFocusOnUsernameInput1();
//apparently Stage never passes on any WindowEvents to the children...
trySetFocusOnUsernameInput2();
}
private void trySetFocusOnUsernameInput1()
{
usernameInput.requestFocus();
}
private void trySetFocusOnUsernameInput2()
{
root.addEventFilter(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>()
{
#Override
public void handle(WindowEvent window)
{
usernameInput.requestFocus();
}
});
root.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>()
{
#Override
public void handle(WindowEvent window)
{
usernameInput.requestFocus();
}
});
}
public void handleWindowShownEvent()
{
usernameInput.requestFocus();
}
}
TestView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox
xmlns:fx="http://javafx.com/fxml"
fx:id="root"
fx:controller="com.example.javafx.event.TestController"
prefHeight="150"
prefWidth="200"
>
<children>
<TextField fx:id="serverAddressInput" />
<TextField fx:id="usernameInput" />
</children>
</VBox>
So, actually, how else can a node become aware of the fact that it's visible/rendered/shown?
I guess one of the possible solutions is to add the following method to TestController.java
public void handleWindowShownEvent()
{
usernameInput.requestFocus();
}
and then change the start method in TestLauncher to the following:
#Override
public void start(Stage stage) throws Exception
{
FXMLLoader loader = new FXMLLoader();
Parent root = (Parent)loader.load(TestController.class.getResourceAsStream("TestView.fxml"));
final TestController controller = (TestController)loader.getController();
stage.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>()
{
#Override
public void handle(WindowEvent window)
{
controller.handleWindowShownEvent();
}
});
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
I would really welcome other solutions as this one seems too clunky...
Another solution that admittedly isn't very sexy but decouples the node from the application:
root.sceneProperty().addListener(new ChangeListener<Scene>() {
#Override
public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
newValue.windowProperty().addListener(new ChangeListener<Window>() {
#Override
public void changed(ObservableValue<? extends Window> observable, Window oldValue, Window newValue) {
newValue.addEventHandler(WindowEvent.WINDOW_SHOWN, new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent event) {
usernameInput.requestFocus();
}
});
}
});
}
});
Made more sense in my case.