Lazy Instantiation of UserControl - c#

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;

Related

Application.Current.<PageName> returns null

I am developing an app in Xamarin.Forms using MVVM model and I use this method DisplayAlert in PageService class to Display Alerts throughout ViewModels:
public async Task DisplayAlert(string title, string message, string ok = "ok")
{
await Application.Current.MainPage.DisplayAlert(title, message, ok);
}
Everything worked just fine since I reinstalled the application on a testing device and made some minor changes that shouldn't affect MainPage. Now, whenever I call this DisplayAlert method, an exception is thrown:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
Xamarin.Forms.Application.MainPage.get returned null
I really don't know where does the error come from. I googled around and the only thing I found was that there might be some problem in MainPage constructor, but in my case I don't see there any.
So now I am stuck and don't know how to move forward with this problem. Could you please at least point me in some direction on how to find out why MainPage returns null?
Big thanks to anyone responding, I hugely appreciate your suggestions.
Regards,
Honza
Solution:
Cheers, thank you, everyone! I finally managed to solve this issue:
So I have this constructor of App which I edited as Lucas Zhang suggested and also added two breakpoints there:
public static Page RootPage { get; set; }
public App()
{
InitializeComponent();
MainPage = new MainPage(); //Breakpoint 1
App.RootPage = MainPage; //Breakpoint 2
}
I found out, that this code is being executed as following: Debbuger stops at breakpoint 1, then exception is thrown just before the debugger reaches breakpoint 2.
Now, MainPage is actually Tabbed Page that consists of 4 other tabs, and all of these tabs are initialized when "MainPage = new MainPage();" is called. In one of these tabs I use a service. What this service does is basically: Initializes itself and determines whether a user has done something and if not, it displays an Alert that prompts him to do so. And all of that happens just before MainPage finishes its initialization, so of course when calling App.Current.MainPage null is returned.
So, that's it, thank you all once again!
Make sure that you had set the MainPage in constructor of App.
public App()
{
InitializeComponent();
MainPage = new xxxPage();
}
If it still doesn't work , you could firstly use the following workaround .
public static Page RootPage;
public App()
{
InitializeComponent();
MainPage = new MainPage();
App.RootPage = this.MainPage;
}
And reference it
App.RootPage.DisplayAlert(title, message, ok);

Calling parent function from frame

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.

Why isnt the text of my label getting updated in a static method? C#

I am currently working with a static method on my page (It is static because it works with other pages). At the end of this method I foreach the results of a list and give the x:name of my label (that i have created in my XAML page) a new text.
To test to see if it worked I write out the labels text in the log and the correct text indeed gets written out, however the text does not get updated on the app.
The code looks something like this:
public static MyPage currentpage = new MyPage();
This is the current page that i am working on. In order to reach the labels x:name I created this code.
And then this is the static method on this page also.
public static async Task loadTheData(string token) //method is static because i send a token from another page
{
...
foreach (var profileinfo in App.registeredUsers) //this is my list
{
currentpage.myXAMLlabel.Text = profileinfo.name; //this is the label where i assign the new text
}
System.Diagnostics.Debug.WriteLine(currentpage.myXAMLlabel.Text); //the correct text gets written out in the log but the text does not get updated "visually" on the app
}
So as I mentioned above, I get the correct text in the log but the text of the label does not get updated "visually" on the app screen.
I call the static method firstly from the specific iOS/Android folders:
App.SuccessfulLoginAction.Invoke();
And on my app-page I have the following method:
public static Action SuccessfulLoginAction
{
get
{
return new Action(async () =>
{
await MyPage.loadTheData(token);
});
}
}
I could move the SuccessfulLoginAction to MyPage instead of having it on the App page. But the method has to still be static i suppose (?) in order for the iOS code to reach the Action.
How can I adjust my code to solve this problem?
If I read your question correctly, you are adding the public static MyPage currentpage = new MyPage(); in the MyPage class?
If that's the case, your instance of MyPage in the currentPage variable, won't be the same instance that you see on the screen. The Debug message will show the instance that is not on screen. You could implement what you want with the singleton pattern.
Get rid of the statics by using the MessagingCenter
Or better yet, to get rid of the statics, make use of the MessagingCenter pub/sub mechanism or any other MVVM equivalent. Example for the MessagingCenter:
You can use the App class as the sender from your platform specific projects. Like this:
MessagingCenter.Send<App>((App)Xamarin.Forms.Application.Cur‌​rent, "myEvent")
Subscribe in your App class:
MessagingCenter.Subscribe<App>(this, "myEvent", ...)
If you want to subscribe in your page:
MessagingCenter.Subscribe<App>((App)Application.Current, "myevent", ...)
See the docs for more information.
Updating UI elements on the right thread
You should also make sure to update UI elements on the Main Thread (which you probably won't be on because you are using async/await). As #SushiHangover mentioned in his answer by using Device.BeginInvokeOnMainThread:
Device.BeginInvokeOnMainThread(() =>
{
currentpage.myXAMLlabel.Text = profileinfo.name;
});
Make sure you are updating any UI elements on the UI/main thread, i.e.:
Device.BeginInvokeOnMainThread(() =>
{
currentpage.myXAMLlabel.Text = profileinfo.name; //this is the label where i assign the new text
});

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.

Custom User Control Not Initialized in Auto-Generated Code

This has happened many times before, but I never bothered to figure out why, and now I am tired of it:
For instance, I derive a class from RichTextBox or Panel, I rebuild my project to have the class added to the VS designer toolbox, and then I drag & drop the custom user control to a Form. Everything works fine, and I can run my project...
The problem comes when I edit properties of the Form or the custom user control through the designer. Sometimes, the designer removes the initialization line from its code-behind, causing an exception in the designer and the executable because the control remains uninitialized.
In other words, the following line is removed from say, Form1.Designer.cs:
this.customRichTextBox1=new CustomRichTextBox();
No other line is removed from the code-behind, so the attributes of the custom control are still set, although the variable stays uninitialized.
My solution has always been to manually initialize my user control in the designer code-behind, but the designer eventually removes it again.
I believe that this does not happen when I build a Custom UserControl through the designer (but I am not completely sure of this). It only happens when I define something like the following manually:
class CustomRichTextBox:RichTextBox{}
This is so annoying. What am I doing wrong?
As #Cody requested, here are the steps to reproduce the problem. I am using VS2010, but I've had this problem since 2005, I think.
Step 1. Create new Windows Forms Application, any Framework
Step 2. Add the following class below your main Form class: (It just happens that this is the control that is causing me this problem this time.)
class CustomRichTextBox : RichTextBox
{
Timer tt = new Timer();
internal CustomRichTextBox()
{
tt.Tick += new EventHandler(tt_Tick);
tt.Interval = 200;
}
protected override void OnTextChanged(EventArgs e)
{
tt.Stop();
tt.Start();
}
void tt_Tick(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("Hello world!");
}
}
Step 3. Press F6 to rebuild.
Step 4. Add the CustomRichTextBox control to your Form by dragging and dropping from the Toolbox.
Step 5. If you wish, you may press F5 to test the application, but it should work. Close the running application.
Step 6. Press F6 to rebuild, and at this point, the designer should crash with the following message: "The variable 'customRichTextBox1' is either undeclared or was never assigned." (In one case, the whole VS completely crashed, but the error is usually contained within the designer.)
Step 7. To correct the issue, go into the code-behind and initialize the variable, but next time you rebuild, the initialization line will be gone.
Thanks to everyone who tried answering my question and who posted comments that helped me diagnose and solve the problem.
The problem occurs when using an "internal" keyword with the control's constructor. Changing it to "public" fixes the problem. The reason for this behavior might be that the Designer's own classes cannot see the constructor because they are not within the namespace of my class unless it is marked public. This all makes sense, and I will use the public keyword from now on.
The class does not need to be in its own individual file or be the first declared class in the file as other answers suggested.
The following class works well because the constructor's keyword was changed to public.
class CustomRichTextBox : RichTextBox
{
Timer tt = new Timer();
public CustomRichTextBox()
{
tt.Tick += new EventHandler(tt_Tick);
tt.Interval = 200;
}
protected override void OnTextChanged(EventArgs e)
{
tt.Stop();
tt.Start();
}
void tt_Tick(object sender, EventArgs e)
{
System.Diagnostics.Trace.WriteLine("Hello world!");
}
}
Is your build set to Debug or it is Release?
I suppose that it is release as I think compiler optimizes the code and remove designer generated line.
Have you tried putting the control code in its own file? I've had problems even with the form designer in the past when the designer code was not int he first class in the file.
I had a similar problem that this posted helped me solve. I have a CustomControl that extends ComboBox, that class contained an internal private class YearItem. I've tried to highlight only the code needed to understand the problem and the solution.
public class YearsCbo : ComboBox //Inherits ComboBox
{
public YearsCbo() {
fill();
}
private void fill() { // <<<=== THIS METHOD ADDED ITEMS TO THE COMBOBOX
for(int idx = 0; idx < 25; idx++) {
this.Items.Add(new YearItem());
}
}
// Other code not shown
private class YearItem {} // <<<=== The VS designer can't access this class and yet
// it generated code to try to do so. That code then fails to compile.
// The compiler error rightfully says it is unable to access
// the private class YearItem
}
I could drag/drop that control YearsCbo onto a form and it worked correctly, but after I returned and edited the form the VS designer generated code that would not compile. The offending code something like this:
Dim YearItem1 As my.ns.YearsCbo.YearItem = New my.ns.YearsCbo.YearItem()
Dim YearItem2 As my.ns.YearsCbo.YearItem = New my.ns.YearsCbo.YearItem()
// This was repeated 25 times because in my constructor I created 25 of these
Me.YearsCbo1.Items.AddRange(New Object() {YearItem1, 2, 3, ..., YearItem25 });
Notice that the designer generated code which tried to access the private class. It didn't need to do that but for some reason it did.
Through trial and error, and this post: How to tell if .NET code is being run by Visual Studio designer came up with a solution:
I added a property to tell if I am running in the designer.
public bool HostedDesignMode
{
get
{
if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
return true;
else
return false;
}
}
I also changed the constructor so that it doesn't call fill() so when the designer runs, there are no items in the ComboBox so the designer doesn't feel the need to manually create those items.
The "fixed" code is shown below:
public class YearsCbo : ComboBox //Inherits ComboBox
{
public YearsCbo() {
if ( ! HostedDesignMode ) {
fill();
}
}
private class YearItem {} // <<<=== Now the VS Designer does not try to access this
}
This code was written using VS2012 Premium on Win7x64 OS (in case it matters).

Categories

Resources