In WPF I got one window in which there is a button and big text block, and the class in which I got a public method which sets the text to the text block in the window. The problem is, that this doesn't work and I quite don't understand why. The code looks like this:
In MainWindow.xaml.cs
private void GoButton_Click(object sender, RoutedEventArgs e)
{
Watcher watcher = new Watcher();
watcher.StoryMode();
}
And here is the public void StoryMode from class Watcher:
string text1 = #"C:/Users/Desktop/Folder/1.txt";
string mainText;
MainWindow mw = new MainWindow();
public void StoryMode()
{
mainText = text1;
mw.textBlock_story.Text = File.ReadAllText(mainText);
}
Every time when I click on the button, nothing happen and when I try to debug, I can see that it calls the function from the Watch class, but doesn't do anything at all. When I tried same code, but outside the Watch class, inside the MainWindow like this :
private void GoButton_Click(object sender, RoutedEventArgs e)
{
string text1 = #"C:/Users/Desktop/Folder/1.txt";
string mainText;
mainText = text1;
textBlock_story.Text = File.ReadAllText(mainText);
}
All works fine, but I that's not exactly something what I want in this case.
Could somebody explain me why does this happen and how to get a better solution for this problem?
Your window class needs to create an instance of the watcher class. It should then extract the text data from the watcher class.
class Watcher
{
public string mainText;
void StoryMode()
{
mainText = "Example Text";
}
}
public partial class MainWindow : Window
{
string texblock_story_text;
public MainWindow()
{
InitializeComponent();
}
void Get_Text()
{
Watcher MyWatcher = new Watcher();
texblock_story_text = MyWatcher.mainText;
}
}
Related
I am making an application that loads a separate form, the user puts in information, and then when done, it will show up on the primary form the application loaded with first.
The issue is that I tried multiple solutions to get this to load in, but it will not load in after the information is put in. I have tried this.Controls.Add(Label); which is what I have seen the most, but it has not worked. Another way I tried was doing Label.Show();, but the same result, with nothing showing. The AddContacts(string Name) method below is how I add the contact
The AddContact_Click(object sender, EventArgs e) method is a button that, when pressed, opens another form that allows information to be inserted.
public partial class Phonebook : Form
{
public Phonebook()
{
InitializeComponent();
MaximumSize = new Size(633, 306);
}
private void AddContact_Click(object sender, EventArgs e)
{
MakeContact MC = new MakeContact();
MC.Show();
}
public void AddContacts(string Name)
{
Label name = new Label();
//Added Style and Location of Label...
name.Text = Name;
name.Location = new Point(98, 13);
name.Font = new Font("Microsoft Sans Serif", 13, FontStyle.Bold);
this.Controls.Add(name);
Refresh();
}
}
Below is the Method I used when the Finish button is pressed, for when the user is done with the information, and then the AddContacts() method is called
public partial class MakeContact : Form
{
public MakeContact()
{
InitializeComponent();
MaximumSize = new Size(394, 377);
}
private void FinishContact_Click(object sender, EventArgs e)
{
//FullName is the name of the TextField when asking for a name
string Name = FullName.Text;
Phonebook PB = new Phonebook();
PB.AddContacts(Name);
//Closes Separate Form and goes back to the
Close();
}
}
Expectation:
It should load the label into the form after the information is put in.
Actual:
It will not show what so ever.
EDIT: Added More to the Code and to the Question since I didn't do too good of asking the question, sorry about that :/
An example of what I described in the comments:
When you do this:
Phonebook PB = new Phonebook();
you create a new instance of the PhoneBook class (your form): this is not the same Form instance (the same object) that created the MakeContact Form and the one you're trying to update. It's a different object.
Whatever change you make to this new object, it will not be reflected in the original, existing, one.
How to solve:
Add a Constructor to the MakeContact Form that a accepts an argument of type PhoneBook and a private object of type Phonebook:
private PhoneBook pBook = null;
public MakeContact() : this(null) { }
public MakeContact(PhoneBook phoneBook)
{
InitializeComponent();
this.pBook = phoneBook;
}
Assign the argument passed in the constructor to the private field of the same type. This Field will then used to call Public methods of the PhoneBook class (a Form is a class, similar in behaviour to other class).
It's not the only possible method. You can see other examples here.
Full sample code:
public partial class Phonebook : Form
{
private void AddContact_Click(object sender, EventArgs e)
{
MakeContact MC = new MakeContact(this);
MC.Show();
}
public void AddContacts(string Name)
{
Label name = new Label();
// (...)
this.Controls.Add(name);
}
}
public partial class MakeContact : Form
{
private PhoneBook pBook = null;
public MakeContact() : this(null) { }
public MakeContact(PhoneBook phoneBook)
{
InitializeComponent();
this.pBook = phoneBook;
}
private void FinishContact_Click(object sender, EventArgs e)
{
string Name = FullName.Text;
this.pBook?.AddContacts(Name);
this.Close();
}
}
So this sounds strange but I always get a stackoverflow exception when I execute 'this.Content' 3 times.
So I have a main window which stores all userControls so I dont have to create them always:
public partial class MainWindow : Window
{
CreateSessionWindow csw;
RateSessionWindow rsw;
CloseSessionWindow closesw;
MainMenuWindow mmw;
public MainWindow()
{
InitializeComponent();
csw = new CreateSessionWindow();
rsw = new RateSessionWindow();
closesw = new CloseSessionWindow();
mmw = new MainMenuWindow();
AllSessionWindows.csw = csw;
AllSessionWindows.rsw = rsw;
AllSessionWindows.closesw = closesw;
AllSessionWindows.mmw = mmw;
}
private void bttnStartProgram_Click(object sender, RoutedEventArgs e)
{
this.Content = AllSessionWindows.mmw;
}
}
public static class AllSessionWindows
{
public static RateSessionWindow rsw;
public static CloseSessionWindow closesw;
public static CreateSessionWindow csw;
public static MainMenuWindow mmw;
}
In my MainMenuWindow class I have a button and when I click on the button it changes the content:
public partial class MainMenuWindow : UserControl
{
public MainMenuWindow()
{
InitializeComponent();
}
private void bttnCreateSession_Click(object sender, RoutedEventArgs e)
{
this.Content = AllSessionWindows.csw; //here
}
}
And here is where I get usually the stackoverflowexception:
public partial class CreateSessionWindow : UserControl
{
public CreateSessionWindow()
{
InitializeComponent();
}
private void bttnGoBack_Click(object sender, RoutedEventArgs e)
{
this.Content = AllSessionWindows.mmw; //here I always get the exception
}
}
So no matter in which order I call this.Content (for eg. first mmw and than csw or csw and than mmw) I always get a stackoverflow Exception when I call it 3 times which you can see above. What could be the problem be?
The problem in your code is this.Content=... in UserControls (in this case this.Content is UserControl content not Window content). If you want to change content in the main window you should add property with MainWindow to class AllSessionWindows:
public static class AllSessionWindows
{
public static MainWindow MainWindow;
public static RateSessionWindow rsw;
public static CloseSessionWindow closesw;
public static CreateSessionWindow csw;
public static MainMenuWindow mmw;
}
In the MainWidnow constructor you must assign this property:
public MainWindow()
{
InitializeComponent();
...
AllSessionWindows.MainWindow = this;
}
And in UserControl you should use following code:
private void bttnCreateSession_Click(object sender, RoutedEventArgs e)
{
AllSessionWindows.MainWindow.Content = AllSessionWindows.csw;
}
Presented solution to this kind of problem by you is not the best solution. For this kind of problem, you can use Caliburn.Micro framework.
In the following link you can find a good tutorial:
http://www.mindscapehq.com/blog/index.php/2012/1/12/caliburn-micro-part-1-getting-started/
Your problem is described in part 5 and 6 of this tutorial.
I'm having trouble assigning the value of a variable from one class to another class. I have tried to do it in several ways but none works, the variable still has no value inside my method.
First Class:
namespace Simulador
{
public partial class Cidade : Window
{
private int QNTTodinho;
private void todinhobotao_Click(object sender, RoutedEventArgs e)
{
TodinhoBotaoo();
}
public void TodinhoBotaoo()
{
QNTTodinho += 1;
MainWindow Valor = new MainWindow(QNTTodinho);
}
}
}
Second Class:
namespace Simulador
{
public partial class MainWindow : Window
{
private int QNTTodinho;
public MainWindow(int qNTTodinho)
{
QNTTodinho = Convert.ToInt32(qNTTodinho);
}
private void Salgadinho_Click(object sender, RoutedEventArgs e)
{
Dinheiro22.Content = QNTTodinho.ToString();
}
}
}
I am guessing that the startup form for your application is
MainWindow. Which means you are creating an instance of MainWindow and running it as the main application window.
If this is the case, then creating another instance of MainWindow like what you are doing and passing the integer to it in the constructor wouldn't affect the actual main window. It is just setting the value in a copy of the MainWindow and directly scrapping that copy afterwards.
One way to fix that, although not the best practice, is to change the member you want to modify to be a static member, like that:
public partial class MainWindow : Window
{
public static int QntTodinho { get; set; }
public MainWindow()
{
}
private void Salgadinho_Click(object sender, RoutedEventArgs e)
{
Dinheiro22.Content = this.QntTodinho.ToString();
}
}
public partial class Cidade : Window
{
private int qntTodinho = 0;
private void todinhobotao_Click(object sender, RoutedEventArgs e)
{
TodinhoBotaoo();
}
public void TodinhoBotaoo()
{
this.qntTodinho += 1;
MainWindow.QntTodinho = this.qntTodinho;
}
}
Notice as well that I've done the following changes:
Used pascalCasing for private member
Used CamelCasing for public member
Get rid of Convert.ToInt32 as it is not needed
Things to consider with this solution are mainly thread safety and the lack of ways to notify MainWindow when the value changes.
Scenario:
Three forms: MainWindow, Positions, Calibration self-named (MainWindow : Window etc.)
MainWindow creates an instance of three objects:
Vars: Model Vars = new Model();
Positions: Positions PositionsWindow = new Positions();
Calibration: Calibration CalibrationWindow = new Calibration();
A button in MainWindow is used to Show and Hide the child windows. Form fields in MainWindow update fields in class Vars.
MainWindow code:
public partial class MainWindow : Window
{
Model Vars = new Model();
Positions PositionsWindow = new Positions();
Calibration CalibrationWindow = new Calibration();
private void OpenButton_Click(object sender, RoutedEventArgs e)
{
PositionsWindow.Show();
}
private void TextBoxUpdate_Click(object sender, RoutedEventArgs e)
{
Vars.TestVar = TestBox.Text;
}
}
Question: How can form fields in the child windows update the parent form fields and/or fields in the class "Vars", i.e. passing data from child to parent and trigger an action in the parent form?
Attempts: A similar question suggested passing the main window this, example: Positions PositionsWindow = new Positions(); however, this only works when the object is created in a method. At this point, PositionsWindow.Show(); is no longer valid. i.e. it is only suitable for a child window created and closed in a single method.
I would not really recommend initializing the variables before the constructor. Don't get used to that.
I would change the constructor of each of the three Windows:
public partial class Model : Window{
MainWindow MW;
Model(MainWindow MW){
this.MW = MW;
// other constructor stuff
}
}
Do the same thing for Positions and Calibration.
Obviously, you cannot use this when you are INITIALIZING the Windows BEFORE the constructor is called, because there is still no this to pass.
Therefore, in your MainWindow:
public partial class MainWindow : Window
{
Model Vars; // = new Model(this); <- the constructor was not yet called, there is no this
Positions PositionsWindow; // = new Positions();
Calibration CalibrationWindow; // = new Calibration();
MainWindow(){
Vars = new Model(this);
Positions = new Positions(this);
CalibrationWindow = new Calibration(this);
}
private void OpenButton_Click(object sender, RoutedEventArgs e)
{
PositionsWindow.Show();
}
private void TextBoxUpdate_Click(object sender, RoutedEventArgs e)
{
Vars.TestVar = TestBox.Text;
}
}
Edit: (to complete the answer to the question):
Now, if you want the Windows to change stuff to each other, just create functions in MainWindow that change stuff in each of the Windows. And with MW you can call these functions from any child Window
For me the best is using Subscribe/Publisher event-based way, here is the way to do it. (i recreate the code so that you can understand)
1) add an event publisher in your child windows.
public partial class ChildWindows : Window
{
// the is the event publisher
public event Action<string> ChildUpdated;
public ChildWindows()
{
InitializeComponent();
}
private void updateParentBtn_Click(object sender, RoutedEventArgs e)
{
// pass the parameter.
ChildUpdated(updateTb.Text);
}
}
2) Subscribe the publisher in your parent windows.
public partial class MainWindow : Window
{
Model Vars;
ChildWindows childWindows;
public MainWindow()
{
InitializeComponent();
Vars = new Model();
childWindows = new ChildWindows();
//this is the event subscriber.
childWindows.ChildUpdated += ChildWindows_ChildUpdated;
}
//do whatever you want here.
void ChildWindows_ChildUpdated(string obj)
{
// Update your Vars and parent
Vars.TestVar = obj;
updateLbl.Content = Vars.TestVar;
}
private void openButton_Click(object sender, RoutedEventArgs e)
{
childWindows.Show();
}
private void textBoxUpdate_Click(object sender, RoutedEventArgs e)
{
}
}
3) In this case, when i type inside the textbox in my child windows, and press a button, it will appear on a label inside my parent windows.
P/S : i had changed the ParentUpdated to ChildUpdated. thanks to #Adrian for constructive feedback
example
I'm working on a project and need to access a label from a normal class.cs.
NOT from the MainWindow.xaml.cs!
MainWindow.xaml: contains a Label lblTag.
Class.cs needs to execute:
lblTag.Content = "Content";
How can I realize it?
I just end up with InvalidOperationExceptions.
Window1.xaml.cs:
public Window1()
{
InitializeComponent();
[...]
}
[...]
StreamElement se1;
StreamElement se2;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
[...]
se1 = new StreamElement(this);
se2 = new StreamElement(this);
[...]
}
[...]
StreamElement.cs:
[...]
private Window1 _window;
[...]
public StreamElement(Window1 window)
{
_window = window;
}
[...]
//metaSync is called, whenever the station (it's a sort of internet radio recorder)
//changes the meta data
public void metaSync(int handle, int channle, int data, IntPtr user)
{
[...]
//Tags just gets the meta data from the file stream
Tags t = new Tags(_url, _stream);
t.getTags();
//throws InvalidOperationException - Already used in another thread
//_window.lblTag.Content = "Content" + t.title;
}
[...]
You need a reference to an instance of MainWindow class in your Class:
public Class
{
private MainWindow window;
public Class(MainWindow mainWindow)
{
window = mainWindow;
}
public void MyMethod()
{
window.lblTag.Content = "Content";
}
}
You need to pass a reference to your window instance to the class. From inside your MainWindow window code behind you would call:
var c = new Class(this);
c.MyMethod();
EDIT:
You can only access controls from the same thread. If your class is running in another thread you need to use the Dispatcher:
public void metaSync(int handle, int channle, int data, IntPtr user)
{
[...]
//Tags just gets the meta data from the file stream
Tags t = new Tags(_url, _stream);
t.getTags();
//throws InvalidOperationException - Already used in another thread
//_window.lblTag.Content = "Content" + t.title;
_window.lblTag.Dispatcher.BeginInvoke((Action)(() =>
{
_window.lblTag.Content = "Content" + t.title;
}));
}
after the edit, this seems clearer now. Damir's answer should be correct.
Just add a mainwindow object on Class.cs and pass the mainwindow's instance to the class's constructor.
on mainwindow.xaml.cs
...
Class class = new Class(this);
...
on Class.cs
...
MainWindow mWindow = null;
// constructor
public Class(MainWindow window)
{
mWindow = window;
}
private void Initialize()
{
window.lblTag.Content = "whateverobject";
}