I have been messing around with c# in Visual Studio (rather new to it) and have been trying to build an application using WPF and I cannot seem to figure out how in my environment to update my WPF view when a button is clicked. I have tried to cull my code down to the relevant information
I have the following scenario in my .cs file
class Program
{
[STAThread]
static void Main(string[] args)
{
try
{
using (VMS.TPS.Common.Model.API.Application app = VMS.TPS.Common.Model.API.Application.CreateApplication("null", "null"))
{
Execute(app);
}
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
static void Execute(VMS.TPS.Common.Model.API.Application app)
{
Window window = new Window();
MainWindow mainWindow = new MainWindow(app);
mainWindow.evalButton.Click += Eval_Click //Button defined in .xaml
//Add a bunch of items
window.ShowDialog();
}
public static void Eval_Click(object sender, EventArgs e)
{
//need to add some more stuff to mainWindow and update window
}
}
My MainWindow.xaml file has class defined as .MainWindow and the MainWindow.xaml.cs file is as follows
public partial class MainWindow : UserControl
{
private VMS.TPS.Common.Model.API.Application _application;
public MainWindow(VMS.TPS.Common.Model.API.Application Application)
{
_application = Application;
InitializeComponent();
}
}
If you want your View layer to update on button press, you can re-assign the DataContext. For example:
public static void Eval_Click(object sender, EventArgs e)
{
this.DataContext = new MyDataContext();
}
But if you're following the MVVM pattern, your DataContext should inherit from the INotifyPropertyChanged interface and you can also just call a PropertyChangedEventHandler event to update specific bindings in the View layer. For example:
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void UpdateView()
{
NotifyPropertyChanged("foo");
NotifyPropertyChanged("bar");
}
...
public static void Eval_Click(object sender, EventArgs e)
{
(this.DataContext as MyDataContext).UpdateView();
}
Related
I have a user control(VCtrlDetails in the code below) that hosts a data grid(detailsGrid) which is private.
Now, i have this control loaded in another user control(UcResult_Details) and i want to handle grid selection changed event in this another user control.
public partial class VCtrlDetails : UserControl
{
public event EventHandler<bool> EnableEditTemplateButton;
private void InitializeComponent()
{
private System.Windows.Forms.DataGrid detailsGrid;
this.detailsGrid.SelectionChanged += new
System.EventHandler(this.detailsGrid_SelectionChanged);
}
private void detailsGrid_SelectionChanged(object sender, EventArgs e)
{
EnableEditButton?.Invoke(this, IsApproved());
}
public bool IsApproved()
{ }
}
public partial class UcResult_Details : UserControl
{
private readonly VCtrlDetails vCtrlDetails;
UcResult_Details()
{
//Need to subscribe to vCtrlDetails' grid selection changed event here in this ctor
}
}
I'm not that well versed with event handlers, so stuck with the solution as the grid object is private in the user control 'VCtrlDetails', so cannot directly do something like:
vCtrlDetails.detailsGrid.SelectionChanged += DetailsGrid_SelectionChanged
You need to bubble the event up and out of the VCtrlDetails class. You could do so by creating an event within the VCtrlDetails class and allowing your UcResult_Details class to subscribe to it.
public partial class VCtrlDetails : UserControl
{
public event EventHandler<bool> EnableEditTemplateButton;
public event EventHandler<EventArgs> DetailsGridSelectionChanged;
private void InitializeComponent()
{
private System.Windows.Forms.DataGrid detailsGrid;
this.detailsGrid.SelectionChanged += new
System.EventHandler(this.detailsGrid_SelectionChanged);
}
private void detailsGrid_SelectionChanged(object sender, EventArgs e)
{
EnableEditButton?.Invoke(this, IsApproved());
//Raise your custom event
DetailsGridSelectionChanged?.Invoke(this, e);
}
public bool IsApproved()
{
}
}
public partial class UcResult_Details : UserControl
{
private readonly VCtrlDetails vCtrlDetails;
UcResult_Details()
{
//Need to subscribe to vCtrlDetails' grid selection changed event here in this ctor
this.vCtrlDetails.DetailsGridSelectionChanged += new
EventHandler(this.vCtrlDetailsSelectionChanged);
}
private void vCtrlDetailsSelectionChanged(object sender, EventArgs e)
{
//Do whatever
}
}
here on Button click to close mordenwindow..thats doesn't work
private void Button_Click_1(object sender, RoutedEventArgs e)
{
// this MainWindow is like this --> <mui:ModernWindow x:Class="FirstFloor.ModernUI.App.MainWindow1" ....>
MainWindow1 mw = new MainWindow1();
// this is my login Page..
Login lg = new Login();
lg.Show();
mw.Close(); //here code is not working
}
What you did in that Button_Click_1 event is you created a new ModernWindow1 then you closed that newly created ModernWindow1, . Now, you technically have two ModernWindow1 in the start of that event. What you need is to close the currently running ModernWindow1, and not the newly created ModernWindow1. to do that, you need to reference the old ModernWindow1 before going to another window.
This is the Second ModernWindow
public partial class ModernWindow2 : ModernWindow
{
public dynamic ReferencedWindow2; //you will put the original Window here
public ModernWindow2()
{
InitializeComponent();
}
public ModernWindow2(dynamic referencedWindow) // second constructor with a parameter
{
InitializeComponent();
ReferencedWindow2 = referencedWindow; // the original modernwindow being put in here
}
private void Button_OnClick(object sender, RoutedEventArgs e)
{
ReferencedWindow2.Close();
}
}
THIS IS THE ORIGINAL OR PRIMARY MODERNWINDOW
public partial class ModernWindow1 : ModernWindow
{
public ModernWindow1()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
/*
this will show the second modernwindow using the second constructor with parameter
*/
ModernWindow2 newWindow2 = new ModernWindow2(this);
newWindow2.Show();
}
}
I am still very new to WPF and I've spent a week trying to understand MVVM with not very much luck, but I am not specifically trying to follow those rules, I just need to store information into a class so all of my user controls can access it and also change it.
This is what I have so far:
namespace WpfApplication2.Funtions
{
public class Binder : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private string _Test_String;
public string Test_String
{
get { return _Test_String; }
set { _Test_String = value; OnPropertyChanged("Test_String"); }
}
}
}
User control:
public partial class UserControl1 : UserControl
{
//Funtions.Binder _B = new Funtions.Binder();
public UserControl1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MainWindow MW = new MainWindow();
textBox1.Text = MW._B.Test_String;
}
}
Mainwindow:
public partial class MainWindow : Window
{
public Funtions.Binder _B = new Funtions.Binder();
public MainWindow()
{
InitializeComponent();
_B.Test_String = "HELLO";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
_B.Test_String = "Hello from main";
}
}
I see what is happening here, the user control is creating a new instance of MainWindow so it is NOT the same value, but how do I actually access the same Binder instance that MainWindow created from usercontrol1 so that it can read and change it? or if I can't achieve what I want this way how should I go about doing it? I know there has got to be a very simple solution to this. I don't want to use the project Settings.
Please don't mind the super noob question but I've been stuck for days now, I just need a simple explanation and solution and I'll be on my way.
Thank you.
You need to grab the instance of your main window, not create a new one as you figured out.
private void button1_Click(object sender, RoutedEventArgs e)
{
MainWindow MW = Application.Current.MainWindow;
textBox1.Text = MW._B.Test_String;
}
you should use same instance of main window on usercontrol's button click . like as follows
var mainwindow =Application.Current.MainWindow;
and using same instance get value
textBox1.Text = mainwindow .Test_String;
I've followed this question and tried to build my solution. The problem is that 'UserControlButtonClicked' appears to be null! So 'UserControlButtonClicked(this, EventArgs.Empty)' inside the if, doesn't run, and the method 'addStepContent' in the parent page is never called.
UserControl 'StepsBar'
public sealed partial class StepsBar : UserControl
{
public event EventHandler UserControlAddStepContent;
[...]
public StepsBar()
{
this.InitializeComponent();
Image step_1 = new Image();
ButtonInfo step_1Info = new ButtonInfo();
step_1Info.Add((int)stepNumber.one, (int)stepStatus.normal);
step_1.Tag = step_1Info;
step_1.Source = setBackground((int)stepStatus.normal);
step_1.Tapped += stepTapped;
[...]
}
public void stepTapped(Object sender, RoutedEventArgs e)
{
[...]
if (step != null)
{
[...]
firePageEvent();
}
}
public void firePageEvent()
{
if (UserControlAddStepContent != null)
{
UserControlAddStepContent(this, EventArgs.Empty);
}
}
Parent Page
public Violation()
{
this.InitializeComponent();
StepsBar stepsBar = new StepsBar();
stepsBar.UserControlAddStepContent += new EventHandler(addStepContent);
}
private void addStepContent(object sender, EventArgs e)
{
CheckBox check_1 = new CheckBox();
check_1.Content = "Check me!";
bodyStackPanel.Children.Add(check_1);
}
This assumes that you want to use an existing delegate rather than make your own and you aren't passing anything specific to the parent page by event args.
In the user control's code-behind (adapt as necessary if not using code-behind or C#):
public partial class MyUserControl : System.Web.UI.UserControl
{
public event EventHandler UserControlButtonClicked;
private void OnUserControlButtonClick()
{
if (UserControlButtonClicked != null)
{
UserControlButtonClicked(this, EventArgs.Empty);
}
}
protected void TheButton_Click(object sender, EventArgs e)
{
// .... do stuff then fire off the event
OnUserControlButtonClick();
}
// .... other code for the user control beyond this point
}
In the page itself you subscribe to the event with something like this:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// hook up event handler for exposed user control event
MyUserControl.UserControlButtonClicked += new
EventHandler(MyUserControl_UserControlButtonClicked);
}
private void MyUserControl_UserControlButtonClicked(object sender, EventArgs e)
{
// ... do something when event is fired
}
}
Solved. The problem was this, on the parent page.
StepsBar stepsBar = new StepsBar();
stepsBar.UserControlAddStepContent += new EventHandler(addStepContent);
The istance of StepsBar was not added to the page. D'OH!
So here's what I've done:
stepsBar.UserControlAddStepContent += new EventHandler(addStepContent);
and on the xaml of the parent page:
<local:StepsBar x:Name="stepsBar"/>
I have a issue with passing information from one wpf window to another. For some reason when main window is loaded nothing is set in the label, I need to be able to keep the data in a string to use for anything (label not important but shows what I mean)?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string MyData { get; set; }
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
label1.Content = MyData;
}
public partial class LoginWindow : Window
{
public LoginWindow()
{
InitializeComponent();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
string mytext = "blabla";
MainWindow fromloginwindow = new MainWindow();
fromloginwindow.Mydata = mytext;
}
Or am I doing this the wrong way round?
EDIT:
Please do not go on a tangent about the label its unimportant I need to be able to get and set a string for use anywhere in the MainWindow. Also the string "mytext" is also irrelevant as obviously I will not be setting the string this way.
It sounds like you are running into an event lifecycle issue; the calls to the Loaded event happen pretty quickly and thus, the chance to set the text has passed. Instead, what you should do is either:
1) Bind the Property to the Label in the XAML
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
protected string _myData = string.Empty;
public string MyData
{
get { return _myData; }
set { _myData = value; NotifyPropertyChanged("MyData"); }
}
protected void NotifyPropertyChanged(string propName)
{
var methods = PropertyChanged;
if(methods != null)
methods(this, new PropertyChangedEventArgs(propName));
}
<Label Content="{Binding MyData}" />
2) Set the control text via another method (or inside the property declaration):
public void SetLabel(string text)
{
label1.Content = text;
}
protected void button2_Click(object sender, RoutedEventArgs e)
{
MainWindow x = new MainWindow();
x.SetLabel("blabla");
}
The Loaded event occurs before you set MyData, change the code like this:
public partial class MainWindow : Window
{
public MainWindow(string data)
{
MyData = data
InitializeComponent();
}
Have you tried passing the value to the second window through the window's constructor?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MainWindow(string data)
: this()
{
label1.Content = data;
}
}
public partial class LoginWindow : Window
{
public LoginWindow()
{
InitializeComponent();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
string mytext = "blabla";
MainWindow fromloginwindow = new MainWindow(mytext);
}
}