Thursday, January 10, 2019

My Nullable DateTime Picker

I needed to bind a nullable DateTime to a Winforms DateTimePicker control. Unfortunately, the standard DateTimePicker only supports regular DateTime objects. I looked around and found some controls that other people had built, but none of them did exactly what I needed. 

I wanted a control where, if the checkbox is unchecked, the value is considered null and the box is blanked out. I couldn’t find one like that, so I built my own.

The first one I built was written in VB.Net. In this blog post, we'll create a C# version. 


We start by creating a new class and having it inherit from DateTimePicker. 
using System.Windows.Forms;

namespace NullableSample
{
    public class NullableDateTimePicker : DateTimePicker
    {
    }
}

Then add a nullable DateTime property to hold the binded value. 
public class NullableDateTimePicker : DateTimePicker
{
    public DateTime? BindedValue { get; set; } 
}
In the constructor, we set the ShowCheckBox value to true because we want it to always be visible. Then we set the format type to custom. 
public NullableDateTimePicker() : base()
{
    this.ShowCheckBox = true;
    this.Format = DateTimePickerFormat.Custom; 
}
Now we create a method to use to bind our nullable value to our property: 
public void Bind(object datasource, string dataproperty)
{
    var oldBinding = this.DataBindings["BindedValue"];
    if (oldBinding != null)
        this.DataBindings.Remove(oldBinding);

    var b = new Binding("BindedValue", datasource, dataproperty, true);
    b.Format += FormatMyDate;
    b.Parse += ParseMyDate;
    this.DataBindings.Add(b);
}
Notice we also add handlers for the Format and Parse events. 

In the Format handler, we check to see if the date is null. If it is, we set the custom format to be a blank string and un-check the check box. Otherwise, if there is a date, we set the custom format to MM/dd/yyyy and the check box to checked. 
private void FormatMyDate(object sender, ConvertEventArgs e)
{
    if (e.Value == null)
    {
        this.Value = this.Value;
        this.Format = DateTimePickerFormat.Custom;
        this.CustomFormat = " ";
        this.Checked = false;
    }
    else
    {
        this.Value = (DateTime)e.Value;
        this.Format = DateTimePickerFormat.Custom;
        this.CustomFormat = "MM/dd/yyyy";
        this.Checked = true;
    }
}
The Parse handler checks to see if the checkbox is checked. If it is, is sets the value. Otherwise is sets the value to null.
private void ParseMyDate(object sender, ConvertEventArgs e)
{
    if (this.Checked == true)
        e.Value = this.Value;
    else
        e.Value = null;
}
Finally we need to override the OnValueChanged event. Here we set the BindedValue property to the value that was entered. Then, if the checkbox is checked, we set the custom format to MM/dd/yyyy. If it’s not, we make it blank.
protected override void OnValueChanged(EventArgs eventargs)
{
    base.OnValueChanged(eventargs);

    BindedValue = this.Value;

    this.Format = DateTimePickerFormat.Custom;

    if (this.Checked == false)
        this.CustomFormat = " ";
    else
        this.CustomFormat = "MM/dd/yyyy";
}

To test it out, we'll create a simple Person class with a nullable DateTime field as the date of birth: 
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime? DOB { get; set; }
    }

Next, we'll compile the project and add a NullableDateTimePicker to our form. In our code we create an instance of the Person class and bind its DOB field to our nullable datetime picker: 

   var tim = new Person { FirstName = "Tim", LastName = "Listman", DOB = new DateTime(1980, 1, 30) };

   nullableDateTimePicker1.Bind(tim, "DOB"); 
If we run it now, it should look like this:



If the box is un-checked, nullableDateTimePicker1.BindedValue.HasValue will return false.

But if the box is checked, we can get the selected date from nullableDateTimePicker1.BindedValue.Value. 


Sunday, May 27, 2018

Restoring Color Icons in Gimp 2.10

When Gimp 2.10 was released, the new icons for the toolbox items were all black and white. If, like me, you'd like to go back to the color versions, here's how: 


  • Select Edit --> Preferences
  • From the Preferences dialog, under the Interface section, select Icon Theme
  • For the icon theme, select Legacy if you want icons like they were in 2.8. 
  • If you'd like color versions of the new icons, select the icon theme named Color



Tuesday, March 20, 2018

VisualTreeChanged Error when Debugging VSIX

I just had an odd thing happen. I tried to debug a VSIX project in VS 2015. When I ran it, I got an error that said:

Message: An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationCore.dll
Additional information: The visual tree has been changed during a 'VisualTreeChanged' event.

At first, I thought it might be related to the issue VS 2017 caused with VSIX projects. But this was a different error. After Googling, I discovered here that you could fix it by disabling Enable UI Debugging Tools for XAML.

Tools --> Options --> Debugging --> General --> Uncheck Enable UI Debugging Tools for XAML.

Monday, January 1, 2018

VSIX Winforms Toolpane

The other day I wanted to create a Visual Studio Toolpane window and I wanted to create it using Winforms (by default, the toolpanes use WPF).

After much research, I got it to work. These are the steps I followed:

I've tested these steps with Visual Studio 2015 & Visual Studio 2017.

  1. Create a new Extensibility project
    1. In Visual Studio, from the File menu, select New --> Project.
    2. In the New Project dialog, one the left, under Visual C#, select Extensibility.
    3. On the right, select VSIX Project.
    4. Give the project a name. In this example, I'm calling it MySampleVSIXProject
  2. Add a Visual Studio Package
    1. Right-click the project in Solution Explorer and select Add --> New Item
    2. In the Add New Item dialog, on the left, expand Extensibility and select VSPackage.
    3. On the right, select Custom Tool Window.
    4. Give the tool window a name. In this example, I'm calling it MySampleToolWindow.cs
  3. Add a Winforms User Control
    1. Right-click the project in Solution Explorer and select Add --> New Item
    2. In the Add New Item dialog, on the left, select Windows Forms
    3. On the right select User Control
    4. Give the User Control a name. In this example, I'm calling it MySampleUserControl.cs
    5. Drag a label to the control so we can see something when we run it. Change the Text property. In this example, I changed it to "I'm a Winforms Control".
  4. Substitute our Winform Control for the WPF Control
    1. Open the MySampleToolWindow.cs file. It should look something like this (I've removed the comments for simplicity):
    2. 
        public class MySampleToolWindow : ToolWindowPane
        {
         public MySampleToolWindow() : base(null)
         {
          this.Caption = "MySampleToolWindow";
          this.Content = new MySampleToolWindowControl();
         }
        }
      
       
    3. Add a field to the MySampleToolWindow class. Make it of type MySampleUserControl and name it control.
    4. Then comment out the line that says "this.Content = new MySampleToolWindowControl();"
    5. Under the commented out line, add a line to create an instance of our control.
    6.   public class MySampleToolWindow : ToolWindowPane
        {
         public MySampleUserControl control; 
         public MySampleToolWindow() : base(null)
              {
                  this.Caption = "MySampleToolWindow";
                  //this.Content = new MySampleToolWindowControl();
                  this.control = new MySampleUserControl();
              } 
        }
       
    7. Add a using System.Windows.Forms; statement at the top of the code.
    8. Next, add this code to the class:
    9.         public override IWin32Window Window
              {
                  get
                  {
                      return (IWin32Window)control;
                  }
              }
       
      It should now look like this:
          public class MySampleToolWindow : ToolWindowPane
          {
              public MySampleUserControl control; 
              public MySampleToolWindow() : base(null)
              {
                  this.Caption = "MySampleToolWindow";
                  //this.Content = new MySampleToolWindowControl();
                  this.control = new MySampleUserControl();
              }
      
              public override IWin32Window Window
              {
                  get
                  {
                      return (IWin32Window)control;
                  }
              }
          }
       

Now we can run the project. In the experimental version of Visual Studio that opens, from the View menu, under Other Windows, you should see our window ("MySampleToolWindow".) Click it to open our window. It opens in a window by itself.

That's great. But what if we want it to open already docked to the Solution Explorer panel?

That's easy. We just have to add some attributes. Open the MySampleToolWindowPackage.cs. On the MySampleToolWindowPackage class, find the ProvideToolWindow attribute. It should look like this:

[ProvideToolWindow(typeof(MySampleToolWindow))]
 

We'll add these Style and Windows attributes to it:

[ProvideToolWindow(typeof(MySampleToolWindow),  
    Style = Microsoft.VisualStudio.Shell.VsDockStyle.Tabbed,  
    Window = "3ae79031-e1bc-11d0-8f78-00a0c9110057"))]
 
According to this MSDN page:
"The first named parameter is Style and its value is Tabbed, which means that the window will be a tab in an existing window. The docking position is specified by the Window parameter, in this case, the GUID of the Solution Explorer."

Now if we run it, it will show up next to the Solution Explorer:

At this point, you can optionally delete the MySampleToolWindowControl.xaml file. This is the WPF control that we're not using.

Thursday, December 28, 2017

Visual Studio 2015 VSIX Project Stopped Working

Yesterday I opened up a VSIX project to add a new feature. This is a Visual Studio 2015 project that I hadn't worked on in a few months. It worked fine a few months ago, but now, all of the sudden, it wouldn't build. What's going on?

The error message I was getting stated:

"Cannot find wrapper assembly for type library "EnvDTE100". Verify that (1) the COM component is registered correctly and (2) your target platform is the same as the bitness of the COM component."

After Googling, I found from this site that if you install Visual Studio 2017 on your machine, it will affect your references to the EnvDTE assemblies. I had recently installed VS 2017. Thankfully, the site also contained the solution:

"In old VSIX projects: Remove old COM references to EnvDTE, EnvDTE80, EnvDTE90 and EnvDTE100. Add new references using Assemblies | Extensions tab and select EnvDTE, EnvDTE80, EnvDTE90, EnvDTE100. The project should build now."

I updated my references and everything started working again.

Tuesday, May 9, 2017

Deleting Files in Visual Studio/TFS Taking a Long Time

I needed to delete some files from source control and noticed that it was taking Visual Studio/TFS about 30 seconds to delete a file (move it to the delete pending list). Since I needed to delete a lot of files, you can imagine how annoying this was. So, I fired up good old Process Monitor and started watching devenv.exe. Then I went into Source Control explorer and deleted a file. Suddenly Process Monitor was full of folder accesses. Dozens of folders were being scanned. After some research, it turned out that when I deleted a file for some reason Visual Studio was looking through all the folders in the same workspace as the file.

So, I created a new workspace and put my project and only that project in the workspace. Now when I try to delete a file it happens very quickly.
Lesson learned.

Thursday, August 11, 2016

More Quotes I Like

Here's some more quotes I've run across that I like: 


"The faster a program converts a task from not-finished to finished, the happier the user will be." -- unknown

"The only value that your software ever has or ever will have is the degree to which it increases the happiness of its users." -- unknown


"You don't want to not have problems. You want to have better problems." -- John Sonmez