Calling parent function from frame - c#

I have have a WPF application Im building with the designer. In it I have a Frame that I am loading a Page in.
To be totally honest Im lost. With a lack of instruction and ability to wrap my head around MVVM I am hoping someone might be able to help me understand how to do what I'm trying to do.
From within the page I need to call a public method 'public void UpdateTxt()'. I know this had been done a million time but I just don't understand. Most of my searches pull web/javascript results too.
I did something similar once before with two windows in a winform environment.
public partial class setupApp : Form
{
private Form1 m_parent;
public setupApp(Form1 frml)
{
InitializeComponent();
m_parent = frml;
}
While the above code works in winform enabling me to locate all public functions from the parent window, I can't seem to translate it to WPF.
I have tried
public partial class Childpage: Page
{
private MainWindow m_parent;
public Childpage(MainWindow mw1)
{
InitializeComponent();
m_parent = mw1;
}
This throws no errors on build, but fails to break mode as soon as the debugger launches. I have no idea what the error means as well.
No Matching Constructor Found on type
Why wont the C# back end code translate? Is there a better way?

I think you set the Source property of the Frame in xaml code like this:
<Frame Source="SamplePage.xaml"/>
in this case you need add a parameter-less constructor to your page.
public partial class SamplePage
{
private MainWindow _parentWindow;
public SamplePage()
{
InitializeComponent();
}
public SamplePage(MainWindow parentWindow) : this()
{
_parentWindow = parentWindow;
}
}
But if you want pass the parent window to child page you can set the Frame content in code-behind. like this:
SampleFrame.NavigationService.Navigate(new SamplePage(this));
in this case you don't need to parameter-less constructor.

Related

Closing a window using ViewModel first MVVM and Stylet

I'm using a MVVM ViewModel first approach with Stylet and I'm struggling to close a window from it's ViewModel.
In the Stylet Wiki it states that I can use:
Screen.RequestClose
I have the following code:
public class MdExportViewModel : Screen
{
public MdExportViewModel()
{
if(canExport == true)
{
this.RequestClose();
}
}
}
When I try to call the 'RequestClose' I get the following error:
System.InvalidOperationException: 'Unable to close ViewModel
Drain.ViewModels.Windows.MdExportViewModel as it must have a conductor
as a parent (note that windows and dialogs automatically have such a
parent)'
I've tried adding the Conductor<T> as follows:
public class MdExportViewModel : Conductor<IScreen>
But I get the same error. I didn't really understand how a conductor should be used in this instance. I assumed my origional attempt would work since note that windows and dialogs automatically have such a parent.
What am I doing wrong here? Other answers to similar questions use complicated workarounds, but I'd like to use a Stylet method to keep things consistent and simple.
EDIT:
The window is opened in another viewmodel as follows:
public void ExportMD()
{
MdExportViewModel MdExportViewModel = new(networkMain, DesignCriteriaViewModel)
{
Parent = this
};
this.windowManager.ShowWindow(MdExportViewModel);
}

How to set focus on the first input in all forms in a WinForms project?

I have one application built with WinForms with a big number of forms on it (approximately 90 forms) and now there is a requirement that when any form is opened the first input should get focused, so that it is ready for being typed into.
I imagine how to do that for a single form. We could run the following:
var firstInput = this.Controls.OfType<TextBox>().FirstOrDefault();
if (firstInput != null)
firstInput.Focus();
That's fine, but replacing this on every form is not just a huge task, but also it is a tremendous amount of code duplication.
I wanted to do that for all forms at once. In that case I created a base class:
public class BaseForm : Form
{
public void SetFoucsOnFirstInput()
{
var firstInput = this.Controls.OfType<TextBox>().FirstOrDefault();
if (firstInput != null)
firstInput.Focus();
}
}
And made all forms inherit from it instead of Form. The only problem is how do I call this method on all the forms after initializing the form. Is searched for some Form intialization event that I could subscribe to on the base class but found none.
How can I accomplish this in Windows Forms? Is there any way I can use my base class approach without needing to modify all 90 forms? Is there any better way? Or simply there isn't any way of doing it without modifying each form itself?
You can override OnShown(...) in your base form:
public class BaseForm : Form
{
public void override OnShown()
{
base.OnShown();
SetFoucsOnFirstInput();
}
public void SetFoucsOnFirstInput()
{
var firstInput = this.Controls.OfType<TextBox>().FirstOrDefault();
if (firstInput != null)
firstInput.Focus();
}
}
This methos will get called once every time a new form is shown.
But the more important thing is - that you dont have to call i yourself - the framework will do it for you.
You can use form Load event for setting focus.
help in msdn

How do I communicate with a control of a Form from another class?

A little new to C#, and approaching something beyond me. Apologies for length.
I have a Windows Form application in Visual Studio C# Express, using the default classes VS spawns. I want to start and stop a Marquee style progressBar from a class other than the default Form1 in which it is declared.
These seems surprisingly difficult, I am sure I am missing something important.
My project has the usual classes that Visual Studio auto generates:
Form1.cs, Form1.Designer.cs , Program.cs .
I added myClass.cs that wants to talk the load bar.
I add progressBar1 bar to my form using the designer, setting Style:Marquee.
In Form1.cs' Form() constructor, I write
this.progressBar1.Visible = false;
This works. Intellisense 'sees' progresBar1.
code in Form1.cs can see and control progressBar1 declared in Form1.Designer.cs.
this makes sense to me.
But the functions which need to start and stop the load bar must live in myClass.cs.
I want to be able to code like this, within myClass.cs:
public void myFunction(){
Form1.progressBar1.visible=true
//do stuff that takes a bit of time
Form1.progressBar1.visible=false
}
This does not work. Intellisense cannot 'see' progresBar1 when typing code in myClass.cs.
In fact, intellisense cannot 'see' anything in Form1.cs from within myClass.cs.
No public propeties or functions added to Form1 ever become visible to intellisense.
This does not make sense to me, I am confused.
This seems like something you would want to do often and easily.
Some searching indicates that this blocking of external access to Form controls is by design. Something to do with 'decoupling' your logic code from GUI code, which makes sense in principal.So clearly there is an expected approach, yet an clear example is hard to find. I can only find examples of loadbars controlled from entirely within the Forms that declare them, or terse half-examples about creating and registering Events or using Invoke or other things I know too little about. There are many apparent solutions but none that I can see clearly apply to me, or that I am able to implement, in my ignorance.
I think I could do it if my Form were an instance.
[EDIT] nope. instance or not, Form1 controls never become exposed outside of Form1.cs
So, How do I to start and stop a Marquee style progressBar from a class other than the default Form1 in which it is declared, in the proper way?
Is there a clear and useful example somewhere?
You can't access your properties this way:
Form1.progressBar1
because Form1 is a type (not an instantiated object). The only methods or properties you can access with this approach have to be marked as static.
To answer your question of how to communicate, you probably want to use the event approach that you mentioned. First you need an event in your logic class:
public event Action<int> UpdateProgress;
Which is called just like a function:
if (UpdateProgress != null)
UpdateProgress(10);
This declares a new event using the Action generic delegate, which means the listening function has to return void and take one int as a parameter.
Then in your forms code, you'll have:
MyClass logic = new MyClass();
private void SomeFunction
{
logic.UpdateProgress += UpdateProgressBar;
}
private void UpdateProgressBar(int newProgress)
{
progressBar1.BeginInvoke(new Action(() =>
{
progressBar1.Value = newProgress;
}));
}
This creates a new instance of your logic class, and assigns the function "UpdateProgressBar" to be called whenever your logic class raises the UpdateProgressBar event. The function itself uses Dispatcher.BeginInvoke because your logic class is likely not running on the UI thread, and you can only do UI tasks from that thread.
There is a lot going on here, so please let me know if I can clarify anything for you!
I would create a model that has properties matching your form, and pass that around.
So you would make a new class like this...
using Windows.Forms;
public class Form1Model {
public ProgressBar progressBar { get; set; }
}
Then when you want to get to your other class holding that function you would create an instance of Form1Model, fill it, and call your function
var fm = new Form1Model {
progressBar = this.progressBar1;
};
otherClass.MyFunction(fm);
now you would have to change your function to accept the new model
public void MyFunction(Form1Model fm){
// do stuff
}
Another option is just making the function take an instance of the form, and not creating a model, but then you are going to be passing a lot of extra bits you probably won't care about
public void MyFunction(Form1 form){
// do stuff
}
Then on your form you would call the function like this
otherClass.myFunction(this);
I would recommend the first way over the second, you can control what data is being passed around
You are trying to access the type Form1 instead of the forms instance. I'll show you, how you can access the instance below.
I assume that Form1 is the applications main form that stays open as long as the application runs. When you create a WinForms application VS creates this code in Program.cs:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
A simple way to make your main form accessible throughout the application is to make it accessible via a public static property. Change the code like this
static class Program
{
public static Form1 MainForm { get; private set; }
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MainForm = new Form1();
Application.Run(MainForm);
}
}
In Form1 create a property that exposes the progress bar's visibility:
public bool IsProgressBarVisible
{
get { return this.progressBar1.Visible; }
set { this.progressBar1.Visible = value; }
}
Now you can make the progress bar visible from any part of the program like this:
Program.MainForm.IsProgressBarVisible = true;
Another way of accessing the main form is, since it is always opened as the first form:
((Form1)Application.OpenForms(0)).IsProgressBarVisible = true;
However, it requires the form to be casted to the right type, since OpenForms returns a Form.
And don't forget: A Form is just a class like any other class. You can do almost everything you can make with other classes. So, communicating with forms is not very different than communication with other objects, as long as you are not using multithreading.

Lazy Instantiation of UserControl

I have a WPF application that I am trying to switch the contents of a window efficiently. I have come up with the solution of the following:
App.cs
internal static Lazy<HomeUserControl> HomePage;
MainWindow.cs
public MainWindow()
{
InitializeComponent();
Application.Current.MainWindow.Content = App.HomePage;
}
HomeUserControl.cs
public HomeUserControl()
{
InitializeComponent();
}
I am running into a problem that MainWindow.Content is basically being set to a blank window (it is actually changing the content of MainWindow). If I use App.MainWindow.Content = new HomePageUserControl(), everything works as it should. However, I would like to keep one instance of the page, which is why I made a static one in the App class. This problem occurs whether Lazy<> is used or not. I have tried a check to see if HomePage was null, and I got back a label that said Value is not created., which I'm pretty sure is the representation of an uninitialized Lazy<>; however, this only occurs if I check App.HomePage == null. Any ideas?
Try
Application.Current.MainWindow.Content = App.HomePage.Value;

Can I make a Form a singleton?

I have a Visual C# 2010 application, and it has one main form called MainWnd with other tool windows and dialogs. I want the other tool windows to be able to 'talk' to the main form, and call its methods. But that requires an instance of MainWnd, and since there will only be one of these forms created at any given time there is no reason while I should enumerate through all instances of MainWnd or look for the first one. So I want my main application form MainWnd to be a singleton so other windows can easily call code from it.
Here is the code of the main form that I would like to make a singleton:
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace MyLittleApp
{
public partial class MainWnd : Form
{
public MainWnd()
{
InitializeComponent();
}
public void SayHello()
{
MessageBox.Show("Hello World!");
// In reality, code that manipulates controls on the form
// would go here. So this method cannot simply be made static.
}
}
}
I am looking to be able to call SayHello() from another form, simply by writing:
MainWnd.SayHello();
How could I accomplish this?
You could probably find a way to make the main window a singleton, however that's not the best way to achieve the outcome you want, nor is it really an appropriate situation in which to use the singleton pattern.
If all of the other tool windows/ dialogs are encapsulated within the main window, then a much better pattern to use for communication would be events.
Have the inner windows/dialogs raise events to represent a 'request' for the main window to do something. Have the main window subscribe to these events, and do the work via the event handlers.
By avoiding the singleton approach, you avoid the difficulties of testing the singleton, as well as avoiding extensive explicit circular references, where not only does the main window have references to the encapsulated windows/dialogs, but they in turn have explicit references back to the main window.
See below.
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace MyLittleApp
{
public partial class MainWnd : Form
{
public static MainWnd Instance;
public MainWnd()
{
Instance = this;
InitializeComponent();
}
public void SayHello()
{
MessageBox.Show("Hello World!");
// In reality, code that manipulates controls on the form
// would go here. So this method cannot simply be made static.
}
}
}
You can now use it anywhere in your code by calling MainWnd.Instance
All its members are also available to the instance.
You can certainly do this.
public MainWnd Instance = new MainWnd();
Then access as MainWnd.Instance.SayHello().
Replace following calls
MainWind instance = new MainWnd();
To
MainWnd instance = MainWnd.Instance;
I am not sure how Visual Studio designer would react after making the constructor as private though.
But if it does not allow, it will be Visual Studio issue, rather than language/compiler issue.

Categories

Resources