I have a Main Window which includes some User Controls that are initialized in the WPF XAML
MainWindow.xaml.
<Grid>
<local:RegularUnit x:Name="ucRegularUnit" Grid.Row="0" />
<local:Actions x:Name="ucActions" Grid.Row="1" />
// .....
</Grid>
I have a public function in the Main Window which I want to call after clicking a Button in my User Control. After searching for some solutions, I found a way to get the parent window instance in my User Control class, but it can't find the function when I'm using parentWindow.myFunction().
User Control RegularUnit.cs:
public partial class RegularUnit : UserControl
{
public RegularUnit()
{
InitializeComponent();
}
private void Button_SearchSerialNumber_Click(object sender, RoutedEventArgs e)
{
Window parentWindow = Window.GetWindow(this);
//parentWindow. //Can't find the function myFunction()
}
}
MainWindow.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void myFunction()
{
// Do Some Stuff...
}
}
What am I doing wrong, and how can I fix it?
You can't call myFunction on parentWindow because it's not a member of the standard WPF Window class but of your custom MainWindow.
What you could do is to cast the result of Window.GetWindow(this) to MainWindow, like
MainWindow parentWindow = (MainWindow) Window.GetWindow(this);
parentWindow.myFunction();
However this is a really bad class design because now your user control depends on being embedded in a specific window.
What you should rather do is to add an event to the user control on which the parent control can subscribe to.
public event EventHandler SerialNumberSearch;
private void Button_SearchSerialNumber_Click(object sender, RoutedEventArgs e)
{
var handler = SerialNumberSearch;
if (handler != null) handler(this, EventArgs.Empty);
}
Of course you could use a different kind of EventHandler, depending on what you need.
System.Windows.Application.Current.Windows.OfType<YourWindow>().SingleOrDefault(x => x.IsActive).YourPublicMethod();
Although the above code is a messy way of doing it, but it gets the job done nevertheless.
Solution based on event subscription as suggested by Dirk. I have based event on a simple delegate but you can follow similar pattern and base it on a delegate that suits your scenario.
// In UserControl
namespace TextEditor
{
public partial class TextEditorToolBar : UserControl
{
// you can use Action type delegate also
public delegate void getDocumentKeywords();
public event getDocumentKeywords getDocumentRakeKeywordsEvent;
public TextEditorToolBar()
{
InitializeComponent();
}
// This is event handloer for the button on your user control
private void ExtractRakeKeywords(object sender, RoutedEventArgs e)
{
var handler = getDocumentRakeKeywordsEvent;
if (getDocumentRakeKeywordsEvent != null)
getDocumentRakeKeywordsEvent();
}
}
}
// In MainWindow
namespace TextEditor
{
public partial class MainWindow : Window
{
private DocumentKeywordsExtractor KeyWordsExtractor;
public MainWindow()
{
InitializeComponent();
KeyWordsExtractor = new DocumentKeywordsExtractor(richTextBox);
// toolbar is the name given to UserControl in MainWindow.xaml
toolbar.getDocumentRakeKeywordsEvent += ExtractRakeKeywords;
}
private void ExtractRakeKeywords()
{
KeyWordsExtractor.GetRakeKeywords();
}
}
Related
I have a parent form that loads different user controls but when I am trying to access a method on the parent form from a button on the user controller which is not working but if the same method is accessed from the own parent form its all good I excluded the rest of code and here is the code not working
method on Parent Form:
using IT_HUB_MANAGEMENT_SOLUTION.SERVICES;
namespace IT_HUB_MANAGEMENT_SOLUTION.APP_FORMS
{
public partial class FIRST_RUN_FRM : Form
{
public FIRST_RUN_FRM()
{
InitializeComponent();
}
public void NEXT_STEP_CMD()
{
MAIN_PANEL.Controls.Clear();
FIRST_RUN_OBJECTS.CRYSTAL_REPORTS_CONTROL cRYSTAL_REPORTS_CONTROL = new();
cRYSTAL_REPORTS_CONTROL.Dock = DockStyle.Fill;
MAIN_PANEL.Controls.Add(cRYSTAL_REPORTS_CONTROL);
}
}
here is the code on user controls:
public partial class DOTNET_INSTALLER_CONTROL : UserControl
{
public DOTNET_INSTALLER_CONTROL()
{
InitializeComponent();
}
private void NEXT_BTN_Click(object sender, EventArgs e)
{
FIRST_RUN_FRM fff = new();
fff.NEXT_STEP_CMD();
}
}
First of all, this is not a normal behavior (at least for me) that a UserControl calls a parent form method directly. Usually user controls are not aware of the parent that is using them, and for me, it's a really smelly code.
In your code:
public partial class DOTNET_INSTALLER_CONTROL : UserControl
{
public DOTNET_INSTALLER_CONTROL()
{
InitializeComponent();
}
private void NEXT_BTN_Click(object sender, EventArgs e)
{
FIRST_RUN_FRM fff = new();
fff.NEXT_STEP_CMD();
}
}
You're simply creating a new instance of the FIRST_RUN_FORM (Also try to follow the naming convention) and then calling it's function. This won't throw a compiler error since it's a valid .Net syntax. You're creating a new class and calling a public function inside it. However, it won't show anything on your initial form because it's not the same instance.
There's two ways to fix this code, and I don't like the first way I'll show you.
You can add the parent form instance to the user control and pass it on when creating a new instance of the UC. Something like this:
public partial class DOTNET_INSTALLER_CONTROL : UserControl
{
private FIRST_RUN_FRM _parent = null;
public DOTNET_INSTALLER_CONTROL(FIRST_RUN_FRM parent)
{
InitializeComponent();
_parent = parent;
}
private void NEXT_BTN_Click(object sender, EventArgs e)
{
if (_parent == null)
{
return;
}
_parent.NEXT_STEP_CMD(); //This might throw an error not related to this question
}
}
I wouldn't recommend this though.
What I'd go for is using events, this way you don't need the UserControl to know who their parent is or what should be done to the parent when a button inside the UC is clicked.
Event:
In the user control:
public partial class DOTNET_INSTALLER_CONTROL : UserControl
{
public event EventHandler NextButtonClicked;
public DOTNET_INSTALLER_CONTROL(FIRST_RUN_FRM parent)
{
InitializeComponent();
_parent = parent;
}
private void NEXT_BTN_Click(object sender, EventArgs e)
{
OnNextButtonClicked();
}
protected virtual void OnNextButtonClicked()
{
EventHandler handler = NextButtonClicked;
handler?.Invoke(this, null);
}
}
And in the parent form you'll subscribe to this event and react when it's fired.
I got an Problem with Events. I got a first Window which looks like this:
using System.Windows;
namespace EventsTests
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
/*Binding Event to MainWindow
dont work until you will help*/
MainWindow mw = new MainWindow();
mw.RaiseEvent += raiseEvent_EventHandler;
}
public void raiseEvent_EventHandler()
{
MessageBox.Show("MAINWINDOW Event Fired");
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SecondPage sp = new SecondPage();
sp.Show();
}
}
}
Now the seconde Page donĀ“t do very much:
using System.Windows;
namespace EventsTests
{
/// <summary>
/// Interaction logic for SecondPage.xaml
/// </summary>
public partial class SecondPage : Window
{
SecondPageViewModel spvm = new SecondPageViewModel();
public SecondPage()
{
this.DataContext = spvm;
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
spvm.raiseEventActivate();
}
}
}
And at last I have the SecondPageViewModel:
namespace EventsTests
{
public delegate void raiseEventEventHandler();
class SecondPageViewModel
{
public event raiseEventEventHandler raiseEvent;
public void raiseEventActivate()
{
if(raiseEvent != null)
{
raiseEvent();
}
}
}
}
Now I want, when I click the button on the second page, the Event is fired an the MainWindow recognise the event.
With this code i get the Error:
Error 1 Cannot assign to 'RaiseEvent' because it is a 'method group'
Can someone help me? Or give me an example?
Thanks for every hint ;)
RaiseEvent is not your event, it's a method of the Window.
I think you want to do this:
SecondPage sp = new SecondPage();
sp.raiseEvent += raiseEvent_EventHandler;
sp.Show();
That is, register an event handler with the second page event.
Though I wouldn't advocate event handlers for this. While I don't know what you are trying to achieve I'd rather do something like pass a ViewModel object to the SecondPage and the main window can respond to state changes on that ViewModel.
In WPF, I always aim for zero code behind.
In response to discussion, how one VM could have reference to another. First pass the VM in:
SecondPageViewModel spvm;
public SecondPage(SecondPageViewModel model)
{
spvm = model;
this.DataContext = spvm;
InitializeComponent();
}
Then the SecondpageVM takes a MainVM as a paramter in the constuctor:
SecondPage sp = new SecondPage(new SecondPageViewModel(mainVM));
Updates to the main model are done within the SecondPageViewModel. The second page itself has no references to it.
ThirdPage tp = new ThirdPage(new ThirdPageViewModel(spvm))
Third page VM can access main page VM via property on second page vm: spvm.MainVm
In MainWindow you're trying to subscribe to a Window method, instead of your raiseEvent. And certainly you don't need to instantiate another MainWindow...
Your MainWindow code should be something like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void raiseEventFromSecondPage_EventHandler()
{
MessageBox.Show("MAINWINDOW Event Fired");
}
private void Button_Click(object sender, RoutedEventArgs e)
{
SecondPage sp = new SecondPage();
sp.raiseEventFromSecondPage += raiseEventFromSecondPage_EventHandler();
sp.Show();
}
}
You then need that SecondPage exposes the raiseEvent. This will be a different event from the one in its ViewModel, but you'll chain both.
public partial class SecondPage : Window
{
SecondPageViewModel spvm = new SecondPageViewModel();
public event raiseEventEventHandler raiseEventFromSecondPage;
public SecondPage()
{
this.DataContext = spvm;
spvm.raiseEvent += raiseEvent_EventHandler;
InitializeComponent();
}
public void raiseEvent_EventHandler()
{
if (raiseEventFromSecondPage != null)
raiseEventFromSecondPage();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
spvm.raiseEventActivate();
}
}
Reuirement :
Need to show all child pages in a master layout. Without opening as new window i mean it should not be visible as separate window from main window
Solution i figured :
I used content presenter in main page.
Create all other pages as User controls.
On click of menu ViewWindow.Content = new SalesEntry();
By using that i am showing that.
Problem :
To close that user control i used a button click (button present inside the user control)
to preform this.Visibility = Visibility.Hidden;
But every time when user request this page the page is initialized and shown.
So, whats the best approach for this to overcome or any other way to solve this.
(I was told not to use any framework as a project requirement)
I am very new WPF..
Please help me in this..
What you are doing is fine, I don't really understand the problem here but I will tell you how I would do it.
You will have a parent view, a Window. You will have many childs, UserControl's.
Inside your window, you should have a way of selecting which child to show. This can be done using buttons or a menu.
When you select a child, you instantiate it as an object and subscribe to its exit event. When this event is fired by the child, you remove that child from your childs in the parent window.
// This one defines the signature of your exit event handler
public delegate void OnExitHandler(UserControl sender);
// This is your child, UserControl
public partial class MyChild : UserControl
{
public event OnExitHandler OnExit;
public MyChild()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.OnExit(this);
}
}
// This is your parent, Window
public partial class MainWindow : Window
{
private MyChild _control; // You can have a List<UserControl> for multiple
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_control = new MyChild();
_control.OnExit += _control_OnExit; // Subscribe to event so you can remove the child when it exits
_content.Content = _control; // _content is a ContentControl defined in Window.xaml
}
private void _control_OnExit(UserControl sender)
{
if(sender == _control)
{
// Or if you have a collection remove the sender like
// _controls.Remove(sender);
_control = null;
_content.Content = null;
}
}
}
If your problem is something else, please comment.
I have a UserControl, that contains a panel, the panel contains a picture box.
When I MouseMove over the Picture Box, I want to update a label on the MainForm.
I have a get/set method on the main form, but how do I use it?? thanks
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
public String MouseCords
{
get { return this.MouseCordsDisplayLabel.Text; }
set { this.MouseCordsDisplayLabel.Text = value; }
}
}
public partial class ScoreUserControl : UserControl
{
public ScoreUserControl()
{
InitializeComponent();
}
private void ScorePictureBox_MouseMove(object sender, MouseEventArgs e)
{
// MainForm.MouseCords("Hello"); //What goes here?
}
}
Actually it's possible to do in your case like:
((MainForm)this.ParentForm).MouseCords = "Some Value Here";
But the right way is with events like Felice Pollano mentinoed:
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
this.myCustomControlInstanse.PicureBoxMouseMove += new EventHandler<StringEventArgs>(myCustomControlInstanse_PicureBoxMouseMove);
}
private void myCustomControlInstanse_PicureBoxMouseMove(object sender, StringEventArgs e)
{
this.MouseCordsDisplayLabel = e.Value // here is your value
}
}
public class StringEventArgs : EventArgs
{
public string Value { get; set; }
}
public partial class ScoreUserControl : UserControl
{
public event EventHandler<StringEventArgs> PicureBoxMouseMove;
public void OnPicureBoxMouseMove(String value)
{
if (this.PicureBoxMouseMove != null)
this.PicureBoxMouseMove(this, new StringEventArgs { Value = value });
}
public ScoreUserControl()
{
InitializeComponent();
}
private void ScorePictureBox_MouseMove(object sender, MouseEventArgs e)
{
this.OnPicureBoxMouseMove("Some Text Here");
}
}
Ideally, you should raise an event for the same.
Create a delegate
public delegate void Update();
in the user control
public class MyUserControl : UserControl
{
public event Update OnUpdate;
}
On the main form register a handler for the user controls event.
public class Main
{
public Main()
{
myUserControl.OnUpdate += new Update(this.UpdateHandler);
}
void UpdateHandler()
{
//you can set the delegate with sm arguments
//set a property here
}
}
On user control,
To raise an event on button click
do this
OnUpdate();
This might give you an idea...
public partial class ScoreUserControl : UserControl
{
public ScoreUserControl()
{
InitializeComponent();
}
private void ScorePictureBox_MouseMove(object sender, MouseEventArgs e)
{
// MainForm.MouseCords("Hello"); //What goes here?
MainForm parent = this.ParentForm as MainForm;
if (parent != null) parent.MouseCordsDisplayLabel.Text = "Hello";
}
}
You have several options:
Create an event on the user control and have to form listen to it (I think this is the recommended way by most C# programmers).
Pass a reference to the main form to the User Control (in the constructor). This way, the user control knows about its MainForm.
Cast this.ParentForm to the MainForm class, then you have the reference.
Options 2 and 3 are somewhat more comfortable and lazy, but the cost is that the user control has to know about the specific class MainForm. The first option has the advantage that you could reuse the user control in another project, because it does not know about the MainForm class.
You should publish an event from the user control and subscribe to it from the main form.
At least this is the pattern suggested for winform. In any case the idea is to make the control "observable" from the agents who need to see the coords, instead of using it as a driver to update the interested agents.
I have a form1.cs and in that form I have a panel1, in the load event of the form1.cs I am adding a control to the panel1. Now my issue is, I have a control called Numbers.cs, I need to add another control to that panel1 but from this control in a button event. How can I do this?
public partial class Number : UserControl
{
public Number()
{
InitializeComponent();
}
private void btnAcceptWelcome_Click(object sender, EventArgs e)
{
//HERE I NEED TO PASS A CONTROL TO THE PANEL1 IN FORM1.CS
//NOT SURE HOW TO DO THIS.
}
}
MORE INFO
So basically I have a folder called UserControls and in that folder I have
Numbers.cs
Letters.cs
Welcome.cs
All of them user controls, then i have a form
Form1.cs
Form1.cs instantiates Welcome and it is added to a Panel1 on the Form1.cs on form load. Then Welcome.cs has a button, when I click this button I need to swap to Numbers.cs. But I dont know how to do this from Welcome.cs
Another way would be to use a Custom Event raised by Numbers and handled by Form1 to pass the control and add it to your Panel's Control Collection.
This is an example of an Custom Event added to UserControl1
Form1
public partial class Form1 : Form
{
UserControl2 mySecondControl = new UserControl2();
public Form1()
{
InitializeComponent();
userControl11.AddControl+=new EventHandler(SwapControls);
}
private void SwapControls(object sender, EventArgs e)
{
panel1.Controls.Remove(userControl11);
userControl11.AddControl -= new EventHandler(SwapControls);
panel1.Controls.Add(mySecondControl);
}
}
UserControl
public partial class UserControl1 : UserControl
{
public event EventHandler AddControl;
public UserControl1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
this.AddControl(this, new EventArgs());
}
}
Note:
Untested code
Assuming Form1 has (or can get) a reference to Number
Add an event handler to Number:
public partial class Number : UserControl
{
// event handler Form1 will subscribe to
public EventHandler<EventArgs> OnWelcomeAccepted = (o, e) => { };
public Number()
{
InitializeComponent();
}
private void btnAcceptWelcome_Click(object sender, EventArgs e)
{
// raise the event
OnWelcomeAccepted(sender, e);
}
}
...Form1 will have a subscription after InitializeComponent(); note the additional subscription to ControlAdded:
public partial class Form1 : Form {
public Form1()
{
InitializeComponent();
this.ControlAdded += Control_Added;
// subscribe to the event and provide the implementation
Number.OnWelcomAccepted += (o, e) => { Controls.Add(GetControl( )); }
}
private void Control_Added(object sender, System.Windows.Forms.ControlEventArgs e)
{
// process size and placement and show
}
}
No other control should be adding anything directly to Form1. Let Form1 control it's children.
One way is to have a reference to panel1 within numbers since both of them are created within form1 you can pass one as an argument to the other's constructor.
It's not very clear from your description but you can just pass the control you want in the constructor or a property. Since in C# objects are always by reference you will be action on the same control in the Button event. You can always write your own event and have the panel register for it. A more complete code sample would be great.
I'm still a little unsure of what you're doing exactly but that's OK. I think the most flexible approach is to create your own custom event. Outside of any class create a delegate:
public delegate void WelcomeClick(object sender, EventArgs e);
Inside Welcome you need to create the event handler, it can be either static or part of the instance:
public event WelcomeClick OnClick;
Inside the Button Click event in welcome you can just call that event with the same parameters:
if (OnClick != null)
OnClick(sender, e);