Blog Code Snippets License Examples

Here is an example of a piece of code posted to a blog which doesn’t explicitly have a license attached. It is a pretty cool example and something that would be nice to use in a basic winforms project.

Now that piece of code doesn’t have a license, but the blog itself does have a Disclaimer in the menu saying “Postings are provided ‘as is’ with no warranties and confer no rights.” with a link to http://www.microsoft.com/mscorp/legal/policy/online_disclaimer.asp 

This is definitely better than nothing.

Even better I think is this post by Jon Gallant on TypeConverter. At the end of the post he explicitly says:

Use of included code sample is subject to the terms specified at http://www.microsoft.com/info/cpyright.htm

This posting is provided “AS IS” with no warranties, and confers no rights.

In other words, you have no rights to what you just saw. You cannot copy and paste into your code. You cannot pass Go or collect $200.

Thankfully Marcos of filehelpers fame corrected me on what I previously wrote about CodeProject. Code at CodeProject explicitly gives others developers permission to use the sourcecode. There isn’t an explicit license attached, but I’d say it is closest to BSD. It just says “keep the original copyright notices”.

The bad example is http://en.csharp-online.net/Design-Time_Integration%E2%80%94Type_Converters. I found this URL when I googled for TypeConverter.

I poked around csharp-online.net and I couldn’t find a license or copyright notice for the site.

Remember to check the copyright on that class that you copy from some guys website.

A Line In The Sand Has Always Existed

Colin Ramsay says to Abandon ALT.NET!

A lot of people in the ALT.NET camp may not feel that they’re being divisive but I can tell you that by propagating this idea you’re creating a line in the sand, with the highly-knowledgable on one side and the masses on the other. You’re not encouraging dissemination of your information, you’re just creating another impenetrable gang of developers that is almost opaque to the guy on the ground.

I’m half laughing because of the absurdity of it. I’m half crying because so many people just don’t get it.

The line in the sand that Colin suggests is newly created has always existed. It is approximately the same line between those who “get it” and those who don’t. Every programmer knows within a short period of time of meeting another programmer if they would like to work with that person. These are the guys who “get it”. When you know you don’t want to work with another programmer, they can fall into the “don’t get it” category. But this is certainly not absolute. I don’t mean to say those that don’t get ALT.NET are people I would not want to work with. On the contrary I’ve been reading Colin’s blog for a while, and I think I’d definitely enjoy working with him.

I think anther example about this line already existing can be seen in the 7+ year old essay by Joel Spolsky, “The Guerrilla Guide to Interviewing“. In it, Joel separates programmers into two camps those you would hire and those you would not hire. These no hires, according to Joel, are anyone who is not smart and who doesn’t get things done.

In my opinion, anyone who is both smart and gets things done will automatically grok the ALT.NET tenets. These tenets, which definitely vary from individual to individual, includes many agile principles such as DRY and DRT. Doing the right thing is important. Most of what is going on in ALT.NET is a response to inability to DRT using existing tools. Its programmers doing what they do best: being lazy. Lazy in that they are making things better for themselves by making it easy to do the right thing.

The problem is that these unknowledgeable masses (myself included) whom Colin defends are, by definition of the lack of knowledge, unable to to create these tools and make things better for themselves. I’d like to think that some of us can grok this ALT.NET thing. But I know that some of us trying to grok it, aren’t currently capable of joining the party as a tool maker.

I’m not currently a tool maker because I can’t take things like Windsor and look at EntLib’s Policy Injection Block and even know where to begin to build something similar. Others can not just know what to do, but actually do it in less than an hour.

The nice thing about ALT.NET is that I shouldn’t have to be able to be a tool maker. I should be able to to just use the tools of others. I should be able to stand on the shoulders of these giants. I should… except that I’m currently on a contract in which ONLY naked CLR is allowed. No EntLib on which to build. No Spring.NET or Windsor to help me with decoupling for testing. No NUnit or MbUnit to help me test. At least I have MSTest. The SCARY thing is that these are some of the things which define Colin’s line. I’m sure I’m not alone in my peril. Consider this a heartfelt giant hug to all those who are forced to develop in such a vacuum.

As for Colin’s line? I think I’ll stand with one foot on one side and another foot on the other side and I’ll straddle it until it ceases to exist.

MS-RL Considered Harmful

Oren and Frans know what is up. Its nice to see other thoughts and reasons for the same things I am worried about. Others suggest not worrying so much.

While I don’t think things are as bad as the comic on Phil’s page suggests, I am on the side of not looking into Medusa’s face. I need The Powerpuff Girls to come and save me from Sedusa. Frans has a suggestion that could save me! It basically references the Java Research License’s Residual Rights clause which says that one cannot be tainted.

I still don’t see the big deal. I keep links to Mono’s System.Windows.Forms implementation and the rest of Mono’s .NET library implementation on hand as references. For one, there are no (or very few) P/Invokes! This makes implementing something myself far easier than much of Microsoft’s System.Windows.Forms implementation. At least easier than the small bits I’ve seen in Reflector.

As for bugs, well, since you can’t actually DO anything about the underlying Microsoft bug implementation, I’m not sure what the big deal is. Heck, Microsoft doesn’t so anything about serious bugs. Workarounds are great, but sometimes bugs can bite you in the ass and cause production downtime. When its known and unfixed bugs that do this, I get angry and wonder why I’m not running on open source to begin with.

Boo at MichiPug

I presented Boo at Michipug last night. That is the Michigan Python Users Group. Boo was interesting to present there because, well, boo is not python. Yes, boo has some language features similar to python, but for a python programmer I think boo is different enough that there is still a steep learning curve. Plus the absence of all of the python standard library in favor of the .NET libraries makes for an very different programming experience.

I don’t know boo deep enough to be able to focus on its extensibility, and I don’t think that those features would be well received by a group of pythonistas. I focused on the basics and instead we talked about python and .net differences and what they meant when you are writing boo.

Assuming the date and time are translated from Israel to EDT, then right around the time I was finishing my boo presentation, Oren wrote a blog post on boo.

… I keep getting more and more and more amazed by the power that Boo is giving me.

In order to fix some of those bugs, I had to literally change the meaning of the if statement. (I defined my own nullable propagator, which I had to implement deeply into the language).

Damn, I love this language.

Oh, and FYI, if 2+2 == 4 will not do the expected thing anymore 🙂 

I’m not there yet with boo, but I will be… I will be.

 

I did enjoy pointing out IQuackFu and how easy it is to have method missing. I have a deep love hate relationship with method missing. On one hand, if you are relying on method missing, then you are probably doing something wrong. On the other hand, it sure is nice to have it if you need it. Who needs IronRuby or IronPython? I’d much rather write boo. That said I think those Iron projects are really sweet and I hope they succeed greatly. I long for the day that I can run a TurboGears app on IIS/ASP.NET or Apache/Mono.

Microsoft’s Restrictive License and Viewable Base Class Libraries

I’m very worried about the announcement yesterday by Microsoft that the source to some of the .NET Base Class Libraries will be available to developers. Mostly I am worried about the impact to the Mono project and how many more developers will now be tainted by viewing the MS code. Miguel has some thoughts on that here. Also, I am worried about the viewable code, strictly from a copyright point of view.

Drawing Parallels

Everything regarding the viewable code is subject to the exact same copyright as another expressive media which is far more well known, Music. I use the word viewable because the code is neither open source or free software. Imagine that one of the “big four”, Sony, BMG, EMI, or Universal, announced that they were going to make all of their most popular music available for free listening.

The company still retains all of the copyrights. The tools to make derivative music is still up to the new artist to obtain and use. The music industry is just encouraging every musician to listen to all of this popular music… but don’t use it!

What is fair use? Remember Vanilla Ice and his uncredited David Bowie sample? He got sued and lost due to copyright violation, for a 5 second drum loop! That isn’t fair use!

So effectively we have history to tell us that if we copy and paste or republish in any way 5 lines of code from this Microsoft source code release, we are violating copyright on the code! This scares me!

I read a lot of software blogs. Check out my bloglines blogroll. I see a lot of source code in blog postings. With this Microsoft viewable code release I dread all of the upcoming posts of copy and pasted code saying “look how Microsoft did it, we can just change this one thing for our implementation”. These will all be copyright violations. These hypothetical posts will violate Microsoft’s copyright on this viewable code and it will be up to Microsoft as the copyright holder to enforce their copyright. IANAL, but I have heard that bad things can happen to copyright holders if it can be shown that they did not attempt to enforce their copyrights.

After AACS last night, some friends and I were sitting around ABC and discussing this. Some people mentioned DMCA and others mentioned patents. These are important issues, but this is not what I’m talking about. I’m talking about good old copyright. See Lawrence Lessig‘s Free Culture for a good background.

Here is an example:

After Scott Guthrie’s original announcement, which included a screenshot of Visual Studio in the debugger stepping into some BCL code, Chris Sells deep linked Scott’s screenshot. Now Chris works for Microsoft too, so maybe he had permission, but maybe he didn’t.

My worry is what happens if I do this?

Take three cases.

  1. I could link to just Scott’s screenshot with the copyright code. Am I now violating Microsoft’s copyright? No you say? Thousands of websites were shut down in years past for linking to mp3s on different servers. How is this different?
  2. I could actually display the screenshot inline using html img tags, but referencing Scott’s server. 
  3. I could transpose the code in the screenshot, thus creating a copy and definitely and blatantly violating the copyright.

I’m actually too afraid to do #3. I think I might be able to get away with #1 and maybe #2.

What once was left to lawyers and the intellectual property experts at your corporation is now something that every developer in an organization has to think about. Every developer needs to be aware of what the license and copyright is on any piece of code they view that is not owned by them or their organization.

Of course one solution to this issue might be to only work with open source software. I’m tempted to do just that, but I find that there are far more jobs available which still use proprietary software development environments, platforms and tools. I’m OK with that. I’m willing to be aware of intellectual property issues in order to work on these platforms. In my experience, other developers are hardly aware of these issues. Unlicensed and therefore entirely copyrighted code is copied out of webpages and blogs and directly into production software in both large enterprises and small businesses. With the release of viewable BCL from Microsoft, these practices become greatly more dangerous.

Copying from the web is a copyright violation

It is all over the .NET world. Today, scottgu announced that Microsoft would be making their base class library implementation source available for free (not open source or free as in freedom or free software) to developers to look at. Details are at scottgu’s blog.

It is a damn shame about the terms of the Microsoft Reference license. This means that if I want to use a bit of the source, that I cannot. For example, I want to customize DataGridViewTextBoxCell’s Paint implementation, I cannot copy the bits of the Paint implementation of DataGridViewTextBoxCell implementation and base classes to tweak my changes. I CAN read what they did, and use this new knowledge in my implementation, but any copy/paste is not allowed! Most programmers I talk to do not understand this. This is going to be a copyright nightmare for most development organizations.

We talked a bit about copyrights and open source at the SRT Solutions open house a couple of weeks ago. I was amazed when all of the programmers I talked to said “huh?” or “really?” when I said that using code they found on the Internet or a blog is a copyright violation unless that code has an express license released with it. This means much of the code at codeproject.com is not available for reuse, only for reading. This means code posted to most blogs. Of course I’m talking about the United States of America, which is where I, and all these programmers, live and work.

Programmers: Look for a license. If there isn’t one, contact the author. Ask them to release the code they put on their blog under the public domain, or creative commons license, or a permissive (BSD, MIT/X) open source license.

If you got the code from a website which specializes in programming examples, such as codeproject, contact that author. Ask them what the license is.

If you are using sample code from a vendor, make sure you are clear on what the license is. Remember, if it is not explicitly stated, then you have ZERO right to reuse that copyright code. I’ve run into to Microsoft sample code releases like this.

Luckily there are places on the internet that ALWAYS attach a license to code. Sourceforge and CodePlex are wonderful for this.

Finally, if you are a copy and paste programmer, you may want to turn off this new Visual Studio 2008 source code feature.

GO FALLEN ROGUE!

http://feeds.feedburner.com/~r/fallenrogue/~3/162666308/222-Open-Letter-to-the-people-who-present-for-Microsoft-Regional-Events

Sweet Lord he is dead on!

…it’s time for Microsoft to stop lowering the bar for developers. It’s time to stop going out on the road and saying you’ve got a presentation for software architects and then spend 2 hours describing features to your most expensive technologies. We’re architects. Price of admission should be: not an idiot or not person who doesn’t know what this thing the “internet” is. Look, I’ve had an assload of your f-ing terrible presentations and demos. Show me something, anything at all worth looking at and maybe I’ll be able to not hang my head in shame every time the term Microsoft comes up.

I hung me head in shame only a few times yesterday at Ohio Linux Fest. That is mostly because every time I said I was a .NET Developer I could at least say that I try to stay on top of Mono and run at least some of the things I write in Mono. Luckily, there weren’t too many developers at OLF. When the Ruby guy, Luke, author of Puppet, asked me “Why would anyone develop in Mono?” I was able to say “why wouldn’t you?” And of course it is easy to site the low memory space usage of a GTK application in Mono vs. Python GTK or Ruby GTK.

Quote of the Day

from http://feeds.feedburner.com/~r/AyendeRahien/~3/160655144/JAOO–Day-I.aspx

I like the ideas being evolved in Ruby, but I have reservation about doing development there. I know that I say this as a person who didn’t do any real project there, but I have the feeling that Ruby isn’t for me. Basically doesn’t provide a significant advantage over what I already have in .Net

Wow. I have to say I agree, but I couldn’t spell it out the same as Oren does. I don’t think my reasons are exactly the same, since I deal very little with profiling, debugging, deployment and monitoring.

With all the talk about Ruby recently in the .NET community, I find this quote to be refreshing. Some people are suggesting that we just jump ship and join the Ruby camp like many Java people did over the past few years. Jeremy Miller write a little bit about it recently in some thoughts about ALT.NET.

More than one person has questioned whether or not the ALT.NET canon and crowd is inevitably destined for Ruby and Ruby on Rails.  There’s some thought, and at this point observation, that the alpha geeks in .Net are starting to drift into Ruby development instead.

Right now, for me, the major frustration is what Jeremy calls “MSDN way”. Bad (non-alt.net) development is done by many shops whose development environment consists of ONLY a Visual Studio installation. Not using excellent open tools like NUnit or MbUnit, StructureMap or Windsor, NMock or RhinoMocks, and others, these are the reasons why the “alpha geeks” are jumping to Ruby. Right now the average Ruby developer understands using open libraries. Most .NET shops don’t seem to think this way. We need to change our way of thinking.

DataGridView File Name Path Columns

Remember that ancient technology known as Winforms. No? Remember that .NET namespace called System.Windows.Forms? You know the one. The one that Mono got to 99% completion right around the time that it became the less cool way to write apps and GTK# and WPF became the cools ways.

Anyway, I’ve been writing more Windows Forms code recently and I had a need to display a full path file name in a column. That is no problem, just data bind. But I wanted a little bit better experience for my users. Instead of a column displaying “C:\Documents and Sett…” when a column isn’t wide enough, I want it to say “…\MyFile.Txt”. It turns out that thanks to the closed nature of some things DataGridView, this isn’t all that easy.

The nice part is that the framework has a TextFormatFlags Enumeration which includes a PathEllipsis value. The not as nice part is that TextRenderer.DrawText is what uses this value. This means that we aren’t using a nice Control, we are instead drawing ourselves using GDI. *sigh* If only DataGridViewTextBoxColumn took a TextFormatFlags argument.

The solution?

Your own DataGridViewColumn and DataGridViewCell types.

Form1

See what I mean?

The cool part was that once we were doing custom painting it was pretty easy to go one step further and optionally allow browsing for a file path when a button is clicked in the cell.

Form1 (2)

Creating the column class is pretty easy.

 

/// <summary>
/// Hosts a collection of DataGridViewTextBoxCell cells.
/// </summary>
public class DataGridViewFilePathColumn : DataGridViewColumn
{
    private bool showBrowseButton;
    [Browsable(true)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [DefaultValue(false)]
    [Category(“Appearance”)]
    [Description(“Show a button in each cell for browsing for files.”)]
    public bool ShowBrowseButton
    {
        get { return showBrowseButton; }
        set
        {
 
            showBrowseButton = value;
        }
    }
 
    private bool useOpenFileDialogOnButtonClick;
    [Browsable(true)]
    [EditorBrowsable(EditorBrowsableState.Always)]
    [DefaultValue(false)]
    [Category(“Behavior”)]
    [Description(“OpenFileDialog is dispalyed and on success the contents of the Cell is replaced with the new file path.”)]
    public bool UseOpenFileDialogOnButtonClick
    {
        get { return useOpenFileDialogOnButtonClick; }
        set
        {
            useOpenFileDialogOnButtonClick = value;
        }
    }
    public DataGridViewFilePathColumn()
        : base(new DataGridViewFilePathCell())
    {
    }
    public override DataGridViewCell CellTemplate
    {
        get
        {
            return base.CellTemplate;
        }
        set
        {
            if (null != value &&
                !value.GetType().IsAssignableFrom(typeof(DataGridViewFilePathCell)))
            {
                throw new InvalidCastException(“must be a DataGridViewFilePathCell”);
            }
            base.CellTemplate = value;
        }
    }
}

/// <summary>
/// Displays editable text information in a DataGridView control. Uses
/// PathEllipsis formatting if the column is smaller than the width of a
/// displayed filesystem path.
/// </summary>
public class DataGridViewFilePathCell : DataGridViewTextBoxCell
{
    Button browseButton;
    Dictionary<Color, SolidBrush> brushes = new Dictionary<Color, SolidBrush>();
    protected virtual SolidBrush GetCachedBrush(Color color)
    {
        if (this.brushes.ContainsKey(color))
            return this.brushes[color];
        SolidBrush brush = new SolidBrush(color);
        this.brushes.Add(color, brush);
        return brush;
    }
 
    protected virtual Button GetBrowseButton(bool wireOpenFileDialog)
    {
        if (null == browseButton)
        {
            browseButton = new Button();
            browseButton.Text = “…”;
            browseButton.Click += new EventHandler(browseButton_Click); //yes, really two event handlers!
            if (wireOpenFileDialog)
                browseButton.Click += new EventHandler(delegate(object sender, EventArgs e)
                {
                    if (this.RowIndex >= 0)
                    {
                        using (OpenFileDialog ofd = new OpenFileDialog())
                        {
                            if (System.IO.File.Exists((string)this.Value))
                                ofd.InitialDirectory = System.IO.Path.GetDirectoryName((string)this.Value);
                            if (ofd.ShowDialog() == DialogResult.OK)
                            {
                                this.Value = ofd.FileName;
                            }
                        }
                    }
                });
        }
        return browseButton;
    }
 
    protected virtual bool RightToLeftInternal
    {
        get
        {
            return this.DataGridView.RightToLeft == RightToLeft.Yes;
        }
    }
    protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
    {
        if (cellStyle == null)
        {
            throw new ArgumentNullException(“cellStyle”);
        }
        this.PaintPrivate(graphics, clipBounds, cellBounds, rowIndex, cellState, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
    }
 
    protected Rectangle PaintPrivate(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
    {
        //System.Diagnostics.Debug.WriteLine(string.Format(“Painting Cell row {0} for rowindex {2} with rectangle {1}”, this.RowIndex, cellBounds, rowIndex));
        SolidBrush cachedBrush;
        Rectangle empty = Rectangle.Empty;
        if (PaintBorder(paintParts))
        {
            this.PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle);
        }
        Rectangle rectangle2 = this.BorderWidths(advancedBorderStyle);
        Rectangle borderedCellRectangle = cellBounds;
        borderedCellRectangle.Offset(rectangle2.X, rectangle2.Y);
        borderedCellRectangle.Width -= rectangle2.Right;
        borderedCellRectangle.Height -= rectangle2.Bottom;
        Point currentCellAddress = base.DataGridView.CurrentCellAddress;
        bool isFirstCell = (currentCellAddress.X == base.ColumnIndex) && (currentCellAddress.Y == rowIndex);
        bool flagisFirstCellAndNotEditing = isFirstCell && (base.DataGridView.EditingControl != null);
        bool thisCellIsSelected = (cellState & DataGridViewElementStates.Selected) != DataGridViewElementStates.None;
        if ((PaintSelectionBackground(paintParts) && thisCellIsSelected) && !flagisFirstCellAndNotEditing)
        {
            cachedBrush = GetCachedBrush(cellStyle.SelectionBackColor);
        }
        else
        {
            cachedBrush = GetCachedBrush(cellStyle.BackColor);
        }
        if (((PaintBackground(paintParts)) && ((cachedBrush.Color.A == 0xff) && (borderedCellRectangle.Width > 0))) && (borderedCellRectangle.Height > 0))
        {
            graphics.FillRectangle(cachedBrush, borderedCellRectangle);
        }
        if (cellStyle.Padding != Padding.Empty)
        {
            if (RightToLeftInternal)
            {
                borderedCellRectangle.Offset(cellStyle.Padding.Right, cellStyle.Padding.Top);
            }
            else
            {
                borderedCellRectangle.Offset(cellStyle.Padding.Left, cellStyle.Padding.Top);
            }
            borderedCellRectangle.Width -= cellStyle.Padding.Horizontal;
            borderedCellRectangle.Height -= cellStyle.Padding.Vertical;
        }
        if (((isFirstCell) && (!flagisFirstCellAndNotEditing && PaintFocus(paintParts))) && ((ShowFocusCues && base.DataGridView.Focused) && ((borderedCellRectangle.Width > 0) && (borderedCellRectangle.Height > 0))))
        {
            ControlPaint.DrawFocusRectangle(graphics, borderedCellRectangle, Color.Empty, cachedBrush.Color);
        }
        Rectangle cellValueBounds = borderedCellRectangle;
        string text = formattedValue as string;
        if ((text != null) && (!flagisFirstCellAndNotEditing))
        {
            int y = (cellStyle.WrapMode == DataGridViewTriState.True) ? 1 : 2;
            borderedCellRectangle.Offset(0, y);
            borderedCellRectangle.Width = borderedCellRectangle.Width;
            borderedCellRectangle.Height -= y + 1;
            if ((borderedCellRectangle.Width > 0) && (borderedCellRectangle.Height > 0))
            {
                TextFormatFlags flags = //DataGridViewUtilities.ComputeTextFormatFlagsForCellStyleAlignment(base.DataGridView.RightToLeftInternal, cellStyle.Alignment, cellStyle.WrapMode);
                    TextFormatFlags.PathEllipsis;
 
                if (PaintContentForeground(paintParts))
                {
                    if ((flags & TextFormatFlags.SingleLine) != TextFormatFlags.GlyphOverhangPadding)
                    {
                        flags |= TextFormatFlags.EndEllipsis;
                    }
                    DataGridViewFilePathColumn filePathColumn = (DataGridViewFilePathColumn)this.DataGridView.Columns[ColumnIndex];
                    if (true && filePathColumn.ShowBrowseButton)
                    {
                        if (this.RowIndex >= 0)
                        {
                            Button browseButton = GetBrowseButton(filePathColumn.UseOpenFileDialogOnButtonClick);
                            bool changed = false;
                            if ((browseButton.Width != Math.Max(10, borderedCellRectangle.Width / 4)) && (browseButton.Width != 20))
                            {
                                System.Diagnostics.Trace.WriteLine(string.Format(“browseButton Width was incorrect:{0} for given rectangle:{1}”, browseButton.Width, borderedCellRectangle));
                                browseButton.Width = Math.Max(10, borderedCellRectangle.Width / 4);
                                browseButton.Width = Math.Min(browseButton.Width, 20);
                                changed = true;
                            }
                            if (browseButton.Height != (borderedCellRectangle.Height – 4))
                            {
                                System.Diagnostics.Trace.WriteLine(string.Format(“browseButton Height was incorrect:{0} for given rectangle:{1}”, browseButton.Height, borderedCellRectangle));
                                browseButton.Height = borderedCellRectangle.Height – 4;
                                changed = true;
                            }
                            Point loc = new Point();
                            loc.X = borderedCellRectangle.X + borderedCellRectangle.Width – browseButton.Width;
                            loc.Y = borderedCellRectangle.Y;
                            if (browseButton.Location != loc)
                            {
                                System.Diagnostics.Trace.WriteLine(string.Format(“browseButton location was incorrect:{0} for given rectangle:{1} with loc: {2}”, browseButton.Location, borderedCellRectangle, loc));
                                browseButton.Location = loc;
                                changed = true;
                            }
                            if (changed)
                                browseButton.Invalidate();
                            if (!this.DataGridView.Controls.Contains(browseButton))
                                this.DataGridView.Controls.Add(browseButton);
                            borderedCellRectangle.Width -= browseButton.Width;
                        }
                    }
                    TextRenderer.DrawText(graphics, text, cellStyle.Font, borderedCellRectangle, thisCellIsSelected ? cellStyle.SelectionForeColor : cellStyle.ForeColor, flags);
                }
 
            }
        }
        if ((base.DataGridView.ShowCellErrors) && PaintErrorIcon(paintParts))
        {
            PaintErrorIcon(graphics, cellStyle, rowIndex, cellBounds, cellValueBounds, errorText);
        }
        return empty;
    }
 
 
    void browseButton_Click(object sender, EventArgs e)
    {
        this.RaiseCellClick(new DataGridViewCellEventArgs(this.ColumnIndex, this.RowIndex));
    }
 
    protected virtual Rectangle ComputeErrorIconBounds(Rectangle cellValueBounds)
    {
        if ((cellValueBounds.Width >= 20) && (cellValueBounds.Height >= 0x13))
        {
            return new Rectangle(RightToLeftInternal ? (cellValueBounds.Left + 4) : ((cellValueBounds.Right – 4) – 12), cellValueBounds.Y + ((cellValueBounds.Height – 11) / 2), 12, 11);
        }
        return Rectangle.Empty;
    }
 
 
 
    protected virtual void PaintErrorIcon(Graphics graphics, DataGridViewCellStyle cellStyle, int rowIndex, Rectangle cellBounds, Rectangle cellValueBounds, string errorText)
    {
        if ((!string.IsNullOrEmpty(errorText) && (cellValueBounds.Width >= 20)) && (cellValueBounds.Height >= 0x13))
        {
            Rectangle iconBounds = this.GetErrorIconBounds(graphics, cellStyle, rowIndex);
            if ((iconBounds.Width >= 4) && (iconBounds.Height >= 11))
            {
                iconBounds.X += cellBounds.X;
                iconBounds.Y += cellBounds.Y;
                PaintErrorIcon(graphics, iconBounds);
            }
        }
    }
 
    protected static void PaintErrorIcon(Graphics graphics, Rectangle iconBounds)
    {
        Bitmap errorBitmap = new Bitmap(typeof(DataGridViewCell), “DataGridViewRow.error.bmp”);
        errorBitmap.MakeTransparent();
        if (errorBitmap != null)
        {
            lock (errorBitmap)
            {
                graphics.DrawImage(errorBitmap, iconBounds, 0, 0, 12, 11, GraphicsUnit.Pixel);
            }
        }
    }
 
    protected static bool PaintErrorIcon(DataGridViewPaintParts paintParts)
    {
        return ((paintParts & DataGridViewPaintParts.ErrorIcon) != DataGridViewPaintParts.None);
    }
    protected static bool PaintFocus(DataGridViewPaintParts paintParts)
    {
        return ((paintParts & DataGridViewPaintParts.Focus) != DataGridViewPaintParts.None);
    }
    protected static bool PaintBackground(DataGridViewPaintParts paintParts)
    {
        return ((paintParts & DataGridViewPaintParts.Background) != DataGridViewPaintParts.None);
    }
    protected static bool PaintBorder(DataGridViewPaintParts paintParts)
    {
        return ((paintParts & DataGridViewPaintParts.Border) != DataGridViewPaintParts.None);
    }
    protected static bool PaintContentBackground(DataGridViewPaintParts paintParts)
    {
        return ((paintParts & DataGridViewPaintParts.ContentBackground) != DataGridViewPaintParts.None);
    }
    protected static bool PaintContentForeground(DataGridViewPaintParts paintParts)
    {
        return ((paintParts & DataGridViewPaintParts.ContentForeground) != DataGridViewPaintParts.None);
    }
    protected static bool PaintSelectionBackground(DataGridViewPaintParts paintParts)
    {
        return ((paintParts & DataGridViewPaintParts.SelectionBackground) != DataGridViewPaintParts.None);
    }
 
    public bool ShowFocusCues
    {
        get { return true; }
    }
 
    protected bool ApplyVisualStylesToHeaders
    {
        get
        {
            if (Application.RenderWithVisualStyles)
            {
                return this.DataGridView.EnableHeadersVisualStyles;
            }
            return false;
        }
    }
 
    public DataGridViewFilePathCell()
        : base()
    {
    }
}

The DataGridViewCell drived class is a little more challenging. It turns out we can inherit from DataGridViewTextBoxCell and get its editing capabilities, but rendering the Text in non-editing mode of a cell is entirely our responsibility. Welcome to the disgusting world of control drawing.

 

Side Note:

I just tried Switcher 2.0 beta thanks to Jeff Atwood’s suggestion and YES! YES! YES! This is what managing multiple windows SHOULD be!

Other Side Note:

How many times can I misspell feisty as fiesty?

Thanks to Dustin Campbell for the AWESOME code formatting tips.

DataGridView DataBound Copy, Paste, Drag, Drop

I’m doing more Windows Forms programming these day and I am very surprised to see the lack of documentation on the web regarding DataGridView and copy and paste as well as drag and drop. Not even the DataGridView FAQ – an excellent resource – covers copy and paste or drag and drop other than the ClipboardCopyMode.

With a little code you can enhance the copy and paste as well as implement drag on drop for selected rows in a DataGridView. First thing, all users think differently. Some use toolbars. some use context menus. Some use window menus. I’ll leave the window menus to the reader.

If you have a toolbar or a binding navigator, add cut, copy, and paste icons to the bar. The easiest way I have found is to add a ToolStrip control, use the “add standard items” and then copy the three ToolStripButtons and delete the unneeded ToolStrip control.

Add a ContextMenuStrip and add Cut, Copy and Paste to it. Wiring up events is pretty easy.

 

        private void cutToolStripMenuItem_Click(object sender, EventArgs e)

        {

            this.cutToClipboard();

        }

 

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)

        {

            this.copyToClipboard();

        }

 

        private void pasteToolStripMenuItem_Click(object sender, EventArgs e)

        {

            this.pasteFromClipboard();

        }

 

        private void cutToolStripButton_Click(object sender, EventArgs e)

        {

            cutToClipboard();

        }

 

        private void copyToolStripButton_Click(object sender, EventArgs e)

        {

            copyToClipboard();

        }

 

        private void pasteToolStripButton_Click(object sender, EventArgs e)

        {

            pasteFromClipboard();

        }

Implementing the actual methods is pretty easy. The trick is to use both DataGridView’s GetClipboardContent as well as your own DataFormat.

        private DataFormats.Format employeeDataFormat = DataFormats.GetFormat(“employeeDataFormat”);

        private void deleteCurrentSelection()

        {

            this.employeeBindingSource.RemoveCurrent();

        }

        private void cutToClipboard()

        {

            copyToClipboard();

            deleteCurrentSelection();

        }

        private void copyToClipboard()

        {

            IDataObject clipboardData = getDataObject();

            Clipboard.SetDataObject(clipboardData);

        }

        private IDataObject getDataObject()

        {

            //TODO: why does this return plaintext(tsv) and CSV that has empty first column?

            DataObject clipboardData = this.dataGridView1.GetClipboardContent();

 

            Employee[] employees =

                new Employee[dataGridView1.SelectedRows.Count];

            for (int i = 0; i < dataGridView1.SelectedRows.Count; i++)

            {

                employees[i] = dataGridView1.SelectedRows[i].DataBoundItem as Employee;

            }

            clipboardData.SetData(employeeDataFormat.Name, employees);

            return clipboardData;

        }

        private void pasteEmployeeDataObject(IDataObject dataObject)

        {

            object employeeDataObject = dataObject.GetData(employeeDataFormat.Name);

            if (employeeDataObject != null)

            {

                Employee[] employees = (Employee[])employeeDataObject;

                foreach (Employee employee in employees)

                {

                    bindingRowData.Add(employee);

                }

            }

        }

        private void pasteCsv(IDataObject dataObject)

        {

            object lDataObjectGetData = dataObject.GetData(DataFormats.CommaSeparatedValue);

            string csv = lDataObjectGetData as string;

            if (csv==null)

            {

                System.IO.MemoryStream stream = lDataObjectGetData as System.IO.MemoryStream;

                if (stream!=null)

                    csv = new System.IO.StreamReader(stream).ReadToEnd();

            }

            if (csv==null)

                return;

            string[] lines = csv.Split(‘\n’);

            int row = dataGridView1.NewRowIndex;

            int col = dataGridView1.CurrentCell.ColumnIndex;

            foreach (string line in lines)

            {

                if (row < dataGridView1.RowCount && line.Length > 0)

                {

                    try

                    {

                        string[] cells = line.Split(‘,’);

                        for (int i = 0; i < cells.Length

                            ; ++i)

                        {

                            if (col + i < this.dataGridView1.ColumnCount)

                            {

                                dataGridView1[col + i, row].Value =

                                    Convert.ChangeType(cells[i], dataGridView1[col + i, row].ValueType);

                            }

                            else

                            {

                                break;

                            }

                        }

                        row++;

                    }

                    catch (FormatException)

                    { //TODO: log exceptions using a nice standard logging library 🙂

                        dataGridView1.CancelEdit();

                    }

                }

                else

                {

                    break;

                }

            }

        }

        private void pasteFromClipboard()

        {

            if (dataGridView1.SelectedRows.Contains(dataGridView1.Rows[dataGridView1.NewRowIndex]))

            {

                dataGridView1.Rows[dataGridView1.NewRowIndex].Selected = false;

                dataGridView1.CancelEdit();

            }

            IDataObject clipboardData = Clipboard.GetDataObject();

            if (Clipboard.ContainsData(employeeDataFormat.Name))

            {

 

                pasteEmployeeDataObject(clipboardData);

            }

            else if (Clipboard.ContainsData(DataFormats.CommaSeparatedValue))

            {

 

                pasteCsv(clipboardData);

            }

        }

The cool thing about the different paste methods and getObject method is that they work well with drag and drop as well.

Accepting drops is easy. It is totally straight forward just like it is with other windows forms controls.

        private void dataGridView1_DragEnter(object sender, DragEventArgs e)

        {

 

            if (e.Data.GetDataPresent(employeeDataFormat.Name) || e.Data.GetDataPresent(DataFormats.CommaSeparatedValue))

            {

                e.Effect = DragDropEffects.Copy;

 

            }

            else

                e.Effect = DragDropEffects.None;

        }

 

        private void dataGridView1_DragDrop(object sender, DragEventArgs e)

        {

            if (e.Effect == DragDropEffects.Copy)

            {

                string[] formats = e.Data.GetFormats();

                if (e.Data.GetDataPresent(employeeDataFormat.Name))

                {

                    pasteEmployeeDataObject(e.Data);

                }

                else if (e.Data.GetDataPresent(DataFormats.CommaSeparatedValue))

                {

                    pasteCsv(e.Data);

                }

            }

        }

Handling drags is a little more challenging. DataGridView doesn’t have the ItemDrag event. Total bummer. It turns out implementing drag detection is very easy with a simple user32.dll function call called – amazingly enough – DragDetect.

        [System.Runtime.InteropServices.DllImport(“user32.dll”)]

        private static extern bool DragDetect(IntPtr intPtr, Point point);

 

        Point dragEnterPoint;

 

        private void dataGridView1_MouseMove(object sender, MouseEventArgs e)

        {

            if (e.Button == MouseButtons.None)

            {

                this.dataGridView1.MouseMove -= new System.Windows.Forms.MouseEventHandler(this.dataGridView1_MouseMove);

            }

            DataGridView.HitTestInfo info = dataGridView1.HitTest(e.X, e.Y);

            bool lDragDetect = DragDetect(this.Handle, dragEnterPoint);

            if (lDragDetect)

            {

                if (info.RowIndex >= 0)

                {

                    IDataObject dataObject = getDataObject();

                    if (dataObject != null)

                        dataGridView1.DoDragDrop(dataObject, DragDropEffects.Copy);

                }

            }

        }

 

        private void dataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)

        {

            if (e.Button == MouseButtons.Left)

            {

                if (e.ColumnIndex >=0 )

                {

                    dragEnterPoint = new Point(e.X, e.Y);

                    this.dataGridView1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.dataGridView1_MouseMove);

                }

            }

        }

 

        private void dataGridView1_CellMouseUp(object sender, DataGridViewCellMouseEventArgs e)

        {

            this.dataGridView1.MouseMove -= new System.Windows.Forms.MouseEventHandler(this.dataGridView1_MouseMove);

        }

A couple things are going on here. We don’t want to change too much behavior of the DataGridView. Dragging in the RowHeader normally highlights multiple rows. So in the  CellMouseDown handler we check that ColumnIndex is non-negative. ColumnIndex is -1 if the mouse was down in the RowHeader.

I’m not using the DataGridView Click or MouseDown events because they would prevent those events from getting down to the cells and prevent normal cell editing behavior. The MouseMove event is used to call DragDetect but the event is only registered and fired if the mouse button is down. I don’t want to be firing lots of pointless MouseMove events. This works well enough.

Finally DoDragDrop is called with the same getObject result as a copy uses.

Once catch. DataGridView does implement its own copy if you press ctrl-c. This means that the current row gets put onto the clipboard, but it isn’t using my copy method. What is worse is that if my copy method is used, then I can do something really cool like copy, then reorder the columns and paste. Since it is using the custom DataFormat and just adding to a databound list, everything pastes into the proper columns. The alternative is that things paste into the wrong columns or worse, you get an exception because the datatypes don’t match. Oddly, DataGridView doesn’t implement paste via ctrl-v. This is trivially rectified.

        private void dataGridView1_KeyDown(object sender, KeyEventArgs e)

        {

            if (e.KeyCode == Keys.C && e.Control)

            {

                this.copyToClipboard();

                e.Handled = true; //otherwise the control itself tries to “copy”

            }

            if (e.KeyCode==Keys.v && e.Control)

            {

                this.pasteFromClipboard();               

            }

        }

Overall the result is nice. You can copy and drag rows within the control itself, between multiple instances of the same DataGrid. You can drag and copy and paste between Excel. Its all good.