Common resources for all projects in a solution - vb.net

I have solution with few projects included.
One of project is common library project which contain mostly reusable code and functions which shares all other projects under solution and which is referenced to all included projects.
In that project I also have resources like are images and icons and I can access those resources from other projects like that:
Me.Icon = myCommonDll.My.Resources.backup__add__16x16
Question is:
Can it be done somehow that those common resources in myCommonDll will be actual resources for other projects in solution so when I click to "Image" or "Icon" in properties window in designer I get listed images or icons which are present in myCommonDll.My.Resources and how to do that?

EDIT: This is primarily for VS versions prior to VS2008 which did NOT allow you to change the access level modifier for REsources. Other alternatives were external tools or Reflection, but still did not expose resources to the designer.
DLL resources are available at runtime fairly easily but not in the designer. You need to write a small broker in the DLL to fetch images by name. DLL:
Public Class ResMgr
' depending on what else is in the DLL, can just add to an existing class
Public Function GetImage(imgName As String) As Image
Return My.Resources.ResourceManager.GetObject(imgName)
End Function
'' alternatively declare it SHARED eg
''Public Shared Function/Property GetImage As Image
End Class
App:
MyRM = New ResMgr
thisImg = New Image
thisImg = MyRM.GetImage(userImg)
If you construct it as Shared method, it is just:
thisImg = ResMgr.GetImage(userImg)
If you want, you can expose an Enum in the DLL to act as a resource manifest:
Public Enum ResImg
Image1 ' use the names of the images
Backup52
Flag_FR
Flag_RUS
...
End Enum
The DLL function can either act on ResImg and use a big case statement, or you can use [Enum].GetNames to convert/get an array of image resource names.

Edit your .vbproj file and update the two statements below by adding the common directory path of your shared resource files. Good Luck!
<Compile Include="<common Dir>\My Project\Resources.Designer.vb">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<EmbeddedResource Include="<common Dir>\My Project\Resources.resx">
<Generator>VbMyResourcesResXFileCodeGenerator</Generator>
<CustomToolNamespace>My.Resources</CustomToolNamespace>
<SubType>Designer</SubType>
<LastGenOutput>Resources.Designer.vb</LastGenOutput>
</EmbeddedResource>

Related

Organizing Resource Files

I need to add support for alternate languages to my VB application. I have started by creating a resource file with strings and values for one form, using GetString. Before I go too far, I want to figure out the best organization. In VB6 I could have a string table with multiple languages in a file which made it easy to add more. It looks like I need files for each langue in .NET.
Should there be a single large file for each language for the entire project, or multiple files, say for groups of forms?
Here is a function I am using to access the strings:
Public Function GetString(ByVal strValue As String)
Select Case m_str_System_Language
Case "EN" 'English
rm = My.Resources.English.ResourceManager
Case "FR" 'French
'rm = My.Resources.French.ResourceManager
Case "ES" 'Spanish
'rm = My.Resources.Spanish.ResourceManager
Case "DE" 'German
'rm = My.Resources.German.ResourceManager
Case Else '"EN" 'English
rm = My.Resources.English.ResourceManager
End Select
'Return language specific string
GetString = rm.GetString(strValue)
End Function
Well, this is a big field. For the beginning, read Walkthrough: Localizing Windows Forms. You will learn a lot from it. Sometimes you find some gems even on the Microsoft website. ;-)
I use three strategies to localize apps. The first is:
Localize Forms
Thats quite easy.
Set the Localizable property to True
Keep in mind that the texts you already have on your form are the default texts which apply when the app is set to a culture which you still didn't create texts for. So this would be normally english.
Now set the Language property of the form to that Language/Culture you want to translate to
Now change all texts on your form (labels, button, menuitems, everything!) with their french, german, spanish or whatever counterpart
Now save the form and switch back to the default language. There you will find your old (english) texts
In the folder where your form locates you will find a new *.resx file which is named YourForm.de-DE.resx or YourForm.fr-FR.resx dependig on what language you just edited
Depending what culture is set for the Thread the appropriate texts will be shown
Localized ComboBox entries
Sometimes you have ComboBoxes in which all texts must be localized. Then I use the old database table approach. This would look like:
ID Group ItemID en_US de_DE
1 Sexes 0 male männlich
2 Sexes 1 female weiblich
3 Sexes 2 diverse divers
4 FamilyStatus 0 single ledig
5 FamilyStatus 1 married verheiratet
6 FamilyStatus 2 divorced geschieden
7 FamilyStatus 3 widowed verwitwet
And then get the right SELECT Statement depending on the language you want and the group you want.
The other stuff
The other stuff means e.g. MessageBox messages. That's stuff which is just shown temporaily or changes from time to time.
Here the resources table in project properties is your default language store. To add new languages do:
Create a folder named "Resources" in your project
In this folder add a new element. Choose Resourcefile. Name it Resources.de-DE.resx or Resources.fr-FR.resx for whatever language you want to create.
Add the stuff in here and also in the default resources
Tip: String.Format is your friend. Create texts with placeholders like: "{0} of {1} records". Keep in mind that other languages maybe need some text before the placeholder "{0}", so you should avoid Position & String.Format(" of {0} records", CounterText, DataTable.Rows.Count).
Now, how to use it:
Switching between cultures
To test localization I added a ComboBox to my test form (in the release in your options dialog) and filled it with these Items (in the properties window):
(machine default)
English
German
French
Gaelic
Then the according event handler looks like:
Imports System.Threading
Private Sub CboLanguage_SelectedIndexChanged(sender As Object, e As EventArgs) Handles CboLanguage.SelectedIndexChanged
If CboLanguage.SelectedItem Is Nothing Then Return
Dim Infos = {"", "en-US", "de-DE", "fr-FR", "gd-GB"}
If CboLanguage.SelectedIndex = 0 Then
Thread.CurrentThread.CurrentUICulture = CultureInfo.CurrentCulture
Else
Thread.CurrentThread.CurrentUICulture = New CultureInfo(Infos(CboLanguage.SelectedIndex))
End If
End Sub
The list contains Gaelic only to test a language I really dont have in my resources, which means to test the switch to the default resource. In production you would delete the languages you don't have resources for from the list and of course the (machine default) entry.
The two default cultures
Note that there are two default cultures when you start an application.
We have the default culture which is defined in your project properties about which I told before. But there is another one which is the computers language the app is running on. There are two scenarios how an app will find the culture it will use.
You start the app, the app determines the computers culture and searches in your resources for the according ressources (this will happen every time a resource is needed). If the according resource is found, it will be used, otherwise the resource from the default resources will be used.
The user sets another language in the options, this is saved in whatever settings strategy (My.Settings, app.config, INI-File, Registry, Isolated Storage, Database), you load it at Initialization/Start of your app and set the proper Threads UICulture, then this Culture will take place where the computers was. From now on resources will be searched in the according Resource file and analog, if not found, in the Default resources.
Getting the texts
Regarding the forms you have to do nothing. Depending on your Thread.CurrentThread.CurrentUICulture the according texts will be shown.
As soon as you add a resource text to the default resources or one of your other language resource files VS will create a new property with the name of your resource text in then Resources.Designer.vb which is located in the "My Project" folder.
This means, if you create a resource text named "New" and with value "&New" in the default resources or the Resources.en-US.resx file, the same with value "&Nouveau" in the Resources.fr-FR.resx file and "&Neu" in the Resources.de-DE.resx file, you can access the text with:
My.Resources.New
That's it. Depending on your Thread.CurrentThread.CurrentUICulture you will get the text in the according language.
Transfer between Assemblies
Note that the My.Resources namespace is declared with the Friend modifier which means it applies only to the local assembly. And every assembly has its own My.Resources namespace.
If you do not Multithreading and every DLL has its own localized texts in the same cultures, you have no problem. But if you want to manage texts in a DLL from your main assembly then you need to have a reference of the main ResourceManager in your DLL. This looks like in your DLL class:
Public Class MyPublicDLLClass
' Members
Public Property ResourceManager As Resources.ResourceManager = Nothing
' Other members
End Class
And in the calling assembly:
Dim DLLObject = New MyPublicDLLClass With {
.ResourceManager = My.Resources.ResourceManager
}
Then you could do this to have access to the resources of the calling assembly:
Function GetText(Item As String) As String
Return GetText(Item, Nothing)
End Function
Function GetText(Item As String, [Default] As String) As String
Dim Def As String = If([Default], Item)
If ResourceManager IsNot Nothing Then
Dim Text = ResourceManager.GetString(Item)
Return If(String.IsNullOrEmpty(Text), Def, Text)
Else
Return Def
End If
End Function

How to rename a method name from Intellisense?

Does anyone know how? I'm trying to make a method that the intellisense shows another name instead of the name you ta the method. example:
The code looks like this:
Public Shared Sub Anything(ByVal blablabla....)
(...)
End Sub
And I want to auto-complete the VisualStudio menu be like:
Async
Alalala
Anymethod
Anything - Like these
Basic
On mouse hover:
instead of...
Public Shared Sub Namespace.Anything(ByVal blablablbal....)
This is the summary...
put it:
Anything <expression>
This is the summary...
-or-
Sub Anything
This is the summary...
Any ideas? thx...
I believe you're talking about XML Comments. Without comments:
With comments:
And Intellisense then also displays the parameter information as you get to each parameter:
Sooooo, I hope my answer isn't too vague. I'd love to help you igure it out either way.
Basically - you have to override the XML Documentation that Intellisense uses, in order to MAP a new MethodName to a Differently-Named method within your assembly.
Intellisense uses an XML Skeleton to define the different properties of what you see. That Skeleton typically looks something like this:
<CodeElement type="Function">
<Template>
<summary/>
<param/>
<returns/>
<remarks/>
</Template>
<CompletionList>
<exception cref=""/>
<include file="" path=""/>
<param name=""/>
<remarks/>
<returns/>
<summary/>
</CompletionList>
</CodeElement>
You can use this template to generate the Intellisense data you're looking for. In real-life application, this would look something like this:
''' <summary>
'''
''' </summary>
''' <param name="str"></praram>
''' <returns></returns>
''' <remarks></remarks>
Function Anything(ByVal str As String) As String
Try
...
Now, this template is Editable
The Visual Basic compiler generates an XML document for your assembly with all the XML comments defined in the code. The compiler will also resolve symbols used in cref, permission, and name attributes, as well as file references in include elements.
The generated file doesn't show your commented members hierarchically. Rather, it is a flat list. It includes a unique ID string for each definition that allows the comments to be mapped back to their definitions in code (see Code Snippet below).
In this case, the string is M:Namespace.Anything(System.String). M stands for method, Namespace specifies the path, System.String the parameter type.
THIS is where you would override the NAME of the Method that INTELLISENSE sees, without effecting the actual Method itself.
<?xml version="1.0" ?>
<doc>
<assembly>
<name>AnyLib</name>
</assembly>
<members>
...
<member name="M:Namespace.Anything(System.String)">
<summary>Does something</summary>
<param name="str">str to pass into method</param>
<returns>A string</returns>
</member>
...
</members>
</doc>
You can generate the XML documentation file using either the command-line compiler or through the Visual Studio interface. If you are compiling with the command-line compiler, use options /doc or /doc+. That will generate an XML file by the same name and in the same path as the assembly. To specify a different file name, use /doc:file.
If you are using the Visual Studio interface, there's a setting that controls whether the XML documentation file is generated. To set it, double-click My Project in Solution Explorer to open the Project Designer. Navigate to the Compile tab. Find "Generate XML documentation file" at the bottom of the window, and make sure it is checked. By default this setting is on. It generates an XML file using the same name and path as the assembly
In short - yes, it is possible, but very cumbersome and makes re-using your code very difficult. Check this link for a more detailed overview:
https://msdn.microsoft.com/en-us/magazine/dd722812.aspx

Cannot access App_Code logic from Controller

I'm just starting with my first ASP.Net WebApi 2 project. I have a file like this:
/AppCode/Remits.vb
Public Class Remits
Public Shared Function GetBoolean(id As Integer, id2 As Integer) As Boolean
Return True
End Function
End Class
This function will need to be called from all my controllers. The function checks that the two ID parameters are valid (done using some extensive database logic which is removed for brevity).
/Controllers/TestController.vb
Namespace Controllers
Public Class TestController
Inherits ApiController
<Route("Test/{id}")>
Public Function [Get](id As Int32) As IHttpActionResult
Dim b As Boolean = myNs.Remits.GetBoolean(123, 456) ' <-- error (see below)
Return Ok(b)
End Function
End Class
End Namespace
This won't even compile, with the error
'myNs' is not declared. It may be inaccessible due to its protection
level
Why is the namespace and/or class not accessible from within a controller? Or, what am I doing wrong?
Update
Anything inside App_Code is not recognised by files in the former directories. I've updated the question to reflect this. Here is a (tiny) sample project highlighting the problem.
http://1drv.ms/1kqjwTX
When you add classes to App_Code it is set to build action "Content". You need to change this to "Compile".
So:
Do not use App_Code, rename that folder
Change any class files to "Compile" build action.
For #2. Select the file in Solution Explorer and in the properties window (if you don't have this open by default right click the file and choose properties) there is an option called Build Action. Change that to Compile.

zmi: duplicates in add products list

I have an old-style product with several classes. In the class, I have defined the meta_type and I have also registered them in __init__.py, i.e.:
def initialize(context):
context.registerClass(
ClassA.ClassA,
permission = "Add ClassA",
constructors = (ClassA.manage_addClassA,
ClassA.manage_addClassA),
icon = 'www/images/ClassA.gif'
)
This worked fine until I updated my Zope from 2.9 to 2.13. Now in the zmi, in the "Add Products" dropdown list, these meta_types are shown two times each.
I tried to track the source of this error:
ObjectManager.py, filtered_meta_types
ObjectManager.py, all_meta_types
getattr(Products, 'meta_types', ())
Now I don't know where to look next :)
It's only a nuisance, it does not cause any problems in the functionality of the product. Maybe I should update it to a new-style zope product, but I'm curious where this error comes from.
You need to remove your <five:registerPackage /> registration from the configure.zcml file, because your legacy package is using the Products. namespace.
The Products. namespace has always been auto-loaded; the initialize(context) function is implicitly being loaded for that namespace since before it was an official namespace.
When Zope started to support packages outside of the Products. namespace, however, the decision was made (wisely) to make registration explicit instead, and the <five:registerPackage /> was introduced to let you use the old registration hook if you still needed it.
In your case, however, that means your initialize() function is being called twice; once because it is a Products. package, and once because you explicitly registered it.

DataContext Doesn't Exist in Dynamic Data Project?

This is really annoying...and I know it is something extremely simple...
1. I create a new Dynamic Data project.
2. I add a LINQ-to-SQL class and drag and drop some tables onto the class.
3. I open the global.asax.vb and uncomment the line:
DefaultModel.RegisterContext(GetType(YourDataContext), New ContextConfiguration() With {.ScaffoldAllTables = True})
I remove YourDataContext and replace it with the DataContext from my LINQ-to-SQL class:
DefaultModel.RegisterContext(GetType(NorthwindDataContext), New ContextConfiguration() With {.ScaffoldAllTables = True})
I then try to debug/build/etc. and receive the following error:
Type 'NorthwindDataContext' is not defined
Why is it not defined? It seems like its not recognizing I created the DBML file.
This is a very strange issue and I am still not sure that I have entirely fixed the issue. But, here is what I think I have learned:
1. If you are creating a New Project in VS2010 you must place the LINQ-to-SQL DBML file in the root project directory.
2. If you are creating a new Web Site in VS2010 you must place the LINQ-to-SQL DBML file in a newly created App_Code directory.
3. If you place it in the wrong place, scrap the project and start over - it doesn't seem to work even if you move the files to the correct location or recreate them in the correct location.
Dave.
Does your project compile? I am guessing you are missing the namespace to your data context. Something like this:
model.RegisterContext(typeof(MyApp.MyNamespace.NorthwindDataContext),
new ContextConfiguration() { ScaffoldAllTables = true });