1. Trang chủ
  2. » Công Nghệ Thông Tin

Thinking in C# phần 8 ppt

85 208 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 85
Dung lượng 565,83 KB

Nội dung

574 Thinking in C# www.ThinkingIn.NET int id = instanceCounter++; internal TwoState(){ this.Text = State; System.EventHandler hndlr = new System.EventHandler(buttonClick); this.Click += hndlr; } bool state = true; public string State{ get { return(state == true) ? "On" : "Off"; } set{ state = (value == "On") ? true : false; OnStateChanged(); } } private void buttonClick( object sender, System.EventArgs e){ changeState(); } public void changeState(){ state = !state; OnStateChanged(); } public void OnStateChanged(){ Console.WriteLine( "TwoState id " + id + " state changed"); this.Text = State; } } class ChristmasTree : Panel { bool allOn; internal bool AllOn{ get { return allOn;} Chapter 14: Programming Windows Forms 575 } public ChristmasTree(){ TwoState ts = new TwoState(); ts.Location = new Point(10, 10); TwoState ts2 = new TwoState(); ts2.Location = new Point(120, 10); Add(ts); Add(ts2); BackColor = Color.Green; } public void Add(TwoState c){ Controls.Add(c); c.Click += new EventHandler( this.TwoClickChanged); } public void AddRange(TwoState[] ca){ foreach(TwoState ts in ca){ ts.Click += new EventHandler( this.TwoClickChanged); } Controls.AddRange(ca); } public void TwoClickChanged( Object src, EventArgs a){ allOn = true; foreach(Control c in Controls){ TwoState ts = c as TwoState; if (ts.State != "On") { allOn = false; } } if (allOn) { BackColor = Color.Green; } else { BackColor = Color.Red; } } 576 Thinking in C# www.MindView.net } class PACForm : Form { ChristmasTree p1 = new ChristmasTree(); ChristmasTree p2 = new ChristmasTree(); public PACForm(){ ClientSize = new Size(450, 200); Text = "Events & Models"; p1.Location = new Point(10,10); p2.Location = new Point(200, 10); Controls.Add(p1); Controls.Add(p2); } static void Main(){ Application.Run(new PACForm()); } }///:~ When run, if you set both the buttons within an individual ChristmasTree panel to “On,” the ChristmasTree’s background color will become green, otherwise, the background color will be red. The PACForm knows nothing about the TwoStates within the ChristmasTree. We could (and indeed it would probably be logical) change TwoState from descending from Button to descending from Checkbox and TwoState.State from a string to a bool and it would make no difference to the PACForm that contains the two instances of ChristmasTree. Presentation-Abstraction-Control is often the best architecture for working with .NET. Its killer advantage is that it provides an encapsulated component. A component is a software module that can be deployed and composed into other components without source-code modification. Visual Basic programmers have enjoyed the benefits of software components for more than a decade, with thousands of third-party components available. Visual Studio .NET ships with a few components that are at a higher level than just encapsulating standard controls, for instance, components which encapsulate ADO and another which encapsulates the Crystal Reports tools. PAC components need not really be written as a single class; rather, a single Control class may provide a single public view of a more complex namespace Chapter 14: Programming Windows Forms 577 whose members are not visible to the outside world. This is called the Façade design pattern. The problem with PAC is that it’s hard work to create a decent component. Our ChristmasTree component is horrible – it doesn’t automatically place the internal TwoStates reasonably, it doesn’t resize to fit new TwoStates, it doesn’t expose both logical and GUI events and properties… The list goes on and on. Reuse is the great unfulfilled promise of object orientation: “Drop a control on the form, set a couple of properties, and boom! You’ve got a payroll system.” But the reality of development is that at least 90% of your time is absolutely controlled by the pressing issues of the current development cycle and there is little or no time to spend on details not related to the task at hand. Plus, it’s difficult enough to create an effective GUI when you have direct access to your end-users; creating an effective GUI component that will be appropriate in situations that haven’t yet arisen is almost impossible. Nevertheless, Visual Studio .NET makes it so easy to create a reusable component (just compile your component to a .DLL and you can add it to the Toolbar!) that you should always at least consider PAC for your GUI architecture. Model-View-Controller Model-View-Controller, commonly referred to simply as “MVC,” was the first widely known architectural pattern for decoupling the graphical user interface from underlying application logic. Unfortunately, many people confuse MVC with any architecture that separates presentation logic from domain logic. So when someone starts talking about MVC, it’s wise to allow for quite a bit of imprecision. In MVC, the Model encapsulates the system’s logical behavior and state, the View requests and reflects that state on the display, and the Controller interprets low- level inputs such as mouse and keyboard strokes, resulting in commands to either the Model or the View. MVC trades off a lot of static structure — the definition of objects for each of the various responsibilities – for the advantage of being able to independently vary the view and the controller. This is not much of an advantage in Windows programs, where the view is always a bitmapped two-dimensional display and the controller is always a combination of a keyboard and a mouse-like pointing device. However, USB’s widespread support has already led to interesting new controllers and the not-so-distant future will bring both voice and gesture control and “hybrid reality” displays to seamlessly integrate computer-generated data into real vision (e.g., glasses that superimpose arrows and labels on reality). If you happen to be lucky enough to be working with such advanced technologies, 578 Thinking in C# www.ThinkingIn.NET MVC may be just the thing. Even if not, it’s worth discussing briefly as an example of decoupling GUI concerns taken to the logical extreme. In our example, our domain state is simply an array of Boolean values; we want the display to show these values as buttons and display, in the title bar, whether all the values are true or whether some are false: //:c14:MVC.cs using System; using System.Windows.Forms; using System.Drawing; class Model { internal bool[] allBools; internal Model(){ Random r = new Random(); int iBools = 2 + r.Next(3); allBools = new bool[iBools]; for (int i = 0; i < iBools; i++) { allBools[i] = r.NextDouble() > 0.5; } } } class View : Form { Model model; Button[] buttons; internal View(Model m, Controller c){ this.model = m; int buttonCount = m.allBools.Length; buttons = new Button[buttonCount]; ClientSize = new Size(300, 50 + buttonCount * 50); for (int i = 0; i < buttonCount; i++) { buttons[i] = new Button(); buttons[i].Location = new Point(10, 5 + i * 50); c.MatchControlToModel(buttons[i], i); buttons[i].Click += Chapter 14: Programming Windows Forms 579 new EventHandler(c.ClickHandler); } ReflectModel(); Controls.AddRange(buttons); } internal void ReflectModel(){ bool allAreTrue = true; for (int i = 0; i < model.allBools.Length; i++) { buttons[i].Text = model.allBools[i].ToString(); if (model.allBools[i] == false) { allAreTrue = false; } } if (allAreTrue) { Text = "All are true"; } else { Text = "Some are not true"; } } } class Controller { Model model; View view; Control[] viewComponents; Controller(Model m){ model = m; viewComponents = new Control[m.allBools.Length]; } internal void MatchControlToModel (Button b, int index){ viewComponents[index] = b; } internal void AttachView(View v){ this.view = v; } 580 Thinking in C# www.MindView.net internal void ClickHandler (Object src, EventArgs ea){ //Modify model in response to input int modelIndex = Array.IndexOf(viewComponents, src); model.allBools[modelIndex] = !model.allBools[modelIndex]; //Have view reflect model view.ReflectModel(); } public static void Main(){ Model m = new Model(); Controller c = new Controller(m); View v = new View(m, c); c.AttachView(v); Application.Run(v); } }///:~ The Model class has an array of bools that are randomly set in the Model constructor. For demonstration purposes, we’re exposing the array directly, but in real life the state of the Model would be reflected in its entire gamut of properties. The View object is a Form that contains a reference to a Model and its role is simply to reflect the state of that Model. The View( ) constructor lays out how this particular view is going to do that – it determines how many bools are in the Model’s allBools array and initializes a Button[] array of the same size. For each bool in allBools, it creates a corresponding Button, and associates this particular aspect of the Model’s state (the index of this particular bool) with this particular aspect of the View (this particular Button). It does this by calling the Controller’s MatchControlToModel( ) method and by adding to the Button’s Click event property a reference to the Controller’s ClickHandler( ). Once the View has initialized its Controls, it calls its own ReflectModel( ) method. View’s ReflectModel( ) method iterates over all the bools in Model, setting the text of the corresponding button to the value of the Boolean. If all are true, the title of the Form is changed to “All are true,” otherwise, it declares that “Some are false.” Chapter 14: Programming Windows Forms 581 The Controller object ultimately needs references to both the Model and to the View, and the View needs a reference to both the Model and the Controller. This leads to a little bit of complexity in the initialization shown in the Main( ) method; the Model( ) is created with no references to anything (the Model is acted upon by the Controller and reflected by the View, the Model itself never needs to call methods or properties in those objects). The Controller is then created with a reference to the Model. The Controller( ) constructor simply stores a reference to the Model and initializes an array of Controls to sufficient size to reflect the size of the Model.allBools array. At this point, the Controller.View reference is still null, since the View has not yet been initialized. Back in the Main( ) method, the just-created Controller is passed to the View( ) constructor, which initializes the View as described previously. Once the View is initialized, the Controller’s AttachView( ) method sets the reference to the View, completing the Controller’s initialization. (You could as easily do the opposite, creating a View with just a reference to the Model, creating a Controller with a reference to the View and the Model, and then finish the View’s initialization with an AttachController( ) method.) During the View’s constructor, it called the Controller’s MatchControlToModel( ) method, which we can now see simply stores a reference to a Button in the viewComponents[ ] array. The Controller is responsible for interpreting events and causing updates to the Model and View as appropriate. ClickHandler( ) is called by the various Buttons in the View when they are clicked. The originating Control is referenced in the src method argument, and because the index in the viewComponents[ ] array was defined to correspond to the index of the Model’s allBools[ ] array, we can learn what aspect of the Model’s state we wish to update by using the static method Array.IndexOf( ). We change the Boolean to its opposite using the ! operator and then, having changed the Model’s state, we call View’s ReflectModel( ) method to keep everything in synchrony. The clear delineation of duties in MVC is appealing – the View passively reflects the Model, the Controller mediates updates, and the Model is responsible only for itself. You can have many View classes that reflect the same Model (say, one showing a graph of values, the other showing a list) and dynamically switch between them. However, the structural complexity of MVC is a considerable burden and is difficult to “integrate” with the Visual Designer tool. 582 Thinking in C# www.ThinkingIn.NET Layout Now that we’ve discussed the various architectural options that should be of major import in any real GUI design discussion, we’re going to move back towards the expedient “Do as we say, not as we do” mode of combining logic, event control, and visual display in the sample programs. It would simply consume too much space to separate domain logic into separate classes when, usually, our example programs are doing nothing but writing out simple lines of text to the console or demonstrating the basics of some simple widget. Another area where the sample programs differ markedly from professional code is in the layout of Controls. So far, we have used the Location property of a Control, which determines the upper-left corner of this Control in the client area of its containing Control (or, in the case of a Form, the Windows display coordinates of its upper-left corner). More frequently, you will use the Dock and Anchor properties of a Control to locate a Control relative to one or more edges of the container in which it resides. These properties allow you to create Controls which properly resize themselves in response to changes in windows size. In our examples so far, resizing the containing Form doesn’t change the Control positions. That is because by default, Controls have an Anchor property set to the AnchorStyles values Top and Left (AnchorStyles are bitwise combinable). In this example, a button moves relative to the opposite corner: //:c14:AnchorValues.cs using System.Windows.Forms; using System.Drawing; class AnchorValues: Form { AnchorValues(){ Button b = new Button(); b.Location = new Point(10, 10); b.Anchor = AnchorStyles.Right | AnchorStyles.Bottom; Controls.Add(b); } public static void Main(){ Application.Run(new AnchorValues()); } Chapter 14: Programming Windows Forms 583 }///:~ If you combine opposite AnchorStyles (Left and Right, Top and Bottom) the Control will resize. If you specify AnchorStyles.None, the control will move half the distance of the containing area’s change in size. This example shows these two types of behavior: //:c14:AnchorResizing.cs using System.Windows.Forms; using System.Drawing; class AnchorResizing: Form { AnchorResizing(){ Button b = new Button(); b.Location = new Point(10, 10); b.Anchor = AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top; b.Text = "Left | Right | Top"; Controls.Add(b); Button b2 = new Button(); b2.Location = new Point(100, 10); b2.Anchor = AnchorStyles.None; b2.Text = "Not anchored"; Controls.Add(b2); } public static void Main(){ Application.Run(new AnchorResizing()); } }///:~ If you run this example and manipulate the screen, you’ll see two undesirable behaviors: b can obscure b2, and b2 can float off the page. Windows Forms’ layout behavior trades off troublesome behavior like this for its straightforward model. An alternative mechanism based on cells, such as that used in HTML or some of Java’s LayoutManagers, may be more robust in avoiding these types of trouble, but anyone who’s tried to get a complex cell-based UI to resize the way they wish is likely to agree with Windows Forms’ philosophy! [...]... using System.Drawing; using System.Diagnostics; using System.Windows.Forms; class LinkLabelDemo : Form { LinkLabelDemo(){ LinkLabel label1 = new LinkLabel(); 604 Thinking in C# www.MindView.net label1.Text = "Download Thinking in C#" ; label1.Links.Add(9, 14, "http://www.ThinkingIn.Net/"); label1.LinkClicked += new LinkLabelLinkClickedEventHandler( InternetExplorerLaunch); label1.Location = new Point(10,... is: 590 Thinking in C# www.ThinkingIn.NET resgen swahili.txt International.sw.resources The resources file now has to be converted into a satellite assembly named MainAssembly.resources.dll This is done with the assembly linker tool al Both of these lines should be typed as a single command: al /t:lib /embed:International.sw.resources /culture:sw /out:International.resources.dll The resulting DLL should... incorporating text links into a UI has been a big challenge Windows Forms changes that with its LinkLabel control The LinkLabel has powerful support for linking, allowing any number of links within the label area The LinkLabel facilitates the creation of even complex linking semantics, such as the XLink standard (http://www.w3.org/TR/xlink/) While it’s possible to use a LinkLabel to activate other Windows Forms... with only a few lines of code, images are inherently dependent on binary data storage It’s not surprising that you can load an image into a Windows Form by using a Stream or a filename, as this example demonstrates: //:c14:SimplePicture.cs Chapter 14: Programming Windows Forms 585 //Loading images from file system using using using using System; System.IO; System.Windows.Forms; System.Drawing; class SimplePicture... e.Link.Visited = true; } public void MessageBoxShow( object src, LinkLabelLinkClickedEventArgs e){ string msg = (string) e.Link.LinkData; MessageBox.Show(msg); e.Link.Visited = true; } public static void Main(){ Application.Run(new LinkLabelDemo()); } }///:~ Not all the LinkLabel.Text need be a link; individual links are added to the Links collection by specifying an offset and the length of the link... chosen semirandomly: //:c14:TextEditing.cs ///Demonstrates the TextBox and RichTextBox controls using System.Windows.Forms; using System; using System.Collections; using System.Drawing; 600 Thinking in C# www.MindView.net class TextEditing : Form { TextBox tb; RichTextBox rtb; Random rand = new Random(); TextEditing() { ClientSize = new Size(450, 400); Text = "Text Editing"; tb = new TextBox(); tb.Text... Controls.Add(label1); LinkLabel label2 = new LinkLabel(); label2.Text = "Show message"; label2.Links.Add(0, 4, "Foo"); label2.Links.Add(5, 8, "Bar"); label2.LinkClicked += new LinkLabelLinkClickedEventHandler( MessageBoxShow); label2.Location = new Point(10, 60); Controls.Add(label2); } public void InternetExplorerLaunch( object src, LinkLabelLinkClickedEventArgs e){ string url = (string) e.Link.LinkData; Process.Start("IExplore.exe",... contains properties for setting environment variables, whether a window should be shown and in what style, input and output redirection, etc This example uses the third overload of Process.Start( ), which takes an application name and a string representing the command-line arguments, to launch Internet Explorer and surf to www.ThinkingIn.Net //:c14:LinkLabelDemo.cs //Demonstrates the LinkLabel using... Programming Windows Forms 605 need any information other than the fact that the link was clicked, you do not need to include the third argument to Links.Add( ), but typically you will store some data to be used by the event handler In the example, the phrase Thinking in C# is presented underlined, in the color of the LinkColor property (by default, this is set by the system and is usually blue) This link... argument to determine which button has been activated The use of a GroupBox and this form of sharing a delegate method is demonstrated in the next example CheckBox controls are not mutually exclusive; any number can be in any state within a container CheckBoxes cycle between two states (CheckState.Checked and CheckState.Unchecked) by default, but by 606 Thinking in C# www.ThinkingIn.NET setting the ThreeState . //:c14:SimplePicture.cs 586 Thinking in C# www.ThinkingIn.NET //Loading images from file system using System; using System.IO; using System.Windows.Forms; using System.Drawing; class SimplePicture. technologies, 5 78 Thinking in C# www.ThinkingIn.NET MVC may be just the thing. Even if not, it’s worth discussing briefly as an example of decoupling GUI concerns taken to the logical extreme. In our. 590 Thinking in C# www.ThinkingIn.NET We’ve not yet created the satellite assemblies which will serve as the “spokes” to our International “hub,” but it is interesting to run the program in

Ngày đăng: 06/08/2014, 20:20

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

  • Đang cập nhật ...

TÀI LIỆU LIÊN QUAN