Design-time problems

mooman_fl

Centurion
Joined
Nov 3, 2002
Messages
193
Location
Florida, USA
I have done variations on this question before and never really did get the problem sorted.

I am making a component that has a collection class as a property. Among the properties in this class is a filename string that should point to an interface library in the containing program's folder.

It also has a second, nested, collection class as a property. When the filepath is set it looks at the interface library and retrieves the interface names listed in the library.

My initial problem was that the path in the filename string was set by a FindFile dialog, and it set an absolute path serialized in the control to the project directory. This would not do for the end-user at runtime however. I fixed this to just serialize the filename without the path. I did this by using this logic in the "set" for the property:

Visual Basic:
[size=2][color=#008000]'Value is the path and filename returned by the FileNameEditor dialog
 
[/color][/size][size=2][color=#0000ff]Set[/color][/size][size=2]([/size][size=2][color=#0000ff]ByVal[/color][/size][size=2] [color=black]Value[/color] [/size][size=2][color=#0000ff]As[/color][/size][size=2][color=#0000ff]String[/color][/size][size=2])
 
[/size][size=2][color=#008000]'Regex determines if the value returned is a complete path.
 
[/color][/size][size=2][color=#008000]'The only time the path should be complete is when the property
 
[/color][/size][size=2][color=#008000]'is set by the FileNameEditor. If it is set at runtime via the
 
[/color][/size][size=2][color=#008000]'serialized value, it will be filename only minus the path.
 
[/color][/size][size=2][color=#0000ff]If[/color][/size][size=2] [color=blue]System.Text.RegularExpressions.Regex.IsMatch[/color][color=black](Value,[/color] [/size][size=2][color=#800000]"^[a-zA-Z]:\\.*\..*"[/color][/size][size=2][color=black])[/color] [/size][size=2][color=#0000ff]Then
 
[/color][/size][size=2][color=#008000]'This retrieves the filename from the complete path.
 
[/color][/size][size=2][color=black]FileName =[/color] [color=blue]System.IO.Path.GetFileName[/color][color=black](Value)[/color]
 
[/size][size=2][color=#0000ff]Else
[/color][/size]
[size=2][color=#008000]
[/color][/size][size=2][color=black]FileName =[/color] [color=blue]System.Windows.Forms.Application.StartupPath[/color] [color=black]+[/color] [/size][size=2][color=#800000]"\"[/color][/size][size=2] [color=black]+ Value[/color]
 
[/size][size=2][color=#0000ff]End[/color][/size][size=2][color=#0000ff]If[/color][/size]

This works fine during runtime and designtime. The problem comes in the next statements during designtime...

Visual Basic:
[size=2][color=#008000]'Loads the assembly and looks through it for interfaces.
 
[/color][/size][size=2][color=#0000ff]Dim[/color][/size][size=2] [color=black]aASM[/color] [/size][size=2][color=#0000ff]As[/color][/size][size=2] [color=black]System.Reflection.Assembly[/color]
 
[/size][size=2][color=#008000]'load interface carrying DLL
 
[/color][/size][size=2][color=black]aASM =[/color] [color=blue]System.Reflection.Assembly.LoadFile[/color][color=black](FileName)[/color]
 
[/size]

At designtime FileName winds up giving a "file not found" error when you try to go to Design View for the containing form. Turns out the it is putting the path of the DLL as the path to the VS.NET folder. I know WHY it is doing this... just not sure what I should do to get more acceptable behavior.

Is there possible a way just to programmatically get a path to the component library dll itself? Or maybe to get the path to the project directory programmatically at design time?
 
Last edited:
Looks like what I may have to do is keep the original Value passed back from the FileNameEditor Dialog and the check to see if the control is in Designtime or Runtime. If it is runtime then I will programmatically strip the path and substitute it with the Application.StartupPath. This just seems a little messy to me because then you have a path in the executable that points to a spot on the programmers drive. I don't like that.

On a slightly different note I did find the way to determine if you are in DesignMode or not... it returns True if you are and errors out if you aren't but a Try/Catch fixes that:

Visual Basic:
[size=2][/size][size=2][color=#0000ff]Try

[/color][/size][size=2][color=black]Windows.Forms.MessageBox.Show[/color][color=black]([/color][/size][size=2][color=#800000]"DesignMode = "[/color][/size][size=2] [color=black]+[/color] [/size][size=2][color=#0000ff]Me[/color][/size][size=2].[color=black]Site.DesignMode.ToString)[/color]

[/size][size=2][color=#0000ff]Catch[/color][/size][size=2] [color=black]ex[/color] [/size][size=2][color=#0000ff]As[/color][/size][size=2] [color=black]Exception[/color]

[color=black]Windows.Forms.MessageBox.Show([/color][/size][size=2][color=#800000]"DesignMode = False"[/color][/size][size=2][color=black])[/color]

[/size][size=2][color=#0000ff]End[/color][/size][size=2] [/size][size=2][color=#0000ff]Try

[/color][/size]
What is the general consensus? Is the method I mention a bit messy or should it be ok? Any suggestions on alternative methods?
 
Why are you using regex to do the path manipulation? Seriuosly considering using the Path static methods instead, if (Path.IsRooted(foo)) foo=Path.GetFileName(foo); for example. Similarly if you want to create a new path with the application current directory and the filename you should use Path.Combine() instead of manually glueing bits of strings together.

You're making a component, which i'm going to assume really means you're deriving from Component (or Control or Form or some other class which either inherits from Component or implements IComponent) so instead of using the .Site property which can be null without first checking it (which can cause a NullReferenceException) why don't you just use the DesignMode Property which won't require you to use expcetion in flow control logic and will simply return a boolean value you can use to determine design mode status or not.
 
Thanks for the reply. I will experiment a bit with your suggestions a bit to see how they can clean things up a bit. However I am still stuck with the problem of the paths being very different at design-time as opposed to run-time. This problem, as outlined in my previous two points is the one I really need addressed.
 
Take another look at the code posted by Wraith:
C#:
string foo = "C:\SomeFolder\SomeDoc.txt";

if (Path.IsRooted(foo)) 
    foo=Path.GetFileName(foo);
// now foo = "SomeDoc.txt"
The IsRooted method determines if the specified filename is absolute (i.e. "rooted" means that a drive name is specified). The GetFileName method gets the name of the file without the preceeing folder path.

Note that this is not the same as a relative path, though. For instance, if a file is loaded from "designTimePath\Data\SomeFile.ext" at design time, the relative path at runtime would be "runTimePath\Data\SomeFile.ext", but your code, as it is written, will store the filename simply as "SomeFile.ext".

The code you posted confuses me... (sans comments)
Visual Basic:
Set(ByVal Value AsString)  
    If System.Text.RegularExpressions.Regex.IsMatch(Value, "^[a-zA-Z]:\.*..*") Then  
        FileName = System.IO.Path.GetFileName(Value)  
    Else
        FileName = System.Windows.Forms.Application.StartupPath + "" + Value  
    EndIf
It looks like you are trying to determine whether the property is set a runtime or design time based on the format of the filename. This is hardly foolproof. If a relative filename is specified it is stored as absolute, and if an absolute filename is specified it is stored as relative. Think about this: if you use a dialog to get a filename it will be absolute, which will cause it to be serialized as a relative filename. Next time the designer is open the property will be initialized with this relative filename, which will cause it to be stored and serialized as absolute. It will flip-flop like this each time the component is opened in the designer, and exceptions become inevitable.

Why not have the file name always stored internally with a relative filename and construct the full absolute filename when the filename is retrieved, i.e.:
Visual Basic:
Dim _Filename As String
 
'Gets/sets the filename. This property always internally stores the filename in a relative manner.
Public Property Filename As String
    Get
        Return _Filename
    End Get
    Set(value As String)
        'If filename is absolute the remove the path and keep only the filename
        If System.IO.Path.IsRooted(value) Then _
            value = System.IO.Path.GetFileName(value)
    
        'Now we will never store a rooted filename here
        _Filename = value
    End Set
End Property
 
'Gets the absolute path to the filename.
Public ReadOnly Property FullFilename As String
    Get
        Return System.IO.Path.Combine(Application.StartupPath, _Filename)
    End Get
End Property
Of course that probably does not suit your needs exactly, and may need to be modified. For instance, you might want to store both the full filename for design-time use and the relative filename for run-time use. It won't hurt to serialize the extra data, and you can be sure that you maintain exactly the right path to the file at design-time (provided that the project is not moved from computer to computer).

BTW, for tips on having your component differentiate from design-time and runtime (i.e. retrieving the correct filename if you use the technique I described above), look in the Code Library for a recent post by me on the topic.
 
Thanks Marble_eater! While your example as you were pointing out doesn't exactly suit my needs you did answer a few questions for me. I feared that it was too messy or unprofessional to serialize the design-time path although I really didn't see a way around that.

Just in case the specifics help, this was part of a property in a control. You specify the interface library dll and it will pull a reference to the interfaces out and put them in a collection for access later by the functions that search plugin dlls for matching interfaces.

Indeed, relative path is desirable at run-time... but design-time seems to be a bit more of a sticky wicket. Seems that MS should have included a method that can be used to determine the path to the current projects directory, or at least the build directory. Would make things alot easier in controls like this since that is often very useful info.

If anyone has any more suggestions I am willing to listen, extra helpful suggestions could net a free, licensed copy of the control when it is done.
 
Back
Top