分享
 
 
 

Mastering Page-UserControl Communication

王朝asp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

source:

http://openmymind.net/communication

begin:

Karl Seguin ?karlseguin@hotmail.com

Table of Contents

Introduction

Understanding the Basics

A page is a class

Inheritance

Basic Communication

From Page to User Control

From User Control to Page

From User Control to User Control

Accessing Methods

Advanced Communication

Basic Communication is Bad Design?

Making Use of Interfaces

Event Driven Communication

Timing

Conclusion

This article is available at Code Project. Check it out to make comments, discuss or rate the article

I'd like to thank Jean-Claude Manoli for developing his C# Code format, which i used in writing this tutorial.

IntroductionIn the previous Mastering article (Mastering ASP.Net DataBinding) we took a detailed look at databinding - one of the most asked about topics in the newsgroup. Today we continue the series by answering another very common question : how to maximize the communication between a page and its user controls. The actual questions asked typically don't include the words "maximize the communication" in them, but are more along the lines of:

How to access values from a page in a user control?

How to access values from one user control in another user control?

How to access values from a user control in a page?

And other similar questions The goal of this tutorial isn't only to answer these questions, but more importantly to build a foundation of understanding around these answers to truly make you a master of page-user control communication.

Understanding the BasicsBefore we can answer the above questions two basic concepts should be understood. As always, these basic concepts not only go beyond the scope of this tutorial, but by really understanding them you'll be on your way to mastering ASP.Net. Both these concepts, and therefore the answers to the above questions, deal with object oriented principals. The use of solid object oriented methodologies is a reoccurring theme in developing solutions in ASP.Net, but we must be conscious that this can unfortunately be intimidating for some programmers. If you've read this far however, you're willing to do more than simply copy and paste an answer.

A page is a classThis is probably something you already know, but each codebehind file you create is actually compiled into a class. There's a good chance however that you haven't really been taking advantage of that knowledge. Before we get ahead of ourselves, let's look at the shell of a codebehind file:

1: //C#

2: public class SamplePage : System.Web.UI.Page {

3: private string title;

4: public string Title{

5: get { return title; }

6: }

7: ...

8: }

1: 'VB.Net

2: Public Class SamplePage

3: Inherits System.Web.UI.Page

4: Private _title As String

5: Public ReadOnly Property Title() As String

6: Get

7: Return _title

8: End Get

9: End Property

10: ...

11: End Class

As you can see, it's a class like any other - except that an ASP.Net page always inherits from System.Web.UI.Page. In reality though, there's nothing special about this class, it's just like any other. It's true that ASP.Net pages behave slightly differently from normal classes, for example Visual Studio.Net automatically generates some code for you called Web Form Designer generated code, and you typically use the OnInit or Page_Load events to place your initializing code - instead of a constructor. But these are difference for the ASP.Net framework, from your own point of view you should treat pages like any other classes.

So what does that really mean? Well, as we'll see when we start to look at specific answers, the System.Web.UI.Control class, which System.Web.UI.Page and System.Web.UI.UserControl both inherit from, exposes a Page property. This Page property is a reference to the instance of the current page the user is accessing. The reference is pretty useless to the actual page (since its a reference to itself), but for a user control it can be quite useful when properly used.

InheritanceI originally wrote quite a bit about what inheritance was. However, from the start it felt like the thousands of tutorials which try to explain core OO principals with a couple basic examples and simplified explanations. While inheritance isn't a complicated topic, there's something about trying to teach it so it doesn't seem cheap which my writing skills just haven't reached yet. Ask google about C# inheritance if you're really new to the topic.

Instead of talking in depth about inheritance, we'll briefly touch on what we need to know. We can clearly see in the above class shell that our SamplePage class inherits from System.Web.UI.Page (we can especially see this in the more verbose VB.Net example). This essentially means that our SamplePage class provides (at the very least) all the functionality provided by the System.Web.UI.Page class. This guarantee that an instance of SamplePage can always safely be treated as an instance of System.Web.UI.Page (or any classes it might inherit from). Of course the opposite isn't always true, an instance of System.Web.UI.Page isn't necessarily an instance of SamplePage.

The truly important thing to understand is that our SamplePage extends the functionality of the System.Web.UI.Page by providing a readonly property named Title. The Title property however is only accessible from an instance of SamplePage and not System.Web.UI.Page. Since this is really the key concept let's look at some examples:

1: //C#

2: public static void SampleFunction(System.Web.UI.Page page, SamplePage samplePage) {

3: //IsPostBack property is a member of the Page class, which all instances

4: //of SamplePage inherit

5: bool pb1 = page.IsPostBack; //valid

6: bool pb2 = samplePage.IsPostBack; //valid

7:

8: //The ToString() method is a member of the Object class, which instances

9: //of both the Page and SamplePage classes inherit

10: string name1 = page.ToString(); //valid

11: string name2 = samplePage.ToString(); //valid

12:

13: //Title is specific to the SamplePage class, only it or classes

14: //which inherit from SamplePage have the Title property

15: string title1 = page.Title; //invalid, won't compile

16: string title2 = samplePage.Title; //valid

17: string title3 = ((SamplePage)page).Title; //valid, but might give a run-time error

18: string title4 = null;

19: if (page is SamplePage){

20: title4 = ((SamplePage)page).Title;

21: }else{

22: title4 = "unknown";

23: }

24: }

1: 'VB.Net

2: Public Shared Sub SampleFunction(ByVal page As System.Web.UI.Page, ByVal samplePage As SamplePage)

3: 'IsPostBack property is a member of the Page class, which all instances

4: 'of SamplePage inherit

5: Dim pb1 As Boolean = page.IsPostBack 'valid

6: Dim pb2 As Boolean = samplePage.IsPostBack 'valid

7:

8: 'The ToString() method is a member of the Object class, which instances

9: 'of both the Page and SamplePage classes inherit

10: Dim name1 As String = page.ToString() 'valid

11: Dim name2 As String = samplePage.ToString() 'valid

12:

13: 'Title is specific to the SamplePage class, only it or classes

14: 'which inherit from SamplePage have the Title property

15: Dim title1 As String = page.Title 'invalid, won't compile

16: Dim title2 As String = samplePage.Title 'valid

17: Dim title3 As String = CType(page, SamplePage).Title 'valid, but might give a run-time error

18: Dim title4 As String = Nothing

19: If TypeOf page Is SamplePage Then

20: title4 = CType(page, SamplePage).Title

21: Else

22: title4 = "unknown"

23: End If

24: End Sub

The first couple cases are straightforward, first we see how our SamplePage class inherits the IsPostBack property from System.Web.UI.Page [5,6]. We then see how both SamplePage and System.Web.UI.Page inherit the ToString() function from System.Object - which all objects in .Net inherit from. Things get more interesting when we play with the Title property. First, since the System.Web.UI.Page class doesn't have a Title property, the first example is totally invalid and thankfully won't even compile [15]. Of course, since our SamplePage class does define it, the second example is perfectly sane [16]. The third and forth examples are really interesting. In order to get our code to compile, we can simply cast the page instance to the type of SamplePage which then allows us to access the Title property [17]. Of course, if page isn't actually an instance of SamplePage this will generate an exception. The forth example illustrate a much safer way to do this: by checking to see if page is an instance of SamplePage [19] and only if it is casting it [20].

To wrap up this [painful] section, the key point to understand is that when you create a new ASPX page, the page itself is a class, which inherits from System.Web.UI.Page. If you have access to an instance of System.Web.UI.Page and you know the actual type (for example SamplePage), you can cast it to this type and then access its functionality - much like we were able to do with page and get the Title.

Basic CommunicationWe'll first discuss basic communication strategies between a page and its user controls in all directions. While this section alone will likely answer your questions, the important stuff comes in the following section where we discuss more advanced strategies. For the basic communication we'll use a single page with two user controls and keep everything fairly simple. We'll use our sample page from above, and these two user controls:

1: 'VB.Net - Results user control

2: Public Class Results

3: Inherits System.Web.UI.UserControl

4: Protected results As Repeater

5: Private info As DataTable

6:

7: Public Property Info() As DataTable

8: Get

9: Return info

10: End Get

11: Set

12: info = value

13: End Set

14: End Property

15:

16: Private Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

17: If Not Page.IsPostBack AndAlso Not (info Is Nothing) Then

18: results.DataSource = info

19: results.DataBind()

20: End If

21: End Sub

22: End Class

1: 'VB.Net - ResultsHeader user control

2: Public Class ResultHeader

3: Inherits System.Web.UI.UserControl

4: Private Const headerTemplate As String = "Page {1} of {2}"

5: Protected header As Literal

6: Private currentPage As Integer

7: Private recordsPerPage As Integer

8:

9: Public Property CurrentPage() As Integer

10: Get

11: Return currentPage

12: End Get

13: Set

14: currentPage = value

15: End Set

16: End Property

17:

18: Public Property RecordsPerPage() As Integer

19: Get

20: Return recordsPerPage

21: End Get

22: Set

23: recordsPerPage = value

24: End Set

25: End Property

26:

27: Private Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs)

28: header.Text = headerTemplate

29: header.Text = header.Text.Replace("{1}", currentPage.ToString())

30: header.Text = header.Text.Replace("{2}", recordsPerPage.ToString())

31: End Sub

32: End Class

From Page to User ControlWhile communicating from a page to a user control isn't something frequently asked (because most people know how to do it) it nevertheless seems like the right place to start. When placing a user control on a page (ie, via the @Control directive) passing values is pretty straightforward for simple types:

1: <%@ Register TagPrefix="Result" TagName="Header" Src="ResultHeader.ascx" %>

2: <%@ Register TagPrefix="Result" TagName="Results" Src="Results.ascx" %>

3: <HTML>

4: <body>

5: <form id="Form1" method="post" runat="server">

6: <Result:Header id="rh" CurrentPage="1" RecordsPerPage="2" runat="server" />

7: <Result:Results id="rr" runat="server" />

8: </form>

9: </body>

10: </HTML>

We can see that the CurrentPage and RecordsPerPage properties of our ResultHeader user control are assigned a value like any other HTML property [5]. However, since the the Results user control's Info property is a more complex type and must thus be set via code:

1: protected Results rr;

2: private void Page_Load(object sender, EventArgs e) {

3: if (!Page.IsPostBack){

4: rr.Info = SomeBusinessLayer.GetAllResults();

5: }

6: }

When loading a control dynamically, via Page.LoadControl, it's important to realize that an instance of System.Web.UI.Control is returned - not the actual class of the control loaded. Since we know the exact type, we simply need to cast it first:

1: //C#

2: Control c = Page.LoadControl("Results.ascx");

3: c.Info = SomeBusinessLayer.GetAllResults(); //not valid, Info isn't a member of Control

4:

5: Results r = (Results)Page.LoadControl("Results.ascx");

6: r.Info = SomeBusinessLayer.GetAllResults(); //valid

1: 'VB.Net

2: dim c as Control = Page.LoadControl("Results.ascx")

3: c.Info = SomeBusinessLayer.GetAllResults() 'not valid, Info isn't a member of Control

4:

5: dim r as Results = ctype(Page.LoadControl("Results.ascx"), Results)

6: r.Info = SomeBusinessLayer.GetAllResults() 'valid

From User Control to PageCommunicating information from a user control to its containing page is not something you'll need to do often. There are timing issues associated with doing this, which tends to make an event-driven model more useful (I'll cover timing issues and using events to communicate later in this tutorial). Since this provides a nice segue into the far more frequently asked user control to user control question we'll throw timing issues to the wind and quickly examine it.

As I've already mentioned, pages and user controls eventually inherit from the System.Web.UI.Control class which exposes the Page property - a reference to the page being run. The Page property can be used by user controls to achieve most of the questions asked in this tutorial. For example, if our ResultHeader user control wanted to access the our SamplePage's Title property, we simply need to:

1: //C#

2: string pageTitle = null;

3: if (Page is SamplePage){

4: pageTitle = ((SamplePage)Page).Title;

5: }else{

6: pageTitle = "unknown";

7: }

1: 'VB.Net

2: Dim pageTitle As String = Nothing

3: If TypeOf (Page) Is SamplePage Then

4: pageTitle = CType(Page, SamplePage).Title

5: Else

6: pageTitle = "unknown"

7: End If

It's important to check that Page is actually of type SamplePage before trying to cast it [3] otherwise we'd risk of having a System.InvalidCastException thrown.

From User Control to User ControlUser control to user control communication is an extension of what we've seen so far. Too often have I seen people try to find ways to directly link the two user controls, as opposed to relying on common ground - the page. Here's the codebehind for SamplePage containing a Results and ResultHeader user control:

1: Public Class SamplePage

2: Inherits System.Web.UI.Page

3: Private rr As Results

4: Private rh As ResultHeader

5: Private _title As String

6: Public ReadOnly Property Title() As String

7: Get

8: Return _title

9: End Get

10: End Property

11: Public ReadOnly Property Results() As Results

12: Get

13: Return rr

14: End Get

15: End Property

16: Public ReadOnly Property Header() As ResultHeader

17: Get

18: Return rh

19: End Get

20: End Property

21: ...

22: End Class

The codebehind looks like any other page, except a readonly property for our two user controls has been added [11-15,16-20]. This allows a user control to access any other via the appropriate property. For example, if our ResultHeader wanted to make use of the Result's info property, it could easily access it via:

1: //C#

2: private void Page_Load(object sender, EventArgs e) {

3: DataTable info;

4: if (Page is SamplePage){

5: info = ((SamplePage)Page).Results.Info;

6: }

7: }

1: 'VB.Net

2: Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

3: Dim info As DataTable

4: If TypeOf (Page) Is SamplePage Then

5: info = CType(Page, SamplePage).Results.Info

6: End If

7: End Sub

This is identical to the code above example - where a user control accessed a page value. In reality this is exactly what's happening, the ResultHeader is accessing the Results property of SamplePage and then going a level deeper and accessing it's Info property.

There's no magic. We are using public properties in classes to achieve our goals. A page sets a user control's value via a property, or vice versa which can be done to any depth. Simply be aware that pages and user controls are actual classes you can program against, create the right public interface (properties and methods) and basic communication becomes rather bland (this isn't always a bad thing).

Accessing MethodsMethods are accessed the same way we've done properties. As long as they are marked public, a page can easily access one of it's user control's methods, or a user control can use the page as a broker to access another user control's method.

Advanced CommunicationWhile the above section aimed at giving you the knowledge to implement a solution to [most of] the questions related to this tutorial, here we'll concentrate on more advanced topics with a strong focus on good design strategies.

Basic Communication is Bad Design?While the code and methods discussed in the above sections will work, and are even at times the right approach, consider if they are truly the right approach for your situation. Why? you ask. Because if they aren't bad design as-is, they will lead to it unless you are vigilant. Take for example the last little blurb about accessing methods. If these are utility/common/static/shared methods, consider moving the function to your business layer instead.

Another example of bad design is the dependency such communication creates between specific pages and user controls. All of our example user controls above would either work very differently or cease to work entirely if they were used on a page other than SamplePage. User controls are meant to be reused, and for the most part (this isn't a 100% rule) shouldn't require other user controls or a specific page to work. The next two sections look at ways of improving this.

Making Use of InterfacesWe can leverage interfaces to reduce the dependency created by such communication. In the last example, the ResultHeader user control accessed the Info property of the Results user control. This is actually a pretty valid thing to do as it avoids having to re-hit the database in order to access the total number of records (although there are certainly alternatives to this approach). The problem with the above approach is that ResultHeader would only work with SamplePage and Results. Making good use of interfaces can actually make ResultHeader work for any page which displays a result (whatever that might be).

What is an interface? An interface is a contract which a class must fulfill. When you create a class and say that it implements a certain interface, you must (otherwise your code won't compile) create all the functions/properties/event/indexers defined in the interface. Much like you are guaranteed that a class which inherits from another will have all of the parent's class functionality, so too are you guaranteed that a class which implements an interface will have all of the interfaces members defined. You can read Microsoft's definition, or this tutorial, but I think the couple example bellow will give you the exposure you need.

To get the most flexibility, we'll create two interfaces. The first will be used by pages which display results and will force them to expose a readonly property which in turn exposes our other interface:

1: //C#

2: public interface IResultContainer{

3: IResult Result { get; }

4: }

1: 'VB.Net

2: Public Interface IResultContainer

3: ReadOnly Property Result() As IResult

4: End Interface

The second interface, IResult exposes a DataTable - the actually results:

1: //C#

2: public interface IResult {

3: DataTable Info { get; }

4: }

1: 'VB.Net

2: Public Interface IResult

3: ReadOnly Property Info() As DataTable

4: End Interface

If you are new to interfaces, notice how no implementation (no code) is actually provided. That's because classes which implement these interfaces must provide the code (as we'll soon see).

Next we make SamplePage implement IResultContainer and implement the necessary code:

1: Public Class SamplePage

2: Inherits System.Web.UI.Page

3: Implements IResultContainer

4:

5: Private rr As Results

6: Public ReadOnly Property Result() As IResult Implements IResultContainer.Result

7: Get

8: Return rr

9: End Get

10: End Property

11: ...

12: End Class

The last step before we can make use of this is to make Results implement IResult:

1: public class Results : UserControl, IResult {

2: private DataTable info;

3: public DataTable Info { //Implements IResult.Info

4: get { return info; }

5: }

6: ...

7: }

With these changes in place, ResultHeader can now decouple itself from SamplePage and instead tie itself to the broader IResultContainer interface:

1: Dim info As DataTable

2: If TypeOf (Page) Is IResultContainer Then

3: info = CType(Page, IResultContainer).Result.Info

4: Else

5: Throw New Exception("ResultHeader user control must be used on a page which implements IResultContainer")

6: End If

There's no denying that the code looks a lot as it did before. But instead of having to be placed on SamplePage, it can now be used with any page which implements IResultContainer. The use of IResult also decouples the page from the actual Results user control and instead allows it to make use of any user control which implements IResult.

All of this might seem like a lot of work in the name of good design. And if you have a simple site which will only display a single result, it might be overkill. But the minute you start to add different results interfaces will pay off both in lower development time and, more importantly, by making your code easily readable and maintainable. And if you don't use interfaces to decouple your communication links, keep an open mind for where else you might be able to use them because you'll probably find a tone.

Event Driven CommunicationOne of the questions I haven't answered yet is how to make a page (or another user control) aware of an event which occurred in a user control. While its possible to use the communication methods described above, creating your own events totally decouples the user control from the page. In other words, the user control raises the event and doesn't care who (if anyone) is listening. Besides, its fun to do!

For our example we'll create a third user control ResultPager which displays paging information for our results. Whenever one of the page numbers is clicked, our user control simply raises an event which the page, or other user controls can catch and do what they will with it:

1: //C#

2: public class ResultPaging : UserControl {

3: private Repeater pager;

4: public event CommandEventHandler PageClick;

5:

6: private void Page_Load(object sender, EventArgs e) {

7: //use the other communication methods to figure out how many pages

8: //there are and bind the result to our pager repeater

9: }

10:

11: private void pager_ItemCommand(object source, RepeaterCommandEventArgs e) {

12: if (PageClick != null){

13: string pageNumber = (string)e.CommandArgument;

14: CommandEventArgs args = new CommandEventArgs("PageClicked", pageNumber);

15: PageClick(this, args);

16: }

17: }

18: }

1: 'VB.Net

2: Public Class ResultPaging

3: Inherits System.Web.UI.UserControl

4: Private pager As Repeater

5: Public Event PageClick As CommandEventHandler

6: Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

7: 'use the other communication methods to figure out how many pages

8: 'there are and bind the result to our pager repeater

9: End Sub

10:

11: Private Sub pager_ItemCommand(ByVal source As Object, ByVal e As RepeaterCommandEventArgs)

12:

13: Dim pageNumber As String = CStr(e.CommandArgument)

14: Dim args As New CommandEventArgs("PageClicked", pageNumber)

15: RaiseEvent PageClick(Me, args)

16:

17: End Sub

18: End Class

With our PageClick event declared of type CommandEventHandler [5] we are able to notify anyone who's interested when a page number is clicked. The general idea behind the control is to load a repeater with the page numbers, and to raise our PageClick event when an event fires within this repeater. As such the user control handle the repeater's ItemCommand [11], retrieve the CommandArgument [13], repackages it into a CommandEventArgs [14] and finally raises the PageClick event [15]. The C# code must do a little extra work by making sure that PageClick isn't null [12] before trying to raise it, whereas VB.Net's RaiseEvent takes care of this (the event will be null/nothing if no one is listening).

SamplePage can then take advantage of this by hooking into the PageClick event like any other:

1: //C#

2: protected ResultPaging rp;

3: private void Page_Load(object sender, EventArgs e) {

4: rp.PageClick +=new System.Web.UI.WebControls.CommandEventHandler(rp_PageClick);

5: }

6: private void rp_PageClick(object sender, System.Web.UI.WebControls.CommandEventArgs e) {

7: //do something

8: }

1: 'VB.Net WithEvents solution

2: Private WithEvents rp As ResultPaging

3: Private Sub rp_PageClick(ByVal sender As Object, ByVal e As CommandEventArgs) Handles rp.PageClick

4: 'do something

5: End Sub

1: 'VB.Net AddHandler solution

2: Private rp As ResultPaging

3: Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

4: AddHandler rp.PageClick, AddressOf rp_PageClick

5: End Sub

6: Private Sub rp_PageClick(ByVal sender As Object, ByVal e As CommandEventArgs)

7: 'do something

8: End Sub

More likely though, the Results user control would take advantage of this event through SamplePage, or better yet by expanding the IResultContainer interface.

TimingOne of the difficulties which arises from communicating between page and user control has to do with when events happen. For example, if Results where to try and access ResultHeader's RecordsPerPage property before it was set, you would get unexpected behavior. The best weapon against such difficulties is knowledge.

When loading controls declaratively (via the @Control directive) the Load event of the page will fire first followed by the user controls in the order which they are placed on the page.

Similarly controls loaded programmatically (via Page.LoadControl) will have their Load event fire in order that they are added to the control tree (not when the call to LoadControl is actually made). For example, given the following code:

1: Control c1 = Page.LoadControl("Results.ascx");

2: Control c2 = Page.LoadControl("ResultHeader.ascx");

3: Control c3 = Page.LoadControl("ResultPaging.ascx");

4: Page.Controls.Add(c2);

5: Page.Controls.Add(c1);

c2's Load event will fire first followed by c1's. c3's load event will never fire because it isn't added to the control tree.

When both types of controls exist (declarative and programmatic), the same rules apply, except all declarative controls are loaded first then the programmatic ones. This is even true if controls are programmatically loaded in Init instead of Load

The same holds true for custom events as with builtin ones. In our event example above the following is the order of execution when a page number is clicked (assuming no control is on the page except ResultPaging):

SamplePage's OnLoad event

ResultPaging's OnLoad event

ResultPaging's pager_ItemCommand event handler

SamplePage's rp_PageClick event handler

The real difficulties arise when dealing with programmatically created controls within events - such as adding a user control to the page when a button is clicked. The problem is that such things happen after the page loads the viewstate, which, depending on what you are doing, might cause you to miss events within your user controls or cause seemingly odd behavior. As always, there are workarounds to such things, but they are well outside the scope of this tutorial. One solution might be Denis Bauer's DynamicControlsPlaceholder control (I haven't tried it yet, but looks very promising).

ConclusionIt seems like good practice to conclude by visiting the key points but really, the key points are to use what you can and try and understand as much as possible. Try to keep your designs clean, your pages flexible and above all your code readable. Pages are classes and should be treated as such, namely by understanding how inheritance works with respect to casting/ctyping and public properties and methods.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有