Q: display a winforms screen)
It takes some time for my application to start. I want to display a pop-up screen (like in Visual Studio. NET and office applications) when the application continues to load ). The Toolbox does not contain such controls. How can I implement it?
A:
The Code included in this column contains a splashscreen class:
public class SplashScreen{ public SplashScreen(Bitmap splash); public void Close();} |
The splashscreen constructor can use the displayed bitmap as a parameter. The close method is used to close the screen. In general, we use splashscreen in the method of processing the form load event (as shown in Figure 1 ):
private void OnLoad(object sender,EventArgs e){ Bitmap splashImage; splashImage = new Bitmap("Splash.bmp"); SplashScreen splashScreen; splashScreen = new SplashScreen(splashImage); //Do some lengthy operations, then: splashScreen.Close(); Activate();} |
After you close the screen, you must activate the form and place it in the most prominent position.
You can use any bitmap as a splash screen. You can also create a bitmap from a BMP or JPG file by creating a new bitmap object:
Bitmap splashImage;splashImage = new Bitmap("Splash.bmp"); |
Alternatively, you can use an image loaded from the form Resource:
using System.Resources;ResourceManager resources;resources = new ResourceManager(typeof(MyForm));Bitmap splashImage;SplashImage = (Bitmap)(resources.GetObject( "SplashImage")) |
To implement a screen flash, not just what we see. It can depend on some good winforms functions, and it also involves some interesting design problems of applications in other winforms environments. A pop-up screen is actually a winforms called splashform. You can use the visual design window (visual designer) of winforms to make full use of the required changes and convert a default form into a pop-up screen. This proves that winforms are not only easy to use, there are also many features. In this example, we add a separate control-a simple image box called m_splashpicturebox.
During compilation, we do not know the size of the pop-up image because it is a runtime parameter, but the image box needs to be adjusted based on the image. You can easily achieve this by setting the sizemode attribute of m_splashpicturebox to autosize. Next, you must position the image box in the upper left corner of the form. You can set the dock attribute of m_splashpicturebox to fill. This will fix the image box in the upper left corner. At runtime, it will expand to the lower right corner to fill the form, because the size mode is set to autosize. Finally, set the cursor attribute of m_splashpicturebox to appstarting (the hourglass with an indicator), so that if the user moves the mouse over the screen, he or she will know that the application is starting.
Figure 2. "src =" http://www.fawcette.com/china/VS/2003_04/SplashScreen/Image/fig2sm.gif "align =" Left "> |
Figure 2. Set visible attributes for the pop-up forms and image boxes |
The pop-up form should not display any control box button (close, minimize, or maximize), nor has a title bar. In the visual design window, we can set the controlbox attribute of splashform to false. In this way, the control box is canceled ). You can clear the text attribute in the design window to delete the title bar.
Next let's look at the boundary of the pop-up screen. It should be a separate line -- not the default adjustable boundary style -- so we should set the formborderstyle attribute of the form to fixedsingle. Set the topmost attribute to true so that the screen is always at the top of Z-order (Windows display window order on the desktop. The screen should always be in the center of the screen. Fortunately, we can set the startposition attribute to centerscreen to achieve this. winforms automatically considers the window size and centers it. Figure 2 shows the properties windows of splashform and m_splashpicturebox, and summarizes the attributes and new values you need to set.
Next, we need to write some code to adjust the size of the pop-up screen. The splashform constructor can take a flashing image as a parameter and assign it to the image in the image box:
internal class SplashForm : Form{ PictureBox m_SplashPictureBox; public SplashForm(Bitmap splashImage) { InitializeComponent(); m_SplashPictureBox.Image = splashImage; ClientSize = m_SplashPictureBox.Size; } //Rest of the implementation } |
Note: You must set the size of the splashform client to the size of the image box. It will automatically adjust its size according to the image size. The result of splashform can be precisely displayed in the image box, because the image box is placed in the upper left corner of the form.
You cannot display the splashform on the same thread used to load the application, because that thread is busy loading the application and will not consider displaying or re-painting the flash screen. As an alternative, we should have the splashscreen create a worker thread to display the splashform (see list 1 ). The worker thread calls the show method. This method creates a splashform object and calls its showdialog method:
void Show(){ m_SplashForm = new SplashForm(m_SplashImage); m_SplashForm.ShowDialog();} |
Showdialog displays the form and begins to fill in Windows messages. The pop-up screen runs on its own thread, so the thread can process messages-not the main application thread busy loading the application.
The next task is to find a method for the main application to close the screen. The easiest way is to use a signal to notify the worker thread to close the form. Unless the show method of the thread is busy filling the message in the form message loop (showdialog method, you cannot view tags or events. The solution is simple, that is, Windows timers. Use the design window to add a timer control to the form and set its interval attribute to an appropriate value, for example, 500 ms. The timer class is actually based on vm_timer messages, so the tick event of timer is Windows message-driven. The worker thread provides the message to the pop-up screen, where it will check whether the pop-up screen needs to be closed because the main application has completed loading. The splashform class provides the Boolean attribute hidesplash. The close method of the splashscreen sets it:
public void Close(){ m_SplashForm.HideSplash = true; m_WorkerThread.Join();} |
Hidesplash can access the m_hidesplash Boolean member variable of splashform. M_hidesplash can be accessed by multiple threads. Therefore, hidesplash needs to access m_hidesplash in a thread-safe way by locking the splashform:
public bool HideSplash{ get { lock(this){ return m_HideSplash; } } set { lock(this){ m_HideSplash = value; } }} |
Splashform processes the tick event of Timer In the ontick method:
private void OnTick(object sender,EventArgs e){ if(HideSplash == true) { m_Timer.Enabled = false; Close(); }} |
If the hidesplash attribute is set to true (because the close method of splashscreen is called), ontick will invalidate the timer and disable the splashform. Its operation process is as follows: the main form starts to load and a pop-up screen is displayed on another thread. Then, the main form continues to start the application. Periodically check whether timer should be disabled. When the main form is loaded, the close method of splashscreen is called. The close method sets hidesplash to true and calls join on the worker thread to close the screen. This will impede the display of the main form, so as long as the screen is displayed, the main form will not be displayed. When the next timer rings, it will view the value of hidesplash. It will cancel the timer and disable the splashform, because hidesplash is set to true. This will return the showdialog method (this method is called in the show method of splashscreen), and then return show. Once show is returned, the thread is terminated because show is the thread method of the worker thread. At this time, the join operation in the close method of splashscreen is returned. The close method is returned to the main form. Now the main form can be displayed.
Q: allowed serializable types include nonserializable members.
I have a serializable class that contains a database connection as a member variable. When I tried to serialize this class, an exception occurred because the connection is not serializable. If I mark the connection as non-serializable, I can serialize the class-but after deserialization (deserialization), I cannot use this object, the connection Member is invalid. What should I do?
A:
When you use the serializable attribute to identify a class for serialization ,. net considers all member variables to be serializable. If it finds a member that is not serializable, it will throw a serializationexception type exception during serialization. However, a class may contain a member that cannot be serialized. This type does not have the serializable attribute and Cannot serialize the contained types. In general, this non-serializable member is a reference type and requires some special initialization settings. To solve this problem, we need to mark such a member as non-serializable and initialize it using a custom step in deserialization.
You must use the nonserialized field attribute to identify the member so that a serializable type contains an unserializable type as a member variable:
public class MyOtherClass{..}[Serializable]public class MyClass{ [NonSerialized] MyOtherClass m_Obj; /* Methods and properties */} |
When. Net serializes a member variable, it first checks whether it has the nonserialized attribute. If so,. Net ignores the variable and skips it. However, when. Net deserializes an object, it initializes the non-serializable member variable of that type and sets it to the default value (for all reference types, the default value is zero ). Then, you can provide the code to initialize the variable to the correct value. Finally, the object must know when it is deserialized. You must implement the ideserializationcallback interface, which is defined in the system. runtime. serialization namespace:
public interface IDeserializationCallback{ void OnDeserialization(object sender);} |
After. Net completes deserialization of the object, it will call the ondeserialization () method of ideserializationcallback to execute the required custom initialization steps. You can ignore the sent parameter because. Net always sets it to zero. The following code illustrates how to implement custom serialization by implementing ideserializationcallback:
using System.Runtime.Serialization;[Serializable]public class MyClass : IDeserializationCallback{ [NonSerialized] IDbConnection m_Connection; public void OnDeserialization(object sender) { Debug.Assert(m_Connection == null); m_Connection = new SqlConnection(); m_Connection.ConnectionString = "data source= ... "; m_Connection.Open(); } /* Other members */} |
In the above Code, the myclass class has a database connection as a member variable. The connection object (sqlconnection) is not a serializable type, so you need to use the nonserialized attribute to identify it. Myclass creates a new connection object in its ondeseralization () implementation because the connection Member is set to the default value (zero) after deserialization ). Then, by providing a connection string, myclass initializes a connection object and opens it.
About Author:
Juval Lowy is an experienced software architect and is in charge of idesign. This is a consulting and training company specialized in. Net Design and. Net transplantation. As Microsoft's Regional Supervisor in Silicon Valley, Juval is responsible for helping apply. Net to enterprises. Recently, he wrote a book namedProgramming. NET components(O 'Reilly & Associates) book. You can contact www.idesign.net.