i would like change the text of in a class i transfered from another class in an Entry ,this is my code
public class contentPage:ContentPage
{
public Entry TextEnrty;
public Button doneButton;
public contentPage()
{
doneButton.Clicked+=doneButtonClicked;
Content =new StackLayout
{
Children = {TextEntry,doneButton}
}
}
private void doneButton_Clicked(object sender, System.EventArgs e)
{
App.Current.MainPage= new ContentPage2(TextEntry.text)
}
}
public class ContentPage2:ContentPage
{
public label TextLabel;
public Button EditButton;
public ContentPage2(string parameter)
{
EditButton.clicked+=EditButtonClicked;
TextLabel.Text = parameter;
Content =new StackLayout
{
Children = {TextLabel,EditButton}
}
}
private void EditButtonClicked(object sender, System.EventArgs e)
{
App.Current.MainPage= new contentPage()
}
}
According to the code when Text is entered in the TextEntry in ContentPage, The TextEntry.Text Is sent as a parameter to contentpage2 after doneButton is clicked, and the value is set equal to TextLabel.Text,
Now, i want to edit the TextLabel.text,but since i am not using the navigation button bit EditButton, the old text will not show in the TextEntry after i go back to edit the text.
So my problem is that , i want the old text to show when i go back to chenge the text and not an empty Entry.
Why don't you just make a second constructor like this:
contentPage(string entryText)
{
InitializeComponent();
TextEnrty.Text=entryText;
}
And in the line in your secon class where you "navigate" back to your class, when creating a new instance of contentPage use this new Constructor.
you should try MVVM pattern and share viewmodel across this two view
<Application.Resources>
<local:ViewModel x:Key="sharedViewModel" />
</Application.Resources>
Related
I'm developing a Windows application (UWP) that has two pages, I want the best practice to pass parameters between pages.
it's my scenario:
We have two pages, each open and remain at the middle of the screen and a Button on each page, which send the message to the other page when we click on it.
I also want to pass information continuously and repeatedly.
in Page1.cs:
Page2 page2;
public Page1()
{
this.InitializeComponent();
CreatPage2();
}
// creat page 2
private async void CreatPage2()
{
var NewWindow = CoreApplication.CreateNewView();
int NewWindowid = 0;
await NewWindow.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
{
Frame newframe = new Frame();
newframe.Navigate(typeof(Page2), this);
Window.Current.Content = newframe;
Window.Current.Activate();
ApplicationView.GetForCurrentView().Title = "page2";
NewWindowid = ApplicationView.GetForCurrentView().Id;
});
await Windows.UI.ViewManagement.ApplicationViewSwitcher.TryShowAsStandaloneAsync(NewWindowid);
}
//Button
private void ChangeP2_Click(object sender, RoutedEventArgs e)
{
// send a message to the texblock in the page2
page2.TexBlock2.Text=$"From page1 :{e.ToString()}";
// change text color of the texblock in the page2
page2.Foreground= new SolidColorBrush(Windows.UI.Colors.Red);
}
in Page2.cs:
Page1 page1;
protected override void OnNavigatedTo(NavigationEventArgs e)
{
page1 = e.Parameter as Page1;
base.OnNavigatedTo(e);
}
public Page2()
{
this.InitializeComponent();
}
//Button
private void ChangeP1_Click(object sender, RoutedEventArgs e)
{
// send a message to the texblock in the page1
page1.TexBlock1.Text=$"From page2 :{e.ToString()}";
// change text color of the texblock in the page1
page1.Foreground= new SolidColorBrush(Windows.UI.Colors.Red);
}
the above code just work for the page2 to the page1. (it can change the textblock of pagea).
Please help me, I can't find a solution that work on two pages
Naah… the best way is to use a standard pattern that consist of an app ViewModel class, which contains all the common app data that you want to use in the logic layer.
I always do it like this:
1) I use the MainPage automatically created as the "shell" of the app, with a property that is the AppViewModel.
The MainPage (and thus the AppViewModel) can be accessed from everywhere in the app, by setting itself as a static field in its own class.
This is the code, simpler than you think:
public sealed partial class MainPage : Page
{
public AppViewModel ViewModel { get; set; } = new AppViewModel();
public static MainPage Current { get; set; }
public MainPage()
{
this.InitializeComponent();
Current = this;
}
}
2) The AppViewModel itself is a class that must implement the INotifyPropertyChanged interface, in order to enable bindable properties and functions.
It is common, among developers, to create a base class that implements it and then derive all the classes that needs bindable properties from it.
Here it is:
public class BaseBind : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected 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;
}
}
Then you derive AppViewModel class (and all the other model and viewmodel classes) from it… populating it with all the common properties that you need to share across pages.
I have even added a derived property, in order to show how you can share even multiple data types at once, and a function:
public class AppViewModel : BaseBind
{
public AppViewModel()
{
// ...
}
// All common app data
private string sampleCommonString;
public String SampleCommonString
{
get { return sampleCommonString; }
set { SetProperty(ref sampleCommonString, value); OnPropertyChanged(nameof(SampleDerivedProperty1)); OnPropertyChanged(nameof(SampleDerivedProperty2)); }
}
public String SampleDerivedProperty1 => "return something based on SampleCommonString";
public String SampleDerivedProperty2
{
get
{
<<evaluate SampleCommonString>>
return "Same thing as SampleDerivedProperty1, but more explicit";
}
}
// This is a property that you can use for functions and internal logic… but it CAN'T be binded
public String SampleNOTBindableProperty { get; set; }
public void SampleFunction()
{
// Insert code here.
// The function has to be with NO parameters, in order to work with simple {x:Bind} markup.
// If your function has to access some specific data, you can create a new bindable (or non) property, just as the ones above, and memorize the data there.
}
}
3) Then, in order to access all this from another Page, just create an AppViewModel field in that page, as seen below:
public sealed partial class SecondPage : Page
{
public AppViewModel ViewModel => MainPage.Current.ViewModel;
public SecondPage()
{
this.InitializeComponent();
}
}
...and you can easily bind XAML controls properties to the AppViewModel itself:
<TextBlock Text="{x:Bind ViewModel.SampleCommonString, Mode=OneWay}"/>
<Button Content="Sample content" Click="{x:Bind ViewModel.SampleFunction}"/>
(Mode=OneWay is for real-time binding, in order that the property is immediately updated even in the UI, while Mode=TwoWay is used for those properties that can be edited from the control itself, by the user, in order to interact with app logic).
Hope this helped.
Best regards and happy new year.
I have two windows: (1) Home and (2) Add
What I want to do is to update the DataGrid on Home whenever I hit the Submit button on the Add page.
The data loaded in the DataGrid is a CSV File. I'm hoping to accomplish this without the use of MVVM if that is possible.
So far, the code that I have works but only on the first run. For example, I fill up the fields in Add then hit the Submit button for the first time. The DataGrid would update but when I try it for a second time (without closing the application), it would no longer do what I want it to do. How can I resolve this?
Submit button click event in Add
public void btn_Submit_Click(object sender, RoutedEventArgs e)
{
save();
string edata = #"Employees.txt";
Home h = new Home();
h.grid.ItemsSource = edata.ToList();
btn_clear_Click(sender, e);
error_mssg.Text = "** SUCCESS! **";
}
Home page xaml
<StackPanel Margin="0 10 0 0">
<DataGrid x:Name="grid" Height="400" ItemsSource="{Binding}"/>
</StackPanel>
Home page cs
public void Populate()
{
DataTable table = LoadDataGrid.display(#"EmployeeData.txt");
grid.DataContext = table;
}
LoadDataGrid cs
public static DataTable display(string path)
{
DataTable dt = new DataTable();
using (StreamReader sr = new StreamReader(path))
{
string[] headers = sr.ReadLine().Split(',');
foreach (string header in headers)
{
dt.Columns.Add(header);
}
while (!sr.EndOfStream)
{
string[] rows = sr.ReadLine().Split(',');
DataRow dr = dt.NewRow();
for (int i = 0; i < headers.Length; i++)
{
dr[i] = rows[i];
}
dt.Rows.Add(dr);
}
}
return dt;
}
I also added the code I used to populate the DataGrid with the contents of the csv file in case it is needed.
I also tried doing
h.grid.Items.Refresh();
instead of
h.grid.ItemsSource = edata.ToList();
I do it this way:
public class Home {
private AddPage ap;
public void createAddPage() {
ap = new AddPage();
ap.setHome(this);
}
public DataGrid getDataGrid() {
return your_datagrid;
}
}
public class AddPage {
private Home home;
public void setHome(Home home) {
this.home = home;
}
public void button_click(/*some params*/) {
DataGrid dg = home.getDataGrid();
//do stuff with dg...
}
}
i have created a simple solution that i think solves your problem. I simplified it to a case where your "Home" window displays a list of names. In the second window, a user can type in a new name that is then added to the list of names displayed in the first window.
Approach:
The solution was to add an event to the second window. When the "Home" window creates an instance of the second window, the home window subscribes to the second window's event. When the second window fires the event, the second window passes the new name to be added to the list displayed on the first window.
So, adding to the list is done in the "Home" window.
Note: The list of items being displayed is actually an ObservableCollection<string>.
Here are the two windows:
Here's the custom EventArgs object that i use to pass the details of the event, in this case, the new name just-added in the second window:
public class NameAddedEventArgs:EventArgs
{
public string NewName { get; set; }
}
Here's the code in my "Home" window. The window is simply name "MainWindow":
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ObservableCollection<string>();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var window1 = new Window1();
window1.ItemAddedEvent += ItemAddedEventHandler;
window1.Show();
}
private void ItemAddedEventHandler(object sender, NameAddedEventArgs e)
{
(DataContext as ObservableCollection<string>).Add(e.NewName);
}
}
So as you can see, when i create "Window1", which is the second window, i subscribe to its event.
Here's the code in Window1:
public partial class Window1 : Window
{
public event EventHandler<NameAddedEventArgs> ItemAddedEvent;
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
RaiseNewNameEvent(txtInput.Text);
}
protected void RaiseNewNameEvent(string newName)
{
var nameAddedEventArgs = new NameAddedEventArgs { NewName=newName };
var handler = ItemAddedEvent;
if (handler != null)
{
handler(this, nameAddedEventArgs);
}
}
}
Here, when the button is clicked to add the name (the name is typed into txtInput), i raise the event, passing in the new name typed-in.
So I have class DigitButton the NormalDigitClick gets called when a button is pressed. and it calls the appendNumber.
public class DigitButton
{
public static void NormalDigitClick(object sender, EventArgs e)
{
Button button = (Button)sender;
Model model = new Model();
model.appendNumber(button.Text);
}
}
The appnedNumber() appends the number and am trying to reflect this number in the textbox in setTextboxVslue() but textbox is not showing anything. Not sure what is going wrong.
public class Model :Form1
{
public static string textBoxValue;
public void appendNumber(string valueToBeAppended)
{
if (textBoxValue == "0")
{
textBoxValue = "";
}
textBoxValue = textBoxValue + valueToBeAppended;
setTextboxValue(textBoxValue);
}
private void setTextboxValue(string textBoxValue)
{
textBox2.Text = textBoxValue;
}
}
You are not accessing the Form that is opened in your application. You are creating a new instance of Model and then you don't even show it. You need to get the instance of the Model form that is already opened in your application. Replace the following two lines:
Model model = new Model();
model.appendNumber(button.Text);
with the following line:
(Application.OpenForms["Model"] as Model).appendNumber(button.Text);
whenever appendTextbox call. create new instance (Model) so the value will not change
try something like this,
Model model = new Model();
Change
Model model = (Model1)button.Parent;
So i have 2 forms.
Form 1 is my main form, and form 2 is where I enter text in a textbox to be displayed on a label on form 1. Also the button to 'confirm' and actually change the entered text of my label is on form 2 which needs to stay that way.
for some reason this does not work.
Form 2 has a text-box and a button, when I press the button, it changes the string value of the designated string.
This string is linked to a label on form 1. the string is being changed so that is not the problem I confirmed this by a adding a button which pops up a message box showing the new string value.
While searching for an answer I found that is must be some sort of refreshing problem, I tried a lot of methods with no success. Only methods that did work where those who would put my button onto form 1 instead of 2.
I've been googling for 3 hours straight on how to fix this problem but either the methods don't work or they change my button from form 2 to my main form (form 1).
Please don't call me lazy I really can't find a method that works!
EDIT:
Code
GameScreen.cs
namespace _2pGame
{
public partial class GameScreen : Form
{
public GameScreen()
{
InitializeComponent();
P1NameLabel.Text = gm.P1Name;
P1ClassLabel.Text = gm.P1Class;
P2NameLabel.Text = gm.P2Name;
P2ClassLabel.Text = gm.P2Class;
}
private void PlayerInfoButton_Click(object sender, EventArgs e)
{
PlayerInfo playerinfoload = new PlayerInfo();
playerinfoload.Show();
}
}
}
PlayerInfo.cs
namespace _2pGame
{
public partial class PlayerInfo : Form
{
public PlayerInfo()
{
InitializeComponent();
}
public void ConfirmPlayerInfo_Click(object sender, EventArgs e)
{
gm.P1Class = P1ClassChoice.Text;
gm.P1Name = P1TextBox.Text;
gm.P2Class = P2ClassChoice.Text;
gm.P2Name = P2TextBox.Text;
}
}
}
Refs.cs
namespace _2pGame
{
public partial class gm
{
public static string
P1Class,
P2Class,
P1Name,
P2Name;
}
}
An approach to this very well know situation is through delegates....
In your PlayerInfo form declare
public partial class PlayerInfo : Form
{
// define the delegate type (a parameterless method that returns nothing)
public delegate void OnConfirmPlayer();
// declare a public variable of that delegate type
public OnConfirmPlayer PlayerConfirmed;
.....
public void ConfirmPlayerInfo_Click(object sender, EventArgs e)
{
gm.P1Class = P1ClassChoice.Text;
gm.P1Name = P1TextBox.Text;
gm.P2Class = P2ClassChoice.Text;
gm.P2Name = P2TextBox.Text;
// Check is someone is interested to be informed of this change
// If someone assign a value to the public delegate variable then
// you have to call that method to let the subscriber know
if (PlayerConfirmed != null)
PlayerConfirmed();
}
}
Then in your GameScreen form, just before showing the PlayerInfo form, set the public PlayerInfo.PlayerConfirmed to a method into the GameScreen form class
private void PlayerInfoButton_Click(object sender, EventArgs e)
{
PlayerInfo playerinfoload = new PlayerInfo();
// Subscribe to the notification from PlayerInfo instance
playerinfoload.PlayerConfirmed += PlayerHasBeenConfirmed;
playerinfoload.Show();
}
// Method that receives the notification from PlayerInfo
private void PlayerHasBeenConfirmed()
{
P1NameLabel.Text = gm.P1Name;
P1ClassLabel.Text = gm.P1Class;
P2NameLabel.Text = gm.P2Name;
P2ClassLabel.Text = gm.P2Class;
}
This approach has the advantage to avoid a coupling between the GameScreen and the PlayerInfo. No need to know inside the PlayerInfo the existance of a GameScreen form and the name of its properties. You just publish a delegate that a subscriber could register to be informed of the changes and let the subscriber acts on its own code.
You need a reference to your main form and assign the textbox values each time they need to be updated.
public partial class PlayerInfo : Form
{
private readonly GameScreen _main;
public PlayerInfo(GameScreen main)
{
_main = main;
InitializeComponent();
}
public void ConfirmPlayerInfo_Click(object sender, EventArgs e)
{
gm.P1Class = P1ClassChoice.Text;
gm.P1Name = P1TextBox.Text;
gm.P2Class = P2ClassChoice.Text;
gm.P2Name = P2TextBox.Text;
main.P1NameLabel.Text = gm.P1Name;
main.P1ClassLabel.Text = gm.P1Class;
main.P2NameLabel.Text = gm.P2Name;
main.P2ClassLabel.Text = gm.P2Class;
}
}
You also need to pass the reference when the PlayerInfo form is created
private void PlayerInfoButton_Click(object sender, EventArgs e)
{
PlayerInfo playerinfoload = new PlayerInfo(this); //pass ref to self
playerinfoload.Show();
}
Note that there are other better ways to do this, but this is the easiest that I can think of.
You can probably look at events or Mediator pattern if you want something better.
I'm a beginner at C# who's decided to create a textbased adventure game in a Winform, but I've been struggling with updating the form whenever it needs to update. In the past, I have used something.Invalidate(); to update an image, but apparently that doesn't work for an entire form.
I have a set of labels that display text based on an integer and whenever the value of the integer updates, I'd like the form to show that.
What I have tried thus far:
public partial class GameWindow : Form
{
public void buttonInventory_Click(object sender, EventArgs e)
{
Basic.HP = Basic.HP++;
this.Refresh();
}
}
While the HP updates, the form doesn't show it. Is there anything else I should use than Refresh();? A lot of googling mostly resulted in explanations about Backgroundworkers, but do I really need another thread for something as simple as this?
Why not just make a separate routine for updating values, that you call after every value change. IE: (Note - I don't program in C#):
public partial class GameWindow : Form
{
public void buttonInventory_Click(object sender, EventArgs e)
{
Basic.HP = Basic.HP++;
updateValues();
}
public void updateValues()
{
hp.text = HealthInteger;
basic.text = BasicInteger;
}
}
And call this for evey value change.
A label can't be bind to a string value like an image can be to a picture box.
The simplest solution here is too explicitly set the Text property of the label each time your HP property is changed :
private void RefreshFormWithModel(Basic basic)
{
labelHP.Text = basic.HP;
}
public void buttonInventory_Click(object sender, EventArgs e)
{
Basic.HP = Basic.HP++;
this.RefreshFormWithModel(Basic);
}
If you really want complex binding, here is some lectures.
I'm going to assume the class name of Basic is just Basic
in the class Basic
private int hp;
public int HP
{
get { return hp; }
set { hp = value; HP_Changed(); }
}
public event EventHandler HPChanged;
private void HP_Changed()
{
if (HPChanged != null) { HPChanged(this, new EventArgs()); }
}
in the GameWindow
//where ever you create a new Basic, add to the event handler
Basic Basic = new Basic();
Basic.HPChanged += Basic_HPChanged;
private void Basic_HPChanged(object sender, EventArgs e)
{
Basic b_sender = (Basic)sender;
int NewHealth = b_sender.HP;
//Update whatever value needs to be updated, here
}
Then whenever the HP of the Basic is changed, it will fire an event in the GameWindow to update the appropriate field.