Thursday, April 8, 2010

ClickOnce and OpenVMS

Tasked with deploying our Windows ClickOnce application to Apache running on OpenVMS, I never imagined the pain I'd encounter.
Yes, it can be made to work.
First requirement, the OpenVMS server must be ODS5 to handle long filenames with spaces and extra dots.
I set up Team Systems to automatically build and FTP the deployment to the Apache directories on the OpenVMS server.  I set up a custom task for the FTP which utilized the FTP classes built into .NET.  This proved to be a huge mistake.  It appears that the FTP conventions Microsoft uses doesn't play well with the FTP conventions used by OpenVMS.  So long as I only uploaded files to directories directly under the root or directly to the default user directory, I had no problems.  But the problem was with deploying to subdirectories, particularly if they had those spaces or extra dots.  There was no way to properly define the path using the URI convention that .NET uses.  Sure, you can use "%20" to represent the space, but for some reason OpenVMS got real confused by the URI convention.  OpenVMS uses brackets ("[" and "]") for directory identification, with periods for subdirectories ("[MAIN.SUBDIRECTORY]" as opposed to "c:\MAIN\SUBDIRECTORY\"), and trying to FTP with a URI ("ftp://MAIN/SUBDIRECTORY") doesn't translate well when there are special characters in the URI.
And no matter what I did, I could not make it work.
I finally succeeded by instead creating a DOS-style FTP command script and using Process.Start to shell out to the built-in Windows FTP command.  It turned out not being all that much more complicated than using the built-in .NET FTP.  The only thing I really lost was accurancy in determining success or fail of the transfer.  I may be able to handle this by capturing standard error on Process.Start, but this is something for another time--when I'm bored or something.

Thursday, December 17, 2009

Detecting Design Mode in WPF controls

Who would've thought that it would be difficult to find information on detecting Design mode in WPF controls.  In Winforms, there is a simple DesignMode boolean property you can check, but it's not so obvious in WPF, plus Bing searches gave convoluted answers (you know, the kind of answer you get from a know-it-all geek), although the answer was there.
 
So here's the beef:
 
System.ComponentModel.DesignerProperties.GetIsInDesignMode(this);
 
This returns a boolean, and "this" is your UserControl or something that is a DependencyObject.

Tuesday, December 8, 2009

Updating WPF window from background thread

I like to do things in the background--you know, you've got a long-running process, so you don't want to bother the user while it runs.

But you want to notify the user when the process is done.  Trouble is, since the background process is running in a different thread from the user interface, the process crashes if you make any attempt to update the user interface directly.

Here's some sample code to solve this with the Dispatcher--kind of well known, but nice to document anyhow:


void UpdateText(string text)
{
    if (this.Dispatcher != System.Windows.Threading.Dispatcher.CurrentDispatcher)
    {
        this.Dispatcher.Invoke(new Action(Update), text);
    }
    else
    {
        this.sampleTextBox.Text = text;
    }

}

Monday, December 7, 2009

C# Inheritance, Typecasting, and Reflection

Did you know that an inherited class cannot be cast into its base class so that reflection will think it's working with the base class?



This proved a minor nuisance as the code had a simplistic work-around, but this might not always be the case.



Below is an example of what I'm talking about:


using System.Reflection;
public static class UtilityClass
{
    public static object GetResult(object workClass)
    {
        string ResultingClassName = "Result" + workClass.GetType().ToString();
        Type provider = Type.GetType(ClassName, true);
        ConstructorInfo constructor = provider.GetConstructor(new Type[0]);
        return constructor.Invoke(new object[0]);
    }
}


public class ResultClass
{
    public ResultClass()
    {
    }
}
public class Class
{
    public ResultClass GetResult()
    {
        return (ResultClass)UtilityClass.GetResult(this);
    }
}
public class ChildClass : Class
{
    public void DoTest()
    {
        ResultClass x = ((Class)this).GetResult();
    }
}

The above code will fail with an exception when DoTest() in ChildClass is called, because it will try to create ResultChildClass, which does not exist.  No matter how you cast, you can't get this to work, because the GetResult method of UtilityClass, using Reflection, will always identify the object for what it really is.

In my case, the solution was to simply create a new "Class" object and copy the base of the ChildClass object into it, then call the "GetResult" method to get my "ResultClass".

Friday, December 4, 2009

System.OutOfMemoryException from Visual Studio

Lately, with my WPF project, I commonly get OutOfMemoryException whey I try to run my project. Visual Studio builds the project, then gets the exception when it tries to start up the executable.

I found a decent work-around is to Build the solution first. Then run the solution. This two-step process seems to solve the issue in that when you run it, Visual Studio doesn't build it--so I guess it's got time to clean up memory before starting your executable.

You might eventually still run out of memory, but this'll give you some extra time before you have to close Visual Studio and start it up again.

Thursday, December 3, 2009

XAML Binding to Datasets

Found that DataSets make lousy binds for WPF forms when the data must be updated from something other than directly by the user, because datasets do not implement INotifyPropertyChanged.

Datasets are great when the data is OneWay binding, because heirarchical data relationships are so easy to define. But you've got to get creative and use something else, such as a BindingList<> when the binding must be TwoWay or OneWayToSource.

Wednesday, December 2, 2009

XAML and the loss of Intellisense

Last week I reported losing my intellisense and tag completion on XAML pages.
 
I finally figured out the cause--and it's not good.  I happend to start up a new solution with a single page and all of a sudden I noticed I had my Intellisense and tag completion back!
 
After getting all excited about it, I went back to my original solution that was having problems--Intellisense and code completion disappeared.
 
Yet they came back with the nothing of a solution.
 
Okay--I get the hint.  XAML and WPF take lots and lots of RAM.  I have only 2GB.  From what I'm gathering, you need at least 3GB RAM.
 
All this only after I totally hosed up my Visual Studio trying to figure it all out.
 

Tuesday, December 1, 2009

Using Syncfusion

I'll give the support team at Syncfusion credit--they do seem to respond quickly to help you out.
 
But I'd give the development team there a failing grade.  They seem to be prone to deploy rather buggy controls for WPF use.
 
Seems getting "NullReferenceException" is commonplace with their controls.  And I just stumbled onto a stupid one--using their DateTimeEdit control and setting the MinDateTime attribute causes the control to display "01/01/0001" as its datetime value.  These kind of bugs are extremely annoying when you are trying to get a job done.  They're so bad that I don't know how they get away with charging for them when so many open-source and non-buggy alternatives are available.  Had I been give the ability to choose which controls to use, Syncfusion would never have been considered.
 
Fortunately, I stumbled onto http://wpf.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=29117.  There is a DatePicker control there that'll work for me--and there's probably a large number of other controls.  Especially since Syncfusion is about as crappy as it comes--try finding what you need without asking their support department--I'm going to trash the Syncfusion control usage wherever possible.
 

Friday, November 27, 2009

Syncfusion GridDataControl and referencing other columns

I was using the Syncfusion GridDataControl in WPF and needed to reference a different column from within the column I was in, so that different attributes could be set based on other columns.  Found out from the SyncFusion Form support center that it's pretty easy.  Just set the Binding Path as follows:
 
Binding Path=Record.Data.OtherColumnName

WPF form and debugging data

I never realized just how painful it can be to debug data that should (or should not) display on a XAML form.  I bound a XAML form to a DataSet.  There was a problem with one of the columns in the dataset that caused problems with the XAML form.  But since there is no way to step through the debugger on the XAML, there was no way to figure out what the specific data was that was giving me problems.
 
The problem was solved through a Converter.  I discovered you can put a breakpoint on a converter, then view your data.  In my case, I had no need of a converter, so couldn't set up a breakpoint.
 
The solution was to add a special converter.  This one did not do any actual conversion--it simply returned the passed value.  But it gave me a simple way to put a breakpoint.
 
Below is the converter code:

class LogObjectDataConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
   
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}