Extending C# Listview with Collapsible Groups (Part II)

Posted on 49/11/2012 0:2:49 PM by John Atten in   ||   Comments (0)

The GroupedList Control Container

This post is part two of a short series on extending the Winforms Listview control. If you missed the previous post, you can review it HERE. Also, the Source Code for this project can be found in my GitHub repo.

In our previous post, we examined the first component of what I am calling the “GroupedList Control” – essentially, a list of contained and extended Listview controls which act as independent groups. Individual ListGroups (which is how I refer to them) may contain independent column headers, and are expandable/collapsible, much like what I believe is called a “slider” control.

A brief note – I am posting somewhat abbreviated code here. I have omitted many common overloads and other features we might discuss in a future post. For now, the code posted here contains only the very core functionality under discussion. The Source, however, contains all my work so far on this control.

Also note – the GroupedListControl arose out of my need for a quick-and-dirty combination of the functionality of the Winforms Listview and a Treeview. A group of columnar lists which could be independently expanded or collapsed.

A Quick Look at a Very Plain Demo:

Gl Demo 4 Widen Column 

 

 

 

 

 

 

 

 

 

 

 

 

 

In the last post, we had assembled our basic ListGroup component, which is essentially an extension of the Winforms Listview control, modified to handle some events related to column and item addition and removal. Where we left off, it was time to assemble our container, the GroupedListControl.

I figured the quickest way to accomplish what I needed (remember – under the gun, here) would be to extend the FlowLayoutPanel such that I could use this ready-made container to manage a collection of ListGroup controls, stack them vertically, and such. There were a few issues with this approach that we will discuss in a bit. First, let’s look at the basic code required to bring the control to life:

The GroupedList Control – Basic Code:

    public class GroupListControl : FlowLayoutPanel
    {

        public GroupListControl()
        {
            // Default configuration. Adapt to suit your needs:
            this.FlowDirection = System.Windows.Forms.FlowDirection.TopDown;
            this.AutoScroll = true;
            this.WrapContents = false;

            // Add a local handler for the ControlAdded Event.
            this.ControlAdded += new ControlEventHandler(GroupListControl_ControlAdded);
        }


        /// <summary>
        /// Handles the ControlAdded Event for the current instance. 
        /// </summary>
        void GroupListControl_ControlAdded(object sender, ControlEventArgs e)
        {
            ListGroup lg = (ListGroup)e.Control;
            lg.Width = this.Width;
            lg.GroupCollapsed += new ListGroup.GroupExpansionHandler(lg_GroupCollapsed);
            lg.GroupExpanded += new ListGroup.GroupExpansionHandler(lg_GroupExpanded);
        }


        /// <summary>
        /// Gets or Sets a boolean value indicating whether multiple ListGroups
        /// may be in the expanded state at the same time. When set to true, the current expanded 
        /// ListGroup is collapsed when a new ListGroup is expanded. 
        /// </summary>
        public bool SingleItemOnlyExpansion { get; set; }


        /// <summary>
        /// Handles the Expanded event for the current instance.
        /// </summary>
        void lg_GroupExpanded(object sender, EventArgs e)
        {
            // Grab a reference to the ListGroup which sent the message:
            ListGroup expanded = (ListGroup)sender;

            // If Single item only expansion, collapse all ListGroups in except
            // the one currently exanding:
            if (this.SingleItemOnlyExpansion)
            {
                this.SuspendLayout();
                foreach (ListGroup lg in this.Controls)
                {
                    if (!lg.Equals(expanded))
                        lg.Collapse();
                }
                this.ResumeLayout(true);
            }

        }


        /// <summary>
        /// Handles the Collapsed event for the current instance.
        /// </summary>
        void lg_GroupCollapsed(object sender, EventArgs e)
        {
            // No need.
        }


        /// <summary>
        /// Expands all listgroups contained in the current instance. 
        /// </summary>
        public void ExpandAll()
        {
            foreach (ListGroup lg in this.Controls)
            {
                lg.Expand();
            }
        }


        /// <summary>
        /// Collapses all ListGroups contained in the current instance.
        /// </summary>
        public void CollapseAll()
        {
            foreach (ListGroup lg in this.Controls)
            {
                lg.Collapse();
            }
        }

    }

 

Of particular note here is the GroupListControl_ControlAdded Event Handler. Sadly, when one adds controls to the FlowLayoutPanel Controls collection, they are just that. The Controls property of the FlowLayout panel represents a ControlCollection object, which accepts a parameter of type (wait for it . . . ) Control.

I wanted MY GroupedListControl to contain a collection of ListGroup objects. However, I have not yet figured out a way to do this while retaining the functionality of the FlowLayout panel. As far as I can tell, we can’t narrow the type requirement of the native ControlCollection. One option I considered would be to add a new method to the class, named AddListGroup, which could then accept a parameter of type ListGroup, and pass THAT to the Controls.Add(Control) method. However, that seems a bit mindless, as the Controls.Add(0 method would remain publicly exposed, thus creating opportunity for confusion.

For now, I decided that those using this control will have to realize that passing anything other than a ListGroup object as the parameter will likely be disappointed in the performance of the control! It is less than elegant, but I didn’t have time to figure out a more elegant solution, and for the moment it works. I would love to hear suggestions for improvement.

The next thing to notice about the GroupListControl_ControlAdded method is that for each ListGroup we add, we are subscribing to the GroupExpanded and GroupCollapsed events sourced by each individual ListGroup. This is mainly because there are use cases in which we might want to limit group expansion to a single group at a time, such that expanding one group collapses any other expanded group. This is accomplished by providing the boolean SingleItemOnlyExpansion property. The GroupListControl_ControlAdded method checks the state of this property, and if true, collapses any expanded groups which are not equal to (as in, referencing the same object instance as) the current group (the “sender” in the method’s signature).

The last thing to note is the manner in which we set the width of each ListGroup in the GroupListControl_ControlAdded method. I tried setting the Dock property instead, and ran into difficulties with that.

Given the code above, you would think all that was pretty simple, no? Yeah. Right. A problem arose in the form of ugly scrollbars. The code above will run, and do everything represented. However, for the GroupedListControl to look like anything other than ass, we need to do something about the horizontal scrollbar which appears at the bottom of the GroupedListControl , due to the width of each ListGroup being essentially the same as the container control. This, I must say was initially giving me pains. The FlowLayoutPanel does not, apparently, afford us the ability to control the appearance of the horizontal and vertical scrollbars individually.

Some research on the interwebs yielded, after no small amount of digging, the following solution. Sadly, it involves Windows messages and API calls, neither of which I am particularly well-versed in. More sadly, I seem to have misplaced the link to where I found the solution. If YOU know where the concept below came from, please forward me a link, so I can link back, and attribute properly.

Add the following code to the end of the GroupedListControl class:

Handling Scrollbars by Intercepting Windows Messages:

        /// <summary>
        /// Consumed by the Win API calls below:
        /// </summary>
        private enum ScrollBarDirection
        {
            SB_HORZ = 0,
            SB_VERT = 1,
            SB_CTL = 2,
            SB_BOTH = 3
        }


        /// <summary>
        /// Disables the horizontal scrollbar in the primary container control.
        /// Individual ListGroups within the GroupList have their own scrollbars
        /// if needed. 
        /// </summary>
        protected override void WndProc(ref System.Windows.Forms.Message m)
        {
            // Call to unmanaged WinAPI:
            ShowScrollBar(this.Handle, (int)ScrollBarDirection.SB_HORZ, false);
            base.WndProc(ref m);
        }


        /// <summary>
        /// Imported from WinAPI: Method to control Scrollbar visibility.
        /// </summary>
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool ShowScrollBar(IntPtr hWnd, int wBar, bool bShow);

 

The above code essentially listens to windows messages, and when it “hears” one related to showing scrollbars in the FlowLayoutPanel base class, performs the appropriate action. in this case, some sort of WinAPI magic related to NOT showing the horizontal scrollbar.

Note that we WANT the vertical scrollbar to show up, anytime the height of the collected ListGroups exceeds the height of the GroupedList client area. But I decided I would prefer to have the horizontal scrolling option available within each individual ListGroup where needed, without the extra screen clutter of another horizontal scrollbar a the bottom of the container control.

Scroll Bars in the Grouped List Control (note horizontal scroll in individual ListGroup, and vertical scroll for container control . . .)

 Gl Demo 5 Vert and Horiz Scroll

 

Summary

What we have done to this point is examine the core essentials of creating a composite control which provides some very basic behaviors I needed for a project at work. Some things to remember:

  • The code in this and the previous post is somewhat abbreviated. For example, there are a number of overloads for the Add() method on both the ListViewItemCollection and the ListViewColumnCollection which we did not address here. They are, however, mostly addressed in the Source Code on Github. I will say not all the overloads have been properly tested.
  • Another requirement I had for my control was the ability to detect Right-Mouse-Clicks on the column headers in each individual GroupedList. This capability is not built into the Listview control, and in fact it was a bit of an exercise to make it happen. More adventures with external calls to the WinApi. I will likely examine this in my next post.
  • Populating the GroupedList control takes only a little more thought and planning that doing the same with a regular Listview. In many ways, it is akin to populating a two-tiered Treeview control. The Example project in the source code repo demonstrates this in a very, very basic way. I know thus far it has met my own needs rather nicely. I needed to make a large amount of data available to the user with a minimal number of clicks, and with minimal return trips to the database.
  • I would love to hear about improvements, and especially where I have done something dumb. I am here to learn, so bring it. Feel free to fork the source, and please do put in a pull request for any changes or improvements you make.

I will try to follow up with a post about adding Right-Click detection for the ListGroup column Headers in a day or two. This enables us to deploy a different ContextMenuStrip when the user right-clicks on a columnheader vs. the standard context menu for the ListView Control.

Thanks for reading do far . . .

 

Posted on 49/11/2012 0:2:49 PM by John Atten     

Comments (0)

Extending C# Listview with Collapsible Groups (Part I)

Posted on 11/09/2012 0:5:11 AM by John Atten in   ||   Comments (0)

NOTE: This post is kinda long. However, most of the length is a result of code postings (even after removing some extra stuff). Bear with me!

I’ve been deep in a project for work for the past two months. Sadly, it is nothing sexy, no exciting bleeding-edge technology, just another enterprise database, using the very mature and slightly dull Winforms library in the .NET platform.

However, I did stumble across an interesting project requirement for what is essentially an expandable group of the venerable Listview control, what one might get if one combined a Listview with a Treeview, or if the “groups” built into the Listview control could be expanded/collapsed, right-clicked, etc.

Fig. 1 – Single Group Expanded:

Gl-Demo-1_thumb6

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Fig. 2 – Multiple Groups Expanded (Note Scrollbar on Container Control):

Gl-Demo-3-Three-Expanded_thumb2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Fig. 3 – On Widen Column (Note Scrollbar on Specific ListGroup):

Gl-Demo-4-Widen-Column_thumb3

For those who are about to point out that such a control exists in the ObjectListView, I am aware. However, I needed to do this using standard .NET Libraries. I am also aware that the standard Listview Group can be forced to expand/Collapse, but I needed this to be a faster solution. Also, causing the standard Listview Group to expand/collapse looked to rely on a whole lot of Windows API calls, and I am not so fluent in that arcane area.

My solution was to extend the Listview control, and then assemble multiple Listview controls within a FlowLayoutPanel control. The ColumnHeaders of each Listview double as the “Group.” Clicking on the left-most column toggles group expansion/collapse. The expanded/collapsed state is indicated by a solid arrow image at the left end of the column. In cases where the group is empty (containing no ListViewItems, and looking for all the world like a collapsed group) the arrow image is empty.

For the purpose of clarity, I refer to the aggregate control as a “GroupedListControl",” and each contained ListView as a “ListGroup".” There is a wide potential for improvement in this naming scheme, I am sure. For the purpose of this narrative, assume the following:

  1. A GroupedListControl Contains one or more ListGroups, which contain ListViewItems.
  2. The GroupedListControl is a container which inherits from FlowLoyoutPanel.
  3. The ListGroup is a container which inherits from the Winforms ListView.

Extending Native Listview Behaviors with Inner Classes

First, I needed to extend some of the basic behaviors of the stock .net Listview control. For example, in order to treat the leftmost column (Column [0]) differently, and to monitor the addition and removal of columns for the purpose of controlling and adjusting for the appearance of scrollbars, I needed an event to be fired when columns are added and removed. I did a little digging on the interwebs, and found my solution in a post on the Code Project site. I was able to take the core concept there and achieve what I needed:

A basic list of desired behaviors for each ListGroup include:

  1. Clicking on the leftmost ColumnHeader of a ListGroup should toggle the expansion/collapse of the group.
  2. When the first column is added, the Expanded/Collapsed indicator arrows should be added to the leftmost ColumnHeader.
  3. If the total width of the columns in any given ListGroup exceed the width of the client area of the containing GroupedListControl, the ListGroup should show a horizontal scrollbar.
  4. The height of each ListGroup should be adjusted such that all Listview items contained should be displayed in the expanded state, up to an optional maximum height determined either at design-time or runtime. If the number of ListViewItems contained exceeds this maximum, the Individual ListGroup Vertical Scrollbar will appear.
  5. Any time the Horizontal Scrollbar is displayed for a specific ListGroup, the client area for that ListGroup should be adjusted such that the Horizontal scrollbar does not partially obscure the last displayed ListViewItem.
  6. Detect Mouse Right-Clicks on specific ListView ColumnHeaders and allow for a context menu specific to right-clicking on ColumnHeaders vs. ListView Items.

This covers some minimums for the control to function properly. Let’s look at what a basic code skeleton would look like here. We will fill in some of the empty code stubs a little later in the post.

Complete source code for this project is available at GroupedListControl Project Source Code On GitHub. The source includes additional code not covered here. We will discuss some of it in upcoming posts.

First, I defined some Custom Event Argument Classes which will be utilized within the control. While they are functionally similar to some existing ListView Event Argument classes, I wanted to maintain clear naming to the degree possible. These are required by subsequent code.

Note that all examples require the following references at the head of your code file:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Layout;
using System.Runtime.InteropServices;
using System.ComponentModel;

Custom Event Arguments:

    public class ListGroupColumnEventArgs : EventArgs
    {
        public ListGroupColumnEventArgs(int ColumnIndex)
        {
            this.ColumnIndex = ColumnIndex;
        }
        
        public ListGroupColumnEventArgs(int[] ColumnIndexes)
        {
            this.ColumnIndexes = ColumnIndexes;
        }

        public int ColumnIndex { get; set; }
        public int[] ColumnIndexes { get; set; }
    }



    public class ListGroupItemEventArgs : EventArgs
    {
        public ListGroupItemEventArgs(ListViewItem Item)
        {
            this.Item = Item;
        }

        public ListGroupItemEventArgs(ListViewItem[] Items)
        {
            this.Items = Items;
        }

        public ListViewItem Item { get; set; }
        public ListViewItem[] Items { get; set; }
    }
 

Now, the ListGroup Class itself. I have cut out some additional methods for the sake of brevity here (even at that, it’s a long chunk of code . . .), and left some code stubs to be filled in shortly. This is just to give an idea of the most basic class structure, and core functionality requirements. As it is right here, this code is not functional.

The ListGroup Class – Essentials and Code Stubs

    public class ListGroup : ListView
    {

        // DELEGATES AND ASSOCIATED EVENTS:

        // NOTE: The events and delegates related to Column and Item Addition/Removal are
        // called by the inner classes ListGroupColumnCollection and LIstGroupItemCollection. 
        // The proper function of the control depends upon these. 

        // Delegates to handle Column addition and removal Events:
        public delegate void ColumnAddedHandler(object sender, ListGroupColumnEventArgs e);
        public delegate void ColumnRemovedHandler(object sender, ListGroupColumnEventArgs e);

        // Events related to Column Addition and removal:
        public event ColumnAddedHandler ColumnAdded;
        public event ColumnRemovedHandler ColumnRemoved;

        // Delegates to handle Item Addition and Removal events:
        public delegate void ItemAddedHandler(object sender, ListGroupItemEventArgs e);
        public delegate void ItemRemovedHandler(object sender, ListGroupItemEventArgs e);

        // Events related to Item Addition and Removal:
        public event ItemAddedHandler ItemAdded;
        public event ItemRemovedHandler ItemRemoved;

        // Delegate and related events to process Group Expansion and Collapse:
        public delegate void GroupExpansionHandler(object sender, EventArgs e);
        public event GroupExpansionHandler GroupExpanded;
        public event GroupExpansionHandler GroupCollapsed;

        // Delegate and related Events to handle Listview Header Right Clicks:
        public delegate void ColumnRightClickHandler(object sender, ColumnClickEventArgs e);
        public event ColumnRightClickHandler ColumnRightClick;


        // PRIVATE INSTANCES OF INNER CLASSES

        // Instances of our inner classes, declared as private members so 
        // that our public Property accessors can be Read-only, yet allow 
        // direct manipulation from within the control instance
        private ListGroupItemCollection _Items;
        private ListGroupColumnCollection _Columns;


        /// <summary>
        /// Constructor Stub
        /// </summary>
        public ListGroup() : base()
        {
            // Implementation Code . . .
        }


        // INNER CLASS INSTANCE ACCESSORS:

        /// <summary>
        /// Hides the ListViewItemCollection internal to the base class, 
        /// and uses the new implementation defined as an inner class, 
        /// which sources an "ItemAdded" Event:
        /// </summary>
        public new ListGroupItemCollection Items
        {
            get { return _Items; }
        }


        /// <summary>
        /// Hides the ColumnHeaderCollection internal to the base class, 
        /// and uses the new implementation defined as an inner class, 
        /// which sources a "ColumnAdded" Event:
        /// </summary>
        public new ListGroupColumnCollection Columns
        {
            get { return _Columns; }
        }


        // INNER CLASS DEFINITIONS:

        /// <summary>
        /// Inner class used to hide the ListViewColumnHeaderCollection 
        /// built in to the ListView Control and provide required extended behaviors.
        /// </summary>
        public class ListGroupColumnCollection : ListView.ColumnHeaderCollection
        {
            // Implementation Code Here . . .
        }


        /// <summary>
        /// Inner class defined for ListGroup to contain List items. 
        /// Derived from ListViewItemCollection and modified to source events 
        /// indicating item addition and removal. 
        /// </summary>
        public class ListGroupItemCollection : ListView.ListViewItemCollection
        {
            // Implementation Code Here . . .
        }


        // ITEM ADDITION AND REMOVAL:


        /// <summary>
        /// Raises the ItemAdded Event when a new item is
        /// added to the items collection.
        /// </summary>
        private void OnItemAdded(ListViewItem Item)
        {
            // Code to set the size of the control to display all the items 
            // so far . . .

            // Raise the ItemAddded event to any subscribers:
            if (ItemAdded != null)
                this.ItemAdded(this, new ListGroupItemEventArgs(Item));
        }


        /// <summary>
        /// Raises the ItemRemoved Event when an item is
        /// removed from the items collection.
        /// </summary>
        private void OnItemRemoved(ListViewItem Item)
        {
            // Code to set the size of the control to display all the items 
            // remaining after the current one is removed . . .

            // Raise the ItemRemoved event to any subscribers:
            if (ItemRemoved != null)
                this.ItemRemoved(this, new ListGroupItemEventArgs(Item));
        }


        // COLUMN ADDITION AND REMOVAL:

        /// <summary>
        /// Raises the ColumnAdded Event when a new column is
        /// added to the ColumnHeaders collection.
        /// </summary>
        private void OnColumnAdded(int ColumnIndex)
        {
            // Code to manage column additions. The first column added
            // needs to have the Expand/Collapse/Empty image added . . .

            // Raise the ColumnAdded event to any subscribers:
            if (this.ColumnAdded != null)
                this.ColumnAdded(this, new ListGroupColumnEventArgs(ColumnIndex));
        }


        /// <summary>
        /// Raises the ColumnRemoved Event when a column is
        /// remmoved from the ColumnHeaders collection.
        /// </summary>
        private void OnColumnRemoved(int ColumnIndex)
        {
            // Code to manage column removals . . . 

            // Raise the ColumnRemoved event to any subscribers:
            if (this.ColumnRemoved != null)
                this.ColumnRemoved(this, new ListGroupColumnEventArgs(ColumnIndex));
        }


        // USER ACTIONS:

        /// <summary>
        /// Handles the ListGroup ColumnClick Event sourced by the base.
        /// </summary>
        void ListGroup_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            // Code to manage ColumnHeader Clicks. If the Event is sourced from 
            // the first Column (Column[0], toggle expansion/collapse of the list . . .
        }


        // CONTROL BEHAVIORS:

        /// <summary>
        /// Causes the list of items to expand, showing all items in the 
        /// Items collection.
        /// </summary>
        public void Expand()
        {
            // Do stuff to make the control expand . . .

            // Raise the Expanded event to notify client code that the ListGroup has expanded:
            if (this.GroupExpanded != null)
                this.GroupExpanded(this, new EventArgs());
        }


        /// <summary>
        /// Causes the Displayed list of items to collapse, hiding all items and 
        /// displaying only the columnheaders. 
        /// </summary>
        public void Collapse()
        {
            // Do stuff to make the control collapse

            // Raise the Collapsed event to notify client code that the ListGroup has expanded:
            if (this.GroupCollapsed != null)
                this.GroupCollapsed(this, new EventArgs());
        }
    }
 

Now, pay close attention to the code stubs where we define our Inner Classes, ListGroupColumnCollection and ListGroupItemCollection. Note that each derives from its respective counterpart in the Winforms ListView Control. This is where we achieve a number of our custom event sourcing behaviors. Once again, I have simplified the class definitions here, leaving out various overloads of the core methods required (for example, there are multiple ways to “Add” an item to either collection – here I only cover the most basic. The rest are defined in the GroupedListControl Project Source Code, obtainable from my GitHub Repo).

Notice how the code within each inner class causes events to be raised within the containing ListGroup class? This provides our event sourcing for the addition/removal of Columns and ListViewItems, since these events are not defined in the base Winforms ListView Class. We need them in order to affect proper control expansion and collapse in response to additions and removals

NOTE: Core Concepts for the use of Inner Classes in this manner was adapted from THIS ARTICLE by Simon Segal on Code Project.

The following code replaces the code stub for the ListGroupColumnCollection Class in our ListGroup class definition:

The ListGroupColumnCollection Class

    /// <summary>
    /// Inner class defined for ListGroup to contain ColumnHeaders. 
    /// Derived from ListView.ColumnHeaderCollection and modified to 
    /// source events indicating column addition and removal. 
    /// </summary>
    public class ListGroupColumnCollection : ListView.ColumnHeaderCollection
    {
        // Reference to the containing ListGroup Control
        private ListGroup _Owner;

        public ListGroupColumnCollection(ListGroup Owner) : base(Owner)
        {
            _Owner = Owner;
        }


        /// <summary>
        /// Gets the total width of all columns currently defined in the control.
        /// </summary>
        public int TotalColumnWidths
        {
            get
            {
                int totalColumnWidths = 0;
                foreach(ColumnHeader clm in this)
                    totalColumnWidths = totalColumnWidths + clm.Width;
                return totalColumnWidths;
            }
        }


        /// <summary>
        /// Adds a column to the current collection and raises 
        /// the OnColumnAddedEvent on the parent control.
        /// </summary>
        public new ColumnHeader Add(string text, int width, HorizontalAlignment textAlign)
        {
            ColumnHeader clm = base.Add(text, width, textAlign);
            _Owner.OnColumnAdded(clm.Index);
            return clm;
        }


        /// <summary>
        /// Removes a column from the current collection and 
        /// raises the OnColumnRemoved Event on the parent control.
        /// </summary>
        public new void Remove(ColumnHeader column)
        {
            int index = column.Index;
            base.Remove(column);
            _Owner.OnColumnRemoved(index);
        }


        public new void Clear()
        {
            base.Clear();
        }

    } // ListGroupColumnCollection
 

The following code replaces the empty stub for the ListGroupItemCollection class in our original ListGroup class definition:

The ListGroupItemCollection Class

    /// <summary>
    /// Inner class defined for ListGroup to contain List items. Derived from ListViewItemCollection
    /// and modified to source events indicating item addition and removal. 
    /// </summary>
    public class ListGroupItemCollection : System.Windows.Forms.ListView.ListViewItemCollection
    {
        private ListGroup _Owner;
        public ListGroupItemCollection(ListGroup Owner) : base(Owner)
        {
            _Owner = Owner;
        }


        /// <summary>
        /// New implementation of Add method hides Add method defined on base class
        /// and causes an event to be sourced informing the parent about item additions.
        /// </summary>
        public new ListViewItem Add(string text)
        {
            ListViewItem item = base.Add(text);
            _Owner.OnItemAdded(item);
            return item;
        }


        /// <summary>
        /// New implementation of Remove method hides Remove method defined on base class
        /// and causes an event to be sourced informing the parent about item Removals.
        /// </summary>
        public new void Remove(ListViewItem Item)
        {
            base.Remove(Item);
            _Owner.OnItemRemoved(Item);
        }

    } // ListGroupItemCollection
 

Filling it all in

The most basic extension of the existing capability of the standard ListView control we are seeking is the ability for each ListGroup to “expand” and/or “collapse” in response to certain user inputs. We will want to define a singular method call which causes the desired action to be performed, so we’ll add a method to our ListGroup class named SetControlHeight. This method evaluates the current state of the control (collapsed or expanded), and calls the appropriate method to toggle that state to the opposite.

The minimum collapsed height of each ListGroup control should be just enough to display the Column Headers. The Expanded height may be unlimited, or may be constrained by setting the MaximumHeight property. In either case, however, the control height should include enough space to display the Column Headers, and an even number of ListViewItems such that the last item is fully visible in the display.

The collapsed state is recognizable if the height of the control is equal to the height of the Column Headers and the item count is greater than 0. Otherwise, the control must be in an expanded state. I had to arbitrarily set the header height as a constant which matches the default header height for the standard ListView Control (25). However, this can be modified to suit.

In order to accomplish all of the above, we will need to define a few more private members in our ListGroup Class. Add the following code in the declaration area of your class (I am still a little old-school, in that I place most of my declarations at the top of the class, just after the class declaration itself). In the Source file, the following appear just before the Constructor:

Additional Member Declarations:

        // Text strings used as Image keys for the expanded/Collapsed image in the 
        // left-most columnHeader:
        static string COLLAPSED_IMAGE_KEY = "CollapsedImage";
        static string EXPANDED_IMAGE_KEY = "ExpandedImageKey";
        static string EMPTY_IMAGE_KEY = "EmptyImageKey";

        // "Magic number" approximates the height of the List View Column Header:
        static int HEADER_HEIGHT = 25;

We also need a Constructor at this point. Note that the Constructor initializes the ListView.SmallImageList with some images stored in the project resources (Properties.Resources). The images are included with the . Replace the Constructor code stub with the following:

The Constructor:

    public ListGroup() : base()
    {
        this.Columns = new ListGroupColumnCollection(this);
        this.Items = new ListGroupItemCollection(this);

        // The Imagelist is used to hold images for the expanded and contracted icons in the
        // Left-most columnheader:
        this.SmallImageList = new ImageList();

        // The tilting arrow images are available in the app resources:
        this.SmallImageList.Images.Add
            (COLLAPSED_IMAGE_KEY, Properties.Resources.CollapsedGroupSmall_png_1616);
        this.SmallImageList.Images.Add
            (EXPANDED_IMAGE_KEY, Properties.Resources.ExpandedGroupSmall_png_1616);
        this.SmallImageList.Images.Add
            (EMPTY_IMAGE_KEY, Properties.Resources.EmptyGroupSmall_png_1616);

        // Default configuration (for this sample. Obviously, configure to fit your needs:
        this.View = System.Windows.Forms.View.Details;
        this.FullRowSelect = true;
        this.GridLines = true;
        this.LabelEdit = false;
        this.Margin = new Padding(0);
        this.SetAutoSizeMode(AutoSizeMode.GrowAndShrink);
        this.MaximumSize = new System.Drawing.Size(1000, 2000);
            
        // Subscribe to local Events:
        this.ColumnClick += new ColumnClickEventHandler(ListGroup_ColumnClick);
        this.ItemAdded += new ItemAddedHandler(ListGroup_ItemAdded);
    }

Now we can add code to manage the re-sizing of the control in response to user actions. While we have existing stubs for the Expand() and Collapse() methods because these formed obvious behaviors for our control, the next two will have to be added. The SetControlHeight() method is our one-stop call to adjust the height of the control:

The SetControlHeight Method:

        /// <summary>
        /// Adjusts the item display area of the control in response to changes in the 
        /// expanded or collapsed state of the control. 
        /// </summary>
        public void SetControlHeight()
        {
            if (this.Height == HEADER_HEIGHT && this.Items.Count != 0)
                this.Expand();
            else
                this.Collapse();
        }

Add the above, along with the next three methods to our ListGroup Class. The next three methods actually perform the heavy lifting in terms of adjusting the control expanded/collapsed state. The first is a function which returns the proper control height after evaluating several factors (explained in the comments). We don’t have a stub for this in our existing structure, so add it right under the SetControlHeight method:

PreferredControlHeight Function:

    private int PreferredControlHeight()
    {
        int output = HEADER_HEIGHT;
        int rowHeight = 0;

        // determine the height of an individual list item:
        if(this.Items.Count > 0)
            rowHeight = this.Items[0].Bounds.Height;

        // In case the horizontal scrollbar makes an appearance, we will
        // need to modify the height of the expanded list so that it does not
        // obscure the last item (default is 10 px to leave a little space 
        // no matter what):
        int horizScrollBarOffset = 10;

        // if the Width of the columns is greater than the width of the control, 
        // the vertical scroll bar will be shown. Increase that offset height by the 
        // height of the scrollbar (approximately the same as the height of a row):
        if (this.Columns.TotalColumnWidths > this.Width)
            horizScrollBarOffset = rowHeight + 10;

        // Increase the height of the control to accomodate the Columnheader, 
        // all of the current items, and the value of the 
        // horizontal scroll bar (if present):
        output = HEADER_HEIGHT + (this.Items.Count) * rowHeight 
        + horizScrollBarOffset + this.Groups.Count * HEADER_HEIGHT;

        return output;
    }

Then replace the Expand() and Collapse() code stubs with the following. While the PreferredControlHeight function provides the optimal height for a ListGroup, the Expand and Collapse methods perform the requested action and also cause the Expanded/Collapsed/Empty images to display properly in the left-most column:

The Expand and Collapse Methods:

    /// <summary>
    /// Causes the list of items to expand, showing all items in the 
    /// Items collection.
    /// </summary>
    public void Expand()
    {
        if (this.Columns.Count > 0)
        {
            this.Height = this.PreferredControlHeight();

            if (this.Items.Count > 0)
                // Set the image in the first column to indicate an expanded state:
                this.Columns[0].ImageKey = EXPANDED_IMAGE_KEY;
            else
                // Set the image in the first column to indicate an empty state:
                this.Columns[0].ImageKey = EMPTY_IMAGE_KEY;

            this.Scrollable = true;

            // Raise the Expanded event to notify client code 
            // that the ListGroup has expanded:
            if (this.GroupExpanded != null)
                this.GroupExpanded(this, new EventArgs());
        }
    }


    /// <summary>
    /// Causes the Displayed list of items to collapse, hiding all items and 
    /// displaying only the columnheaders. 
    /// </summary>
    public void Collapse()
    {
        if (this.Columns.Count > 0)
        {
            this.Scrollable = false;

            // Collapse the ListGroup to show only the header:
            this.Height = HEADER_HEIGHT;

            if (this.Items.Count > 0)
                // Set the image in the first column to indicate a collapsed state:
                this.Columns[0].ImageKey = COLLAPSED_IMAGE_KEY;
            else
                // Set the image in the first column to indicate an empty state:
                this.Columns[0].ImageKey = EMPTY_IMAGE_KEY;

            // Raise the Collapsed event to notify client code that 
            // the ListGroup has expanded:
            if (this.GroupCollapsed != null)
                this.GroupCollapsed(this, new EventArgs());
        }
    }

Wiring it all up

Now we need to wire up behaviors (Expand/Collapse) to the appropriate events. Some of these are obvious. When the user clicks on the left-most column (with the Expanded/Collapsed state image), the control should toggle this state. However, we also need the control to adjust its displayed area (and possible toggle the state image) when items are added/removed, and when columns are added/removed (because when columns are added, there may be a need to add the state image to the first column).

First, we will address Column addition/removal. When a column is added to the control, if it is the FIRST column, it will need the initial state image added, and the control will need to size itself. Since it is the first column, it is reasonably safe (but not 100%) to assume that there have been no items added yet, so the ListGroup is empty, and the state image should reflect this.

Add the highlighted items to the OnColumnAdded() Method:

    private void OnColumnAdded(int ColumnIndex)
    {
        if (ColumnIndex == 0)
        {
            this.Columns[0].ImageKey = EMPTY_IMAGE_KEY;
            this.SetControlHeight();
        }

        if(this.ColumnAdded != null)
            this.ColumnAdded(this, new ListGroupColumnEventArgs(ColumnIndex));
    }

What happens if the ListGroup is populated with items, and the last column is removed? I don’t have a good answer for this, other than to clear the ListItems. It seems to me that the control loses its identity and purpose. If you have thoughts about this, please do discuss in the comments, or fork the code on Github. In any case, when removing columns, we need to test and see if the column removed is the last column in the control. If so, call the Clear() Method:

    private void OnColumnRemoved(int ColumnIndex)
    {
        if (this.Columns.Count == 0)
        {
            this.Items.Clear();
        }

        // Raise the ColumnRemoved event to any subscribers:
        if (this.ColumnRemoved != null)
            this.ColumnRemoved(this, new ListGroupColumnEventArgs(ColumnIndex));
    }

Now, the final piece in our simplified ListGroup is the whole thing where the user clicks on the left-most column (Column[0]), and is rewarded by the control expanding or collapsing. The state image provides a sort of visual cue/affordance to the user which implies the current state. All we have to do is handle the ColumnClick event (sourced by the base class ListView) and we’re done with this part of our (abbreviated) control

Replace the ListGroup_ColumnClick code stub with the following:

Handling the ColumnClick Event:

    void ListGroup_ColumnClick(object sender, ColumnClickEventArgs e)
    {
        int columnClicked = e.Column;
            
        // The first column (Column[0]) is what activates the expansion/collapse of the 
        // List view item group:
        if (columnClicked == 0)
        {
            this.SuspendLayout();
            this.SetControlHeight();
            this.ResumeLayout();
        }
    }

Summing Up Part I

Sadly, I need to break this project up into two parts. Even this one is too long (although much of the length is simply code samples.

In this first post, we have examined extending the Winforms ListView class so that it can serve as a component within a container control. We have also examined extending the events sourced by the ListView control through the use of inner classes, used to extend the ListViewColumnCollection and ListViewItemCollection classes.

In the next post, we will fold our ListGroup class into the container GroupedListControl. IN doing so , we will need to make a few calls to the Windows API. I hate it when that happens, but so it is (I hate it because I am not well-schooled in the Win32 API, so that kind of thing is HARD for me!). After that, we will examine some special methods to source a custom Context Menu specific to right-clicks on the List Group column headers, and a few other interesting tidbits which were necessary to make the overall control work properly.

Report Bugs. Submit Improvements. Do Good. Help Me Get Better!

I will try to get the next post up is a day or two. In the meantime, if you find over bugs in the code, or see areas for improvement in the overall implementation, please report bugs (in the comments here, or on Github), and feel free to fork the source and submit pull requests for improvements. I have said many times, I need all the help I can get!

Referenced in This Article:

 

Posted on 11/09/2012 0:5:11 AM by John Atten     

Comments (0)

Over 500 Channels of Crap for just $79.99 per month

Posted on 12/18/2012 0:10:12 PM by John Atten in Free Internet, SOPA, Web   ||   Comments (0)

We’re from the government. We’re here to help.

There is a war going on with respect to the internet, but it is not the war most people think it is. We hear concerns about digital piracy from the entertainment industry, the RIAA and MPAA in particular representing the most vocal representatives. We hear rumblings from the publishing industry, as the printed word begins to decline relative to digital content. And we see ever-increasing numbers of consumers spending more and more of their income through on-line purchases.

We hear concerns about security and privacy, and the need to essentially “protect” the user from predatory companies which seek to exploit the user data for commercial gain. The current explosion in “Social Networking” and “cloud computing” has set the stage for the next big struggle. Large software companies, hardware manufacturers, and web-based social networks, creating what they call “ecosystems” and what many others call “walled gardens” represent the next battleground in the war.

Fmr. Senator Chris DoddThe RIAA and the MPAA, arriving late to the party, have finally figured out that the internet is the content delivery mechanism of the future. The entertainment industry is in the content delivery business, and for decades have controlled most aspects of how we access, choose, and pay for their goods. They are not going to sit idly by and watch the empire crumble. And because they missed the boat at the beginning, it is too late for them to grab the early foothold the needed in order to bring something fresh to the game. So they will fall back on what they know. Lawyers, lawsuits, and lobbying.

The MPAA, in the news of late due to its heavy lobbying investment (failing) to pass the SOPA and PIPA legislation, has in its CEO the perfect, iconic representation of the problem at hand. Former US Senator Chris Dodd, barely out of office, seems to land himself the cushy gig of CEO to the Motion Picture Association of America. Is this because of his imminent qualifications and experience in the motion picture industry? I think not. It is because of his political connections, and favors performed while in the senate. By now, most are familiar with Dodd’s poorly-conceived response following massive protest of the SOPA/PIPA legislation. But this is how they are trying to play it.

Remember the “Big Three?”

Now that the internet represents the obvious distribution mechanism for all levels of content, and provides smaller publishers an equal platform on which to compete, the media companies are running scared. The war is not truly about piracy. Piracy is a very real problem, for these and other industries. However, it is not going to go away, despite new legislation or enforcement efforts. The war is, at its core, for control of the internet. Media companies have controlled the radio, television, and cable airwaves for decades. The internet, representing the ultimate replacement of those delivery mechanisms, is now the primary target.

Mark my words. While piracy, copyright, and patent protections are the buzzwords of the moment in this war, it is only the beginning. Think about this. If the realm of the internet is made “safer” or “protected” for rights holders at the expense of the overall freedom, we have lost. In 10 years time, if we allow it to happen, the internet will look a whole lot more like the broadcast television spectrum of the ‘70’s and ‘80s, and the cable networks of the ‘90s. Big media will dominate, and control most of the space. There will likely be some “fringe” domains in which “public access” occurs, but the bulk of the web will be all about big media and advertising.

Your Papers, Please?

Paperwork: photo by luxomediaThe large corporate interests will accomplish this through heavy lobbying, for progressively more restrictive legislation which “protects” the interest of rights holders at the expense of our (the public) freedoms. It won’t take long before the costs associated with domain applications, the inevitable licensing requirements, and compliance will be so great that the average user will find the barriers to entry too high.

Arguments about piracy, privacy, security, and the safety of the “social web” are all very real and very valid concerns. However, in the next few years, these terms will be bandied about by large corporate interests, inside and outside the hallowed halls of legislatures, boardrooms, and smoky back rooms with the general goal of re-purposing the web infrastructure. If we, the public, allow ourselves to buy into it, the internet as a free vehicle of personal access, enlightenment, and engagement will cease to exists.

In its place will be another version of “Over 500 channels of crap for just $79.99 per month – some restrictions apply”

 

Posted on 12/18/2012 0:10:12 PM by John Atten     

Comments (0)

Dana White vs. Anonymous: SOPA/PIPA and Due Process

Posted on 21/27/2012 0:9:21 PM by John Atten in SOPA, Web, Constitution   ||   Comments (0)

Revenge of the Nerds

In the last week, there has been a most entertaining (and semi-civil) exchange on Twitter between the public face of the Activist group Anonymous and Dana White, President of the UFC (Ultimate Championship Fighting). The discussion escalated to the point of tough-guy talk from Dana White (Like, uh, what else would you expect from the President of the UFC?) and some interesting game-playing on the part of Anonymous. The "Hactivists" posted a link on twitter to a page containing what appeared to be a swath of White's personal data, including Social Security number, address, and financial data. On Friday, Anonymous attempted to break into the UFC server, and initiated targeted redirects of the UFC.com domain.

"Dana White - what do you have against the Internet? We're just curious, as we were quite surprised at the harsh tone of your comments . . . do you support what we do, in terms of activism & raising public awareness of critical security/privacy issues?"

- @YourAnonNews to Dana White,  via Twitter

Mr. White is a supporter of SOPA/PIPA, and feels that internet piracy is no different than stealing. He would know, apparently, because UFC "pay-per-view" fights are often pirated, depriving the UFC of revenue. The hackers of  Anonymous contend that SOPA/PIPA amounts to censorship and a deprivation of our basic freedoms. On which point I heartily agree.

"The only thing that we're focused on is piracy. Piracy is stealing. You walk into a store and you steal a (freaking) gold watch, it's the same as stealing a pay-per-view."

- Dana White, Thursday January 26, 2012

Been Caught Stealin'

On the point of piracy I agree with Mr.. White. To a point.

Piracy IS a big issue. Mr. White, and all those whose livelihoods are impacted by piracy are understandably pissed. And, no, it really does not matter that some of them may be really, really rich already (Mr. White is pretty well off). Stealing is still stealing, and it is wrong and illegal.

According to White: "The only thing that we're focused on is piracy. Piracy is stealing. You walk into a store and you steal a (freaking) gold watch, it's the same as stealing a pay-per-view."

And that is true.

The problem with SOPA/PIPA, and the problem with Data White's analogy of stealing from a store, is that you still need to be proven guilty when you are arrested for stealing from a store.

"I love the Internet. It helped us grow our biz. Stealing is stealing! And hacking into people's (expletive) is terrorism . . . I'm a fight promoter. I put on fights. People are stealing my shit on the net and selling it or selling ads on it."

- Dana White to Anonymous, via Twitter

SOPA/PIPA, and any other legislation which allows the government to block or takedown entire IP addresses and remove domains from the DNS system is that, under SOPA and PIPA, due process has been circumvented. SOPA/PIPA would empower the government to block sites "suspected" or "reasonably believed to be" hosting pirated content with minimal due process, and largely at the urging of large media companies and content providers. 

jolly-rogerMr. White, I feel your pain. Piracy sucks, and needs to be addressed. However, compromising the basic freedoms of the internet is not the way to do it. The unfortunate truth is that, like any other criminal enterprise, online piracy will continue in spite of any government intervention. Like any other criminal enterprise, we the people must fight within the confines of law and order, while the criminals are not so constrained.

UFC, Hollywood, and the US government already have tools to fight piracy. They are not as effective as some would like, but they exist. Hell, the best SOPA could have done would have been to block Americans from accessing pirated content hosted on foreign servers. It would have had no impact upon foreign access to pirated American content hosted on those same foreign servers.

Increased regulation, especially of the sort offered by SOPA/PIPA would most definitely impact the internet as a whole, however. Now that Mr. White and the rest of Hollywood have begun to realize the profit potential of the internet, they and their cohorts in the Congress are also suddenly interested in seizing as much control of the internet as possible.

As I related in an earlier post, there exists the very real possibility that by the end of the decade, the internet as we know it will no longer exist. After all, the same media companies which brought you the "Big Three" broadcast television networks (ABC, CBS, and NBC), and the same providers who have dominated the cable television industry have now seen the future of content delivery. And that future is the internet.

In the past decade, the internet has exploded with user-generated content. The medium is such that just about anyone has been able to create a blog, or publish a video, or create an online forum, etc. Hosting has been cheap or free, and there are few barriers to entry. We were all just fine without streaming UFC fights and Hollywood movies.

If the government and Hollywood become the heavy players in this space, you can kiss all that goodbye.

I say again - Mr. White, I feel your pain. It sucks to be stolen from, whether you are rich or poor. But let's not destroy the very thing that makes the internet great. If SOPA and PIPA had been passed into law, your pay-per-views would still be stolen. Pirates would still make new release movies available, both on the web, and on DVD. Thieves are very clever, and they don't play by the rules.

112 Channels of Crap for just $79.95 per Month . . . If you act NOW!!

I urge you all to adapt and innovate. As Apple, Netflix, and Amazon have aptly demonstrated, a user-friendly experience at the right price will win the day with handsome profits. And without forcing the customer to buy bloated programming "packages" which include two channels they don't want for every one they do. Or an entire album, at album prices, to get the two songs they want. Oh, and take some of the money you all have been shoveling into the hands of Congress through your lobbyist middle-men, and use it instead to spearhead investigations. Or to work with tech companies to find solutions which work for everyone, instead of just yourselves.

The key to maintaining your enterprise in the internet era is to jump on board, without destroying the very thing which makes the internet what it is. After all, it was a bunch of geeks and hackers who brought it this far. What right have you to claim it as yours, now?

References for this post include:

 

Posted on 21/27/2012 0:9:21 PM by John Atten     

Comments (0)

Whence Comes the Money, Hence Comes Regulation . . .

Posted on 08/18/2012 0:9:08 PM by John Atten in Constitution, Education, Web, SOPA   ||   Comments (3)

SOPA and PIPA are only the beginning . . .

We all knew it would happen. Back in the '90s, Big media came late to the internet game. For most if its history, the internet has been (rightfully) held as a free exchange of ideas and information, with very little interference from government, at least in the Democratic West.

Now, in the last decade Big Media has taken notice. With the effective monetization of the web, Corporations have now moved into "our" space. The impact can be felt in the form of new regulations proposed, on a regular basis, with the aim of restricting the freedoms of the average internet user in a manner which benefits Big Media.

Prior to the evolution of profitable business models for on-line commerce, the big boys really had little interest in "our" space, and the profiteers in the US Senate and the House of Congress were less than interested in the internet. In the last ten years, however, that has begun to change, and in the last two, things have reached a critical mass. While big business was slow to catch on for the first decade of the internet, the sleeping giant is stirring . . .

Like turning an oil tanker

Home-taping-is-killing-musicThere is a familiar pattern to this. Each time a technology shift occurs in the content delivery space, large publishers and media companies cry about the threat of piracy, and that the sky is falling. Remember how Home Taping was going to kill music ("and it's illegal")?

How about when recordable CD's became publicly available? This was going to end the music industry.

With the advent of the internet, the game changed on media publishers, first and most visibly for the music industry. Napster made possible the ability to share your music collection far and wide. More importantly, you didn't have to buy an entire album to get the one song you wanted by an artist. While the wide-scale distribution of content-for-nothing via Napster and it's subsequent imitators in reality did represent copyright infringement and piracy, it also demonstrated a new business model for the internet era. Unfortunately, the record companies' and publisher's reaction was not to recognize which way the wind was blowing and adapt. Instead, they resorted to tried and true tactics (not really) of "lawyering up", using lawsuits, threats of lawsuits, and scare tactics in an attempt to restore the status quo. Instead of seizing the moment, instead of becoming early adopters and profiting handsomely, the Recording Industry Association of America (RIAA) reacted in a protectionist fashion, to its own detriment.

Come bite the Apple . . .

As it turned out, Apple recognized the potential of the new business model, and dragged the music industry kicking and screaming into the internet content delivery market. iTunes demonstrated that the majority will pay for legal downloadable content, provided an adequate, user-friendly mechanism to do so. Millions of paid downloads later, both Apple and subsequently Amazon have demonstrated that on-line delivery of paid-for digital music is not only viable, but highly profitable.

By the end of the first decade in the new millennium, delivery of high-definition streaming video has become not just a reality, but a fact of life. From humble beginnings in 2001, Netflix has amassed nearly 25 million subscribers, with an average growth of 2.4 million new subscribers per quarter through most of 2010 and 2011. While the company has since lost subscribers due to a series of marketing blunders, the demand for streaming video content delivery, and the willingness of the masses to pay for it are clear. At 25 million subscribers, Netflix currently has more subscribers than any single cable service provider including Comcast.

Think the internet is going to be the place where your video content comes from over the next decade?

Reddit?Kindle Pic

Print media is lagging behind audio and video in achieving a profitable digital presence. But the decline in readership of "old-fashioned" paper magazines and newspapers, combined with the rise of digital readers such as the Kindle, Nook, and of course, the iPad show which way the wind is blowing here as well. In May of 2009, eMarketer reported that "nearly 20 million eReaders were expected to be in consumer hands" by the end of 2011, and that 12% of adults will have an eReader of some type or another. And of course, who can forget that on December 26th 2009, Amazon announced that for the first time, sales of Kindle books exceeded that of physical books. 

Bring on the clowns . . .

Now, as the U.S. Congress attempts to pass some extraordinarily bad legislation in the form of SOPA ("Stop Online Piracy Act") and the US Senate does the same with PIPA  ("ProtectIP" Act), we stand at a precipice. For the first time, American legislators are actively considering passing into law an Act or Acts which will radically change the internet as we know it forever. Either of these proposed pieces of legislation represent the first step in a process by official-photo-of-110thwhich Corporate America will attempt to seize control of the internet in much the same way it did the radio broadcast market, and subsequently, the broadcast television spectrum.

As things sit right now, at 10:42 PM Central Time, a day of "protest" by major internet sites such as Wikipedia, Reddit, and others appears to have rattled the cage of our fearless leaders in D.C. Support for SOPA and PIPA has been evaporating throughout the day.

Don't worry. They'll be back.

Plus ça change, plus c'est la même chose (The more things change, the more they stay the same) . . .

With billions of dollars on the line, and the evaporation of historical distribution mechanisms for print, music, and visual media, the large powerhouses of the entertainment industry are not going to sit idly by while the free-as-in-beer internet replaces the money machine. SOPA and PIPA are likely only the opening salvos in a war by which the media companies do their ham-fisted worst to seize control of the internet, to the exclusion of the layman.

Can you imagine a day in which you might need to apply for a license to host a website? Or a day in which the dominance of a small group of mega-corporations on the web infrastructure is so great that there are no avenues for entry at the ground level?

Media markets for sale - contact your state representative for more information!

This may seem like paranoid thinking, when we examine the web as it is today. But take a look back. The internet is rapidly supplanting a multitude of industries which remain, to varying degrees, entrenched in an antiquated business model predicated on dominance through regulation. Television. Telecom. Audio recording, publishing and copyright. Print media. The players in these industries are not going to go quietly into the night. Instead they will, in the coming years, bring the full war chest to the table, starting with our elected officials (you know, the path of least resistance, right?).

It is our hope that the White House and the Congress will call on those who intend to stage this “blackout” to stop the hyperbole and PR stunts and engage in meaningful efforts to combat piracy.”

- (Former) Senator Chris Dodd, CEO of the Motion Picture Association of America, in response to planned protest of SOPA/PIPA

Imagine that! A former US Senator, now heading one of the largest lobbying groups in the country, is accusing the tech industry of engaging in "PR stunts" and hyperbole. I can only chuckle at the irony.

The thinking of big industry, and its attitude towards the rest of the internet community, could not be more effectively demonstrated than by this release (former) Senator Dodd from January 17th, the day before the "blackouts":

Chris Dodd Statement on "Blackout Day Protests"

If the connected community that is the world-wide-web does not succeed in preventing government encroachment and regulation, we face the very real threat of an internet which more closely resembles the "big three" of radio and television networks than the free information super-highway envisioned by its creators. SOPA and PIPA are bad, and they will likely die in the chambers of the Congress and the Senate. But SOPA and PIPA are only the beginning.

Relevant Resources

Congressional Support for SOPA/PIPA - A chart by Pro Publica showing who supports and opposes the measures. Interestingly, in the wake of today's (January 18th, 2012) internet "blackout" by a number of major internet sites (including Wikipedia), the balance on this chart has tipped dramatically since last night as legislators flee a "sinking ship".

SOPA is a red herring by Adam Curry - Interesting commentary makes the case that SOPA and PIPA are small-potatoes compared to changes being made to the Domain Naming system itself, and that the privacy aspect of the internet will be compromised forever.

Senate Copyright Bill Loses Key Supporters - Forbes Magazine, following the rush of former supporters (and several original co-sponsors) of the SOPA/PIPA legislation as they backpedal in an early election-season rush to please voters.

 

Posted on 08/18/2012 0:9:08 PM by John Atten     

Comments (3)

About the author

My name is John Atten, and my username on many of my online accounts is xivSolutions. I am Fascinated by all things technology and software development. I work mostly with C#, Java, SQL Server 2008 R2, learning ASP.NET MVC. I am always looking for new information, and value your feedback (especially where I got something wrong!).

Web Hosting by