devxlogo

What’s New in the Visual Studio 2005 Toolbox for Windows Applications?

What’s New in the Visual Studio 2005 Toolbox for Windows Applications?

he Visual Studio 2005 toolbox provides new controls in three different areas: Data, UI, and system components. Low-level members of the old data binding architecture are now better encapsulated in new, off-the-shelf components like the DataConnector. Strip controls finally make the appealing user interface of Office 2003 available to all Windows applications. New system components like BackgroundWorker, SerialPort, and SoundPlayer enrich the toolkit and make programming much quicker, easier, and pleasant.

I had all my hair several years ago when VBX components were all the rage in the Visual Basic community. For the youngest of you, I’ll say that in the early 90s, VBX components were the first serious attempt to break applications into really reusable bits and pieces. At its core, a VBX component was nothing more than a special kind of dynamic-link library (DLL) with an embedded programming interface that meant a lot (only) to the Visual Basic 3.0 environment.

You could build GUI applications by combining a few VBX components and writing a minimum amount of glue code to put them all together. It was more or less like what you do today in .NET with controls, but it was more than ten years ago and, more importantly, it was a brand new and revolutionary model for Windows applications at the time.

When faced with a new assignment, most developers (myself included) just asked for the URL of a site where we could find a made-to-measure VBX component to have the mission accomplished in a few clicks and keystrokes. It is probably a legacy of those happy years if today many developers judge a new development tool by the number of free components it brings to the table.

Visual Studio 2005 doesn’t disappoint from this viewpoint. Its toolbox is significantly rich and, more importantly, the new toolbox addresses loudly requested functions like sexy toolbars, attractive menus, Web-like form layouts, more system components, and an improved model for data binding.

The New Toolbox
Figure 1 shows the Visual Studio 2005 toolbox as it shows up in the Beta 1 build. The toolbox is subdivided in three groups: data access, system components, and user interface elements.

?
Figure 1: A view of the Visual Studio 2005 toolbox in the Beta 1 build.

The Data block contains data-bound controls (grid and navigator), data containers (DataSet), and a new data connector component to make the binding happen.

The Components block lists some system-level controls with no user interface that provide a declarative programming layer on top of system capabilities such as the shell tray area, background threads, serial port I/O, and file watching.

Finally, the Windows Forms tab contains all visual controls, most of which are already available in Visual Studio .NET 2003. Also in this area, though, there are some interesting new entries like several strip controls, layout controls, dialog boxes, and document hosts.

Let’s start with data controls.

The Data Binding Model
The key data-driven control in Windows Forms 2.0 is the DataGridView control, the successor to the .NET 1.x DataGrid control.

Author’s Note: The DataGridView control was named GridView in builds earlier than Beta 1.

The DataGridView control provides a very flexible way to display tabular data and you can use it for a variety of purposes. For example, you can use the control to provide a read-only view of a small amount of data. Furthermore, you can successfully employ it to show editable views of larger blocks of data.

?
Figure 2: The Actions panel of a DataGridView control in a Windows Form.

DataGridView controls are highly customizable and not just as far as visual settings are concerned. You can personalize the control beyond imagination with custom behaviors such as a user-defined sorting or your own types of cells. In addition, you can bind it to many types of data stores: local and remote databases, custom business objects, and Web services. The control, though, can also operate with no bound data source.

Let’s see what happens when you drag and drop a DataGridView control onto a Windows Form. Figure 2 shows the action panel that displays next to the control once it has been added or selected.

The “Auto Format…” action lets you change the appearance of the control at will by choosing one of several predefined skins. If you’re familiar with Visual Studio .NET 2003, this feature is nothing new. You can add a new column or edit the existing ones, and you can enable and disable basic functions such as delete, insert, update, and column reordering. The key operation, though, is assigning the control a data source.

A data source can be a data connector or a local instance of an enumerable data object. More precisely, the DataGridView control accepts any class that implements any of the interfaces in Table 1.

Table 1: Valid interfaces for binding data to a DataGridView control.

Interface

Description

IList

An enumerable interface that is typically implemented by arrays, collections, and ADO.NET DataView objects.

IListSource

The interface supported by classes that provide several views of the data. Classic examples are the ADO.NET DataSet and DataTable classes.

IBindingList

Evolution of the IList interface, IBindingList provides the features required to support both complex and simple scenarios when binding to a data source. A typical class that implements this interface is the generic BindingList class.

IBindingListView

Extends the IBindingList interface by providing advanced sorting and filtering capabilities. DataConnector classes implement this interface.

To bind a data source, you simply assign the object to the DataSource property of the control.

In most cases, you will typically bind the control to a data connector class and bind, in turn, the connector component to another data source or populate it using business objects. In Windows Forms 2.0, the DataConnector component is the favorite data source object because through it you can bind to a variety of data sources. In addition, the DataConnector class is designed to resolve many data binding issues automatically. Let’s see how Visual Studio .NET 2005 creates a data connector.

Creating a Connector to the Data
The first time you click to choose the data source of a DataGridView control, the only action you can take is to add a new data source project (see Figure 3).

?
Figure 3: The dialog box that lets you add a new data source project to the application.

The Database option lets you define the connection string, select the table to query, and then creates a typed DataSet object in the project to hold the returned data. At the end of the wizard, the Visual Studio 2005 project contains three new objects: a data connector, a typed DataSet, and a custom data adapter. The data adapter will run the query to populate the typed DataSet.

The following code snippet shows the code that is added to the form to make sure the DataSet fills when the form loads.

   void Form1_Load(object sender, EventArgs e)   {   // Fill the DataSet       employeesTableAdapter.Fill(          northwindDataSet.Employees);   }

Note that Visual Studio 2005 creates and manages several .cs or .vb files for each class member of the project. The class that implements the main form, for example, is split over at least two files: form1.cs and form1.designer.cs. The latter file contains the blocks of code that were hidden in auto-generated regions in Visual Studio .NET 2003. The mechanics of partial classes makes this possible. Note that all the involved files are visible only if you toggle on the “Show All Files” option in the Solution Explorer. Be aware of this if you’re going to move files from one folder to another.

The DataConnector control encapsulates the data source for a Windows Form. It binds to a physical data source through the DataSource and DataMember properties, as shown below.

   // Settings excerpted from form1.designer.cs   EmpDataConnector.DataMember = "Employees";   EmpDataConnector.DataSource = northwindDataSet;   EmpDataConnector.CurrentChanged +=       new EventHandler(EmpDataConnector_CurrentChanged);

Visual Studio 2005 automatically creates the data connector during the setup of the DataGridView control. However, you can find the control in the toolbox so you can also add it manually.

The data connector monitors the associated data source object. When a change is detected, the bound control is promptly and automatically refreshed. As in Windows Forms 1.x, the Currency Manager object does the monitoring. The CurrentChanged event handler you see above is added to the form’s code in case you want to perform some extra action when the grid (or any other data-bound control) is being refreshed.

As mentioned earlier, you can create a data connector from a variety of sources including Web services, custom data container classes, and local database files such as MDF SQL Server files and MDB Access files. If you choose the Web service option from the dialog in Figure 3, Visual Studio 2005 runs the Add Web Reference dialog box and lets you locate a Web service locally or remotely. The binding in this case is not direct, but you end up adding a Web service proxy class to the project. Using the methods of the Web service you get a bindable object for the grid.

Finally, you can opt for a custom object. In this case, the object can be part of the project or it can be located through a referenced assembly. In any case, the class must implement any of the interfaces listed in Table 1.

The DataNavigator Control
The DataNavigator control provides the navigation and manipulation bar for controls on a form that are bound to data. You will use this control when you have a bunch of labels, text boxes, drop-down lists, and list boxes to display all the information available for a given record. If you only display one record at a time, how do you move to the next or the previous record?

In .NET 1.x, you create your own button bar to navigate, edit, and save the currently displayed record. The DataNavigator control (see Figure 4) provides just this functionality through a common user interface.

?
Figure 4: The DataNavigator control in action drives the contents of a few text boxes.

You can move through the bound set of records either using a classic VCR-like interface or by typing the index of the desired record. The standard configuration of the control also provides predefined buttons to add a new record or delete the currently displayed one.

You can customize the toolbar that forms the navigator through the Visual Studio 2005 IDE. You can add new buttons, remove existing ones, and dock it elsewhere in the form.

How do you ensure that a new index set causes bound controls to refresh? Visual Studio 2005 has a few Currency Manager objects working behind the scenes. In .NET 2.0, the Navigator control hides these objects from view. You associate the text boxes with binding objects as follows.

   // Bind the Text property of the lName textbox    // to the LastName data field   lName.DataBindings.Add(      new Binding("Text", EmpDataConnector,       "LastName", true)   );
?
Figure 5: Binding the Text property of a TextBox to a data source field in the Visual Studio 2005 IDE.

The code shown above should be nothing new to experienced Windows Forms developers. You first create a new Binding object that creates a logical link between a control property?Text in this case?and a field in a data source component. In the sample, the data source is the data connector object whereas the field name is LastName.

A Binding object alone is not enough to bind properties with data. The logical binding must be associated with a physical control that has a Text property to supply. This is what happens when you add the Binding object to the DataBindings collection of the lName TextBox control.

The actual code run by the .NET Framework is nearly identical in version 2.0 and 1.x. However, the Visual Studio 2005 designer runs some greatly improved code, as Figure 5 shows.

A dialog box pops up when you try to bind the Text property to a data source field. You simply have to select the proper field out of an existing data source. If needed, you can use a wizard to create a new data source. The underlying data binding mechanism is not really different from Windows Forms 1.x; the overlooking design-time support is significantly and pleasantly different.

New Components for System Functions
As you saw in Figure 1, the Components tab of the Visual Studio 2005 toolbox sports a few new and interesting entries. Aside from the DataConnector component that I just discussed, two more entries catch the eye?the SerialPort and BackgroundWorker components.

With the SerialPort class, serial port communication finally arrives in .NET. Preceded by a free component available on GotDotNet and working in .NET 1.x applications, the SerialPort class represents any resources available through the serial port. You use this class to perform synchronous I/O over a port. In addition, a bunch of events are available to let you catch bytes, EOF characters received, or errors. You can also use the SerialPort class to gain access to pin and break states, and driver properties.

More importantly, you can wrap the functionality of the SerialPort class in an internal Stream object, accessible through the BaseStream property. In this way, you can pass it to any classes that can consume streams.

Visual Studio 2005 defines the SerialPort class in the System.IO.Ports namespace and you use it as follows.

   SerialPort com = new SerialPort("COM1");   com.ReceivedEvent += new EventHandler(Received);

Note that the serial port components work well with virtual COM ports but it doesn’t support USB ports. This is by design because USB is a hardware bus that only Windows device-drivers control. As such, it is not directly exposed to user-mode applications, including .NET applications.

Background Tasks
I would say nothing really new by stating that many commonly performed operations can take a long time to execute. Some operations normally cause the front-end of your application to hang until they complete and return. A prompt and responsive user interface is way better for all users and developers. The recipe to solve this issue is known: Run the lengthy operation on a background thread. In Windows Forms 1.x, the threading API made this task much more affordable than it was in Win32. In the .NET Framework 2.0, the BackgroundWorker class provides an even more convenient solution.

The BackgroundWorker class saves you from dirtying your hands with threads and provides watershed events where you know exactly what to do and how. One of the snags with direct thread handling is that you can’t touch any UI control from within the secondary thread running the lengthy operation. No doubt this fact leads you to writing neater code; however, it raises trouble in the short run by forcing you to implement effective patterns. The BackgroundWorker class does much of this work for you. I’d be a liar if I said that you only need to write a little code on your own, but the good news is that the code you must write is clear, concise, and easy to understand. Let’s take the sample code you can get from the MSDN online documentation, put it through its paces, and build a sample form that calculates a Fibonacci number with the usual set of bells and whistles of an asynchronous operation: toggle button, abort option, and progress bar.

Setting up a BackgroundWorker component is an overall simple procedure. You start the operation by calling the RunWorkerAsync method.

   // Toggle the status of Start/Cancel buttons   buttonStart.Enabled = false;   buttonCancel.Enabled = true;      // Get the value from the UpDown control   numberToCompute = (int) numericUpDown1.Value;      // Start the LENGTHY operation   backgroundWorker1.RunWorkerAsync(numberToCompute);

The RunWorkerAsync method takes an object that represents the input parameter for the lengthy operation. Should you need to pass multiple values to the method, all of them must be grouped in a custom class. In the sample above, it just receives an integer that represents the value to compute in the Fibonacci series.

The default event of the BackgroundWorker class is DoWork and it’s the place where the actual, potentially time-consuming work, is done. Here’s a typical implementation of the event handler.

   void backgroundWorker1_DoWork(object sender,       DoWorkEventArgs e)   {      BackgroundWorker worker;      worker = (BackgroundWorker) sender;   int num = (int) e.Argument;      e.Result = ComputeFibonacci(num, worker, e);   }

The handler retrieves the instance of the class and the argument passed to RunWorkerAsync and uses this data to prepare the call to the long-running method. The result of the computation is assigned to the Result property of the DoWorkEventArgs object. In this way, the result will be available to the application through the component’s RunWorkerCompleted event.

You must also handle the RunWorkerCompleted event. It fires when the lengthy operation has completed. Note that this event handler runs in the primary thread so that you can safely and successfully access UI elements. While refreshing the user interface, you might want to check some state properties on the BackgroundWorker class. Here’s an example:

   if (e.Error != null)      MessageBox.Show(e.Error.Message);   else if (e.Cancelled)      result.Text = "Operation aborted";   else       result.Text = e.Result.ToString();

If your code detects an error, you show a message box. If the user aborts the operation, you reset the user interface; otherwise, you proceed consuming the results.

Interacting with a progress bar is easy too. You just have to write a handler for the ProgressChanged event. The handler receives the progress percentage that you retrieve and uses it to update your user interface. (This handler, in fact, runs within the UI thread.) The following code snippet demonstrates how to proceed with a Progress Bar control and a label.

   void backgroundWorker1_ProgressChanged(   object sender, ProgressChangedEventArgs e)   {     progressBar1.Value = e.ProgressPercentage;     result.Text = String.Format(        "Calculating... {0}% done.",           e.ProgressPercentage);   }

Figure 6 shows the sample application in action. Listing 1 contains the most important part of this code.

?
Figure 6: A form that executes a background operation.

You must design the method that implements the lengthy operation to support a progress bar, or any sort of similar UI widget. In fact, it is up to the method that performs the lengthy task to fire the ProgressChanged event whenever it reckons it useful and significant. Listing 2 presents the full implementation of the method that computes a Fibonacci number.

The ComputeFibonacci method calculates the value to report as the progress done on the operation and calls a method on the worker class-the ReportProgress method. This method greatly simplifies programming because of its simple prototype and the fact that it is exposed from the ubiquitous BackgroundWorker class. Use the CancelAsync method to abort a running operation.

A Revamped User Interface
The official style of Windows applications has never been determined by the official API?the Windows SDK earlier and the .NET Framework now. Flagship applications like Microsoft Office and Visual Studio have always boasted the user interface of tomorrow’s user applications and gave the start to a bunch of third-party companies to provide similar controls to the community.

?
Figure 7: The new ToolStrip control at design-time.

In the .NET Framework 2.0, Microsoft has updated some user interface controls including toolbars and menus to look much more trendy and appealing to users. Any Windows Forms 2.0 applications can now show the same look-and-feel of Microsoft Office 2003 or Visual Studio 2005.

The new MenuStrip and ToolStrip controls are the successors to MainMenu and Toolbar respectively, but they provide all the features of popular applications including rebar bands, gradients of color, and a variety of different items. Figure 7 shows a ToolStrip control at design-time.

As you can guess from the figure, the control is a collection of items each with a different user interface and capabilities. Feasible items include buttons, labels, separators, drop-down lists, drop-down buttons, text boxes, and progress bars. You build your toolbar by taking advantage of the rich services offered by Visual Studio 2005 and arranging any combination of the above items.

?
Figure 8: Configuring a toolbar button.

You can associate each button with a bitmap; but where do you store these images? You should take these bitmaps out of a resource file possibly embedded in the assembly. In Windows Forms 1.x, though, working with resources was no picnic. Things are much better in Visual Studio 2005. Figure 8 shows the dialog box you use to configure a toolbar button.

You can easily control the behavior of the button and decide whether it is enabled or disabled, if it has to show a shortcut key, or toggle its pressed state on click. Likewise, you can control text, name, type, and image. When you click to edit the image, a new dialog box appears to let you import images from your hard drive into a brand new resource file. Needless to say, Visual Studio 2005 automatically creates the resource .resx file and attaches it to the current project. As a result, from now on your images travel with your assemblies.

In Windows Forms 2.0, you will find two classes for each type of menu you may think of?main menu and context menu. MenuStrip and ContextMenuStrip are new to the .NET Framework 2.0 and provide a nicer user interface and a richer set of design-time services. You’ll also find the menu classes you know from the previous version of the .NET Framework. These classes haven’t been updated. Note, though, that in order to associate a context menu to a .NET control, you have to define a ContextMenuStrip object. Figure 9 demonstrates the new look-and-feel of menus.

?
Figure 9: The new user interface of Windows menus.

Speaking of strip controls, the StatusStrip control is also worth mentioning. It succeeds the Status Bar control and is rendered as a collection of rich items like text panels (the standard cells of classic status bars), progress bars, drop-down lists, and split buttons. (A split button is a variant of a drop-down list like the “People” button in the toolbar of Figure 9.)

Layout Controls
HTML-based user interfaces give you much more freedom when it comes to designing the layout of a form. This

tag provides most of the flexibility because its usage goes far beyond the original goal of providing a tabular list of strings.

While building HTML pages, you place one or more

tags in a page and populate each cell with any combination of tags and text. Windows forms don’t have an equivalent to the
tag. This doesn’t mean, though, that you can’t get the same flexibility when you build a user interface in a Windows application; quite the reverse, I’d say. The rub, though, is that you have to build it yourself, step-by-step, and with no help from the designer.

In Windows Forms 2.0, you will find the TableLayoutPanel and FlowLayoutPanel layout controls that help a lot in pursuing a behavior similar to the

tag of HTML pages.
?
Figure 10: A TableLayoutPanel control in action at design time.

The TableLayoutPanel represents a panel that dynamically lays out its content in a grid composed of a given number of rows and columns. The FlowLayoutPanel dynamically lays out its contents horizontally or vertically. Each cell of the grid (see Figure 10) contains at most one control. If you need to display more controls, you should first create a composite custom control to group them all with any needed glue code.

Both controls inherit from Panel and implement the IExtenderProvider interface to automatically extend the programming interface of contained controls. Dynamically added properties include Row, RowSpan, Column, and ColumnSpan. The TableLayoutPanel supports a fair number of visual properties much like the HTML counterpart.

Miscellaneous Controls
To top off this article entirely devoted to a tour of the new controls in the .NET Framework 2.0, I want to briefly review a bunch of controls that provide miscellaneous functions not available in .NET Framework 1.x and loudly requested by the community of developers. Let me introduce you to the MaskedTextBox, SoundPlayer, and WebBrowser controls.

The MaskedTextBox Control
MaskedTextBox is a variant to the classic TextBox control and provides for masked input. The principal extension to the base control is the Mask property.

   // Configure the control to accept only    // valid social security numbers   maskedTextBox1.Mask = "000-00-0000";

The Mask property indicates the mask to use at runtime when typing into the control. The mask must be a string composed of one or more masking elements as defined in Table 2.

Table 2: Masking elements supported by the MaskedTextBox control.

Element

Description

0

Accept any single digit between 0 and 9. The digit is required.

9

Accept a digit or space. The entry is optional.

#

Optional entry, accepts digits or spaces. If this position is left blank, it will be rendered as a space in the text properties of the control. Plus (+) and minus (-) signs are allowed.

L

Required entry, restricts input to the ASCII letters a-z and A-Z.

?

Like “L” but optional.

&

Required entry, accepts a character. If the AsciiOnly property on the control is set to true, this element behaves like the “L” element.

C

Like “&” but optional. If the AsciiOnly property on the control is set to true, this element behaves like the “?” element.

A

Optional entry that accepts any alphanumeric characters.

.

Decimal placeholder. The actual display character is determined by the control’s Culture property.

,

Thousands placeholder. The actual display character is determined by the control’s Culture property.

:

Time separator. The actual display character is determined by the control’s Culture property.

/

Date separator. The actual display character is determined by the control’s Culture property.

$

Currency symbol. The actual display character is determined by the control’s Culture property.

Converts all characters that follow to lowercase. (Shift down)

>

Converts all characters that follow to uppercase. (Shift up)

|

Disable a previous shift up or shift down.

Escape. Escapes a mask character, turning it into a literal. “\” is the escape sequence for a backslash.

The MaskedTextBox control features three text properties: Text, InputText, and OutputText. The first property gets and sets the text as it is currently displayed to the user. Looking at Figure 11, Text is 123-45-67 with two final blanks. InputText simply returns or sets the raw sequence of input characters (1234567 in the example). OutputText represents the real input buffer of the control and returns the formatted text including any placeholder and embedded symbols. Unlike the other two, this property is read-only.

Playing Sounds

?
Figure 11: Entering preformatted input in a MaskedTextBox control.

The SoundPlayer control governs the playback of .wav files from a local path, a stream, or a URL. The playback is articulated in two steps-loading and playing-and can occur both synchronously and asynchronously. In the latter case, an event-driven interface lets you detect the end of the playback.

   // Instantiate the control    SoundPlayer player = new SoundPlayer();      // Set the location of the file to play   player.SoundLocation = wavPath;

You first set the location for the .wav file and then load and play the file. Methods in Table 3 show how to accomplish it. You store the path to the sound file in the SoundLocation property.

Table 3: Method to load and play .wav files.

Method

Description

Load

Loads the specified sound into an internal buffer. The operation occurs synchronously.

LoadAsync

Same as above except that operation occurs asynchronously on a new thread.

Play

Plays the .wav file asynchronously using a new thread. If the no method Load was previously called it first loads the file and then plays it.

PlayLooping

Same as Play except that it loops until the method Stop is called.

PlaySync

Optionally loads and plays the specified .wav file on the synchronously on the primary UI thread.

Stop

Stops playback of the sound.

After your application successfully loads a .wav file in memory, future calls to playback methods will not need to reload the file. If you change the path stored in the SoundLocation property, this action resets an internal flag that causes the sound to be reloaded before the next playback.

If the name of the .wav file has not been specified, or if the file fails to load, the playback methods will play the default beep sound.

A WebBrowser Wrapper
Windows Forms 1.x really needed a control with browsing capabilities, although embedding the WebBrowser ActiveX control isn’t a hard task at all. Luckily Microsoft will ship a ready-to-use browsing .NET component in Windows Forms 2.0.

The WebBrowser control lets you host Web pages and other browser-enabled documents in Windows applications. You use the Url property to set the URL of the page to display or you can call the Navigate method.

   webBrowser1.Navigate(TextBoxAddress.Text); 

Methods like GoBack and GoForward let you move between pages. Overall, the programming interface of the control mimics that of the WebBrowser ActiveX component of which the WebBrowser control is a wrapper. One significant enhancement is the possibility the control offers to display content that you create in your application or retrieve from a database or resource file.

You use the DocumentText or DocumentStream property to get or set the contents of the current document as a string or data stream.

Your application can manipulate the contents of the currently displayed Web page through the Document property. The property returns an HtmlDocument object that provides access to the Dynamic HTML (DHTML) object model for the page.

You can make the communication between the hosted page and the surrounding application bi-directional if you set the ObjectForScripting property (see Listing 3). The object you assign to the ObjectForScripting property is associated with the window.external object in the DHTML object model and can be invoked from within any script code you use, or inject, in the hosted page. The code in Listing 3 shows how to build and display a page dynamically using DocumentText. The dynamically created page contains some script code that uses an external object defined on the host-the window.external object. In the sample code, this external object is the form class itself. As a result, your HTML script can invoke any public method on a .NET class.

Many useful controls and required functionalities didn’t find their way into Windows Forms 1.x. Some of them made it through in the next version, currently available in Beta. In this article, I’ve examined the new stuff in the Visual Studio 2005 toolbox.

In brief, you have new controls in the data binding area, new system components, and a bunch of new user interface elements. In my humble opinion, the real added value to programming power is in the layout controls?a pretty hard feature to build manually. Strip controls, data connectors, and various system components are mostly excellent wrappers around existing Win32 functions or public domain components (like the SerialPort control).

However, in no way does this mean that there’s nothing new, or nothing really interesting, in Windows Forms 2.0. Quite the opposite! The improvement in productivity is huge and the design-time support is really great.

For me, the biggest things in Visual Studio 2005 are the designers both for ASP.NET and Windows Forms applications, for data binding and UI-related tasks. Try it out!

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist