Este artigo começará com os conceitos básicos de globalização e localização de recursos no Asp.net e explicará as etapas e métodos para alcançar a globalização e localização no Asp.net1.1 e Asp.net2.0.
um. Conceitos básicos
1. Por que localizar recursos?
Nosso site pode ser visualizado por pessoas de vários países e regiões ao redor do mundo, e as pessoas de cada país e região têm seu próprio idioma e características culturais. Tomemos como exemplo a nossa grande pátria, a China Continental, que utiliza o chinês simplificado, enquanto Hong Kong, Macau e Taiwan utilizam o chinês tradicional. Além disso, cada país possui diferentes formatos de expressão para moeda, números, calendário e outras informações. Nosso país usa principalmente o formato de ano, mês e dia, enquanto os Estados Unidos usam o formato de mês, dia e ano. Existem muitas diferenças como essa, então não vou dar muitos exemplos. Para proporcionar uma melhor experiência ao usuário do nosso site, devemos fornecer uma solução global. Desde que o usuário selecione seu idioma e região, o site exibirá as informações da página de acordo com seu idioma e hábitos culturais. localização.
2.
Os nomesde cultura, cultura fixa, cultura não específica e cultura específica
seguem o padrão RFC 1766 e estão no formato " Nome da cultura Identificador de cultura Idioma - País zh-CN 0x0804 Chinês - China zh-TW 0x0404 Chinês - Taiwan zh-CHS 0x0004 Chinês simplificado zh-CHT 0x
7C |
04 | Chinês Tradicional | |
en | 0x0009 | Inglês |
en | - | US |
0x0409 | Inglês - Estados Unidos en - | GB | 0x0809
uz | - | UZ - Cyrl |
0x0843 | Uzbeque (Cirílico) - Uzbequistão uz-UZ-Latn 0x0443 | Uzbeque (Latim ) - Uzbequistão |
Culturas fixas não são sensível à cultura. Você pode usar a string vazia ("") para especificar uma cultura fixa por nome ou pelo identificador de cultura 0x007F. Instâncias de cultura invariáveis são representadas pela propriedade InvariantCulture da classe CultureInfo. As culturas fixas estão associadas apenas à língua inglesa, e não a qualquer país. Ele pode ser usado em quase qualquer método no namespace "globalizado" que exija cultura. Se o seu programa realiza comparações de strings ou operações de mudança de maiúsculas e minúsculas, ele deve usar um InvariantCulture para garantir que, independentemente da cultura definida pelo sistema, o comportamento será feito de acordo com a cultura fixa do idioma inglês representada pelo InvariantCulture. No entanto, uma cultura fixa deve ser utilizada apenas por processos que exijam resultados independentes da cultura (como serviços de sistema, caso contrário, os resultados que obtém podem ser linguisticamente incorretos ou culturalmente inadequados); Exemplo: CultureInfo Invc = New CultureInfo("");
CultureInfo Invc = CultureInfo.InvariantCulture Essas duas linhas de código têm o mesmo efeito, o objetivo é obter uma instância de cultura invariável.
Por exemplo, agora você deseja executar o método dateTime.ToString() em uma instância DateTime dateTime. Na verdade, esse método usa CurrentCulture do seu thread atual como cultura padrão e converte a instância de data no formato de string correspondente com base nesta cultura. Portanto, se não precisarmos dele para executar a operação ToString de acordo com o thread ou cultura do sistema neste momento, devemos usar este método dateTime.ToString("G", CultureInfo.InvariantCulture) ou dateTime.ToString("G" , DateTimeFormatInfo.InvariantInfo) .
Uma cultura neutra é aquela associada a um idioma, mas não a um país. Uma cultura específica é aquela associada a um determinado idioma e a um determinado país. Por exemplo, “en” é uma cultura neutra, enquanto “en-US” é uma cultura específica. Observe que "zh-CHS" (chinês simplificado) e "zh-CHT" (chinês tradicional) são neutros em termos culturais.
As culturas possuem uma estrutura hierárquica, ou seja, o pai de uma cultura específica é uma cultura não específica, e o pai de uma cultura não específica é uma Cultura Invariável. A propriedade Parent da classe CultureInfo retornará a cultura não específica associada à cultura específica. Se o recurso específico da cultura não existir no sistema ou estiver indisponível de outra forma, o recurso não específico da cultura será usado; se o recurso não específico da cultura também estiver indisponível, o recurso incorporado no assembly principal será usado;
3. Implementar tipos, propriedades e métodos comuns para localização
A classe CultureInfo representa informações sobre uma cultura específica, incluindo o nome da cultura, sistema de escrita e calendário usado, bem como informações sobre operações comuns (como formatação de datas e datas). classificação de strings). Fornece acesso a objetos de informação específicos da cultura. Geralmente há duas maneiras de instanciar a classe CultureInfo, como segue:
CultureInfo cultura = CultureInfo.
CultureInfo culture = new CultureInfo(name);
A diferença entre os dois é que usando o primeiro método, você só pode criar uma instância CultureInfo de uma cultura fixa ou de uma cultura específica. Se name for uma sequência vazia, uma instância de cultura fixa será criada. Se name for uma cultura não específica, uma instância CultureInfo específica da cultura padrão associada a name será criada. O segundo método é criar uma instância CultureInfo da cultura especificada por nome, que pode ser fixa, não específica ou específica da cultura.
A propriedade CurrentCulture da classe Thread é usada para obter ou definir a cultura do thread atual. Deve ser definido para uma cultura específica. Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); Se Thread.CurrentThread.CurrentCulture = new CultureInfo("en ");
A propriedade CurrentUICulture da classe Thread é usada para obter ou definir a cultura atual usada pelo gerenciador de recursos para localizar recursos específicos da cultura em tempo de execução. O gerenciador de recursos aqui pode ser associado à classe ResourceManger.
Thread.CurrentThread.CurrentUICulture = new CultureInfo("pt");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
A classe ResourceManger pode encontrar recursos específicos da cultura, fornecer recursos substitutos quando não existem recursos localizados e oferecer suporte à serialização de recursos. O construtor ResourceManager comumente usado é public ResourceManager(string, Assembly). Seu significado é inicializar uma nova instância da classe ResourceManager, que procura arquivos de recursos de um determinado Assembly usando o nome raiz especificado. O chamado nome raiz é, por exemplo, o nome raiz de um arquivo de recurso denominado "MyResource.en-US.resources" é "MyResource". Um namespace pode ser adicionado à expressão do nome raiz, como "MyWebSite.Resource.UserFolder.MyResource". O Assembly pode ser o Assembly onde está localizada a página que precisa chamar o arquivo de recurso, como typeof(MyPage).Assembly. O método GetString da classe ResourceManager é usado para obter o valor da chave especificada no arquivo de recurso. Exemplo: Quando a propriedade CurrentUICulture do thread for definida, siga o método a seguir.
ResourceManager rm = new ResourceManager("itens", Assembly.GetExecutingAssembly());
String str = rm.GetString("welcome");
Se você deseja obter recursos de acordo com a cultura especificada, escreva o seguinte:
ResourceManager rm = new ResourceManager("itens", Assembly.GetExecutingAssembly());
CultureInfo ci = Thread.CurrentThread.CurrentCulture;
String str = rm.GetString("bem-vindo",ci);
dois. Para implementar a localização de recursos no Asp.net1.1,
você deve primeiro criar uma pasta Resource no projeto do site WebTest e armazenar arquivos de recursos comuns a todo o projeto nesta pasta. Por exemplo, criamos os três arquivos de recursos a seguir: MyResource.en.resx, MyResource.en-US.resx, MyResource.zh-CN.res. Existem dois pares de valores-chave em cada arquivo de recurso, os valores-chave são Estado e Endereço. Chame o arquivo de recursos na página MyPage.aspx que precisa usar o arquivo de recursos, conforme mostrado abaixo:
Thread.CurrentThread.CurrentCulture= CultureInfo.CreateSpecificCulture("zh-CN");
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
ResourceManager rm = novo ResourceManager("WebTest.Resource.MyResource", typeof (MyPage).Assembly);
Label1.Text = rm.GetString("Estado");
Label2.Text = rm.GetString("Address");
Bem, neste momento Label1 e Label2 exibirão "estado" e "endereço" de acordo com os regulamentos no arquivo MyResource.zh-CN.resx. O método de localização acima é o mais básico e simples. Existem alguns problemas ocultos aqui.
1. A forma de obter a cultura padrão do usuário
é através das configurações na opção "Propriedades" -> "Idioma" do navegador do usuário.
CultureInfo cultureInfo = CultureInfo.CreateSpecificCulture(Request.UserLanguages[0]);
Thread.CurrentThread.CurrentCulture = culturaInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
Geralmente, CurrentCulture e CurrentUICulture são definidos para ter a mesma cultura. Claro, eles também podem ser diferentes. Por exemplo, você especifica que CurrentCulture é en-US e CurrentUICulture é zh-CN. O efeito disso é que a moeda, a data e outras informações na página são exibidas no formato inglês americano, e o gerenciador de recursos obterá o conteúdo que precisa ser obtido do arquivo de recursos do MyResource.zh-CN.resx arquivo.
Caso a página do seu site não forneça a função para os usuários selecionarem um idioma, então ela será exibida de acordo com a cultura definida pelo navegador do usuário por padrão, então você pode colocar o código acima no método Application_BeginRequest do Global.asax. arquivo cs. Dessa forma, toda vez que um usuário fizer uma solicitação de uma página, nosso programa primeiro definirá a cultura.
2. Lembrar as configurações de localidade do usuário
As configurações ou seleções de localidade do navegador podem ser lembradas durante a sessão. Porém esta operação não pode ser realizada no método Application_BeginRequest no arquivo Global.asax.cs, pois a sessão ainda está indisponível naquele momento. Se o seu site não fornecer a função para os usuários selecionarem um idioma, não será necessário lembrar as configurações regionais do usuário. Basta configurá-lo no método Application_BeginRequest no arquivo Global.asax.cs conforme descrito acima. desempenho. Isso evita principalmente conflitos em que o usuário altera repentinamente as configurações de idioma no navegador no meio do caminho, mas o site ainda exibe o conteúdo da página ao usuário de acordo com a cultura armazenada na sessão.
Se você mencionou a função de permitir que o usuário selecione um idioma, obviamente precisará usar uma sessão no programa de página para registrar a seleção de cultura do usuário. Porque cada solicitação do cliente para o segmento do servidor, o segmento do servidor abrirá um novo thread para processamento e resposta. Caso o seu programa não se lembre da seleção do cliente, ele responderá apenas com a cultura padrão.
3. Como o gerenciador de recursos encontra os arquivos de recursos correspondentes para uma cultura específica?
Ao realizar uma operação de valor, ou seja, ao executar o método GetString da classe ResourceManager, o gerenciador de recursos irá procurar o arquivo de recurso correspondente de acordo com a propriedade CurrentUICulture do thread atual. Existem várias situações:
(1).Por exemplo, se a cultura correspondente ao CurrentUICulture for en-US, primeiro verifique se MyResource.en-US.resx existe, retire o valor dela, se não existir; se MyResource.en.resx existe.
(2). Por exemplo, a cultura correspondente à CurrentUICulture atual é en, porque en é uma cultura não específica, então primeiro descubra se o arquivo de recurso MyResource.en-US.resx da cultura específica en-US está associado a ele. por padrão existe. Em caso afirmativo, obtenha o valor dele; se não existir, verifique se MyResource.en.resx existe.
(3).Por exemplo, se a cultura correspondente ao CurrentUICulture for en-GB, primeiro encontre o arquivo de recurso MyResource.en-GB.resx. Se não existir, verifique se MyResource.en.resx existe. ele existe, obtenha o valor dele; se não existir, verifique se o arquivo de recurso MyResource.en-US.resx da cultura específica padrão en-US associada a en existe. existir neste momento, mas MyResource.en-CA existir, o programa ainda lançará uma exceção informando que o arquivo de recurso apropriado não pode ser encontrado.
Portanto, podemos resumir que quando o thread atual CurrentUICulture corresponde a uma cultura específica, o gerenciador de recursos primeiro procura o arquivo de recursos correspondente a essa cultura específica. Se não for encontrado, ele procurará seu arquivo de recursos que não seja de cultura. ainda não foi Encontre-o e procure o arquivo de recursos da cultura padrão associado à sua associação não específica da cultura. Quando o CurrentUICulture do thread atual corresponde a uma cultura não específica, o gerenciador de recursos primeiro verifica se o arquivo de recursos específico da cultura padrão correspondente à cultura não específica existe. Se não existir, verifique se o arquivo de recursos correspondente à cultura não específica existe. -cultura específica existe e lança uma exceção se também não existir.
4. Como lidar com culturas que não oferecem suporte de localização?
Se o site não fornecer arquivos de recursos correspondentes para dar suporte à cultura padrão do usuário, o CurrentUICulture de seu thread atual deverá ser convertido para a cultura padrão do seu site, como en-US ou zh-CN. Existem duas oportunidades de conversão:
Uma é que, ao obter Request.UserLanguages[0], compare-o com a predefinição de cultura suportada no arquivo de configuração. Se for confirmado que não é compatível, defina imediatamente CurrentUICulture como a cultura padrão.
A segunda é usar a estrutura try catch para capturar a exceção MissingManifestResourceException ao usar o método GetString de ResourceManager para obter o valor. No tratamento de exceção, primeiro defina CurrentUICulture para a cultura padrão e, em seguida, use GetString para obter o valor novamente.
5. Defina a cultura padrão e uiCulture do site por meio de Web.config
Conforme mostrado acima: É especificado que a cultura padrão do site é en-US (deve ser uma cultura específica aqui), e a uiCulture é zh-CN.
Claro, você também pode defini-lo página por página na tag Page de cada página: <@Page Culture="zh-CN" UICulture="en">. Independentemente de como o web.config é definido aqui, a página será exibida de acordo com as configurações da tag Page.
três. Implementando a localização de recursos no Asp.net2.0
O Asp.net2.0 fornece métodos de implementação mais diversos para localização de recursos. Vou me concentrar aqui nas diferenças do Asp.net1.1.
1. Definir a cultura padrão e uiCulture do site por meio do Web.config
já foi discutido no Asp.net1.1 usando o arquivo web.config para definir a cultura do site, mas no Asp.net2.0 é mais flexível. Normalmente, você deseja que todas as páginas do seu site estejam em conformidade com a mesma cultura. Basta atribuir um valor "auto" para todo o site aos atributos UICulture e Culture (culture) do elemento globalization em web.config conforme mostrado abaixo. Observe que esse valor "auto" não é aceito no Asp.net1.1.
Além das configurações automáticas, você também pode especificar a cultura padrão de um site para Asp.net:
Depois que a globalização for configurada no web.config, seu aplicativo não precisará escrever nenhum código. O CurrentUICulture e CurrentCulture do thread obterão as configurações de cultura de acordo com os valores dos atributos uiCulture e culture definidos no elemento de globalização. Se a globalização não estiver configurada, CurrentUICulture e CurrentCulture do thread serão padronizados como en-US.
2. Use o arquivo Web.config para rastrear a seleção regional do usuário
No Asp.net1.1, os sites que fornecem seleção regional geralmente usam sessões para registrar a seleção do usuário, de modo que toda vez que o usuário fizer uma solicitação ao site, todos. o conteúdo de exibição é localizado de acordo com a cultura escolhida pelo usuário. Outro método fornecido no Asp.net2.0 é usar o arquivo web.config para rastrear a seleção de cultura do usuário.
Você pode oferecer suporte à identificação anônima da cultura de um usuário adicionando uma propriedade de perfil baseada em string chamada LanguagePreference ao seu arquivo web.config. Observe que o atributo enabled do elemento anônimaIdentification deve ser "true", caso contrário a função de identificação anônima não estará disponível.
Abaixo explicarei como programar o atributo LanguagePreference no Asp.net2.0. Primeiro, você pode escrever uma classe PageBase, que herda de System.Web.UI.Page e serve como classe base para todas as classes de páginas do site. O objetivo disso é realmente muito simples: extrair alguns processos de processamento comuns em cada página e colocá-los na classe base para reduzir a duplicação de código e melhorar a capacidade de manutenção. Em seguida, escreva o seguinte código na classe PageBase: protected override void InitializeCulture()
{
base.InitializeCulture();
string LanguagePreference = ((ProfileCommon)this.Context.Profile).LanguagePreference;
//Quando o usuário visitar este site pela primeira vez e Profile.LanguagePreference estiver vazio, identifique a configuração de idioma do navegador do usuário
if(string.IsNullOrEmpty(LanguagePreference))
{
if (this.Context.Request.UserLanguages ! = nulo)
{
LanguagePreference = this.Context.Request.UserLanguages[0];
((ProfileCommon)Context.Profile).LanguagePreference = LanguagePreference;
}
}
outro
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(LanguagePreference);
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(LanguagePreference);
}
}
O método InitializeCulture da classe System.Web.UI.Page foi adicionado recentemente no Asp.net2.0. Ele define Culture e UICulture para o thread atual. O ciclo de vida da página foi projetado para que o método InitializeCulture seja executado antes do Init e Load da página. No código acima, primeiro use ((ProfileCommon)this.Context.Profile).LanguagePreference para obter o valor do atributo de perfil LanguagePreference atual e determinar se ele está vazio, ou seja, se as configurações culturais foram salvas para o usuário; . Se estiver vazio, a cultura preferida do usuário é obtida do cabeçalho Http e salva via ((ProfileCommon)Context.Profile).LanguagePreference = LanguagePreference;. Se não estiver vazio, significa que as configurações de cultura do usuário foram salvas, então use esta cultura para definir as propriedades CurrentUICulture e CurrentCulture do thread atual.
Se
{
base.InitializeCulture();
string LanguagePreference = ((ProfileCommon)this.Context.Profile).LanguagePreference
if(!string.IsNullOrEmpty(LanguagePreference))
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(LanguagePreference);
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(LanguagePreference);
}
outro
{
((ProfileCommon)Context.Profile).LanguagePreference = Thread.CurrentThread.CurrentCulture.Name;
}
}
Se o site fornecer uma função que permite aos usuários selecionar uma cultura, como colocar uma lista de seleções de idiomas na página mestra do site, a escolha de cultura do usuário poderá ser lembrada por meio da seguinte instrução:
protegido void lstLanguage_SelectedIndexChanged(objeto remetente,EventArgs e)
{
if (lstLanguage.SelectedValue != "Auto") //A opção padrão é Auto
{
Profile.LanguagePreference = lstLanguage.SelectedValue;
}
outro
{
Perfil.LanguagePreference = null;
}
Response.Redirect(Request.Url.AbsolutePath);
}
Preste atenção na linha de código Response.Redirect(Request.Url.AbsolutePath); pois o código de manipulação de eventos é executado após Page_Load, se você deseja que a página mude rapidamente, você deve realizar uma operação de redirecionamento.
Ao usar arquivos de recursos para criar arquivos de recursos globais no site
em Asp.net2.0
, o VS.Net2005 criará automaticamente uma pasta App_GlobalResources especificamente para armazenar arquivos de recursos globais.O chamado arquivo de recurso global é um arquivo de recurso usado por vários arquivos de páginas ou páginas mestras no site. Suponha que criemos arquivos chamados MyResource.resx e MyResource.zh-cn.resx. No programa, podemos usar o seguinte código para obter o valor no arquivo de recurso: this.lblCountry.Text = Resources.MyResource.Country;
Onde Country é a chave no arquivo de recursos. Obviamente, isso é muito mais fácil do que obter valores de arquivos de recursos no Asp.net 1.1.
Há duas questões a serem observadas aqui: primeiro, ao criar um grupo de arquivos de recursos com o mesmo nome raiz, devem ser criados arquivos sem identificação de cultura, como MyResource.resx, outros como MyResource.en-gb. zh-cn.resx são criados conforme necessário. Se MyResource.resx não for criado e apenas MyResource.zh-cn.resx for criado, MyResource não aparecerá no namespace Resources no código acima, portanto, a compilação do código acima não poderá ser aprovada. MyResource.resx deve armazenar o conteúdo do idioma padrão do site caso um arquivo de recurso localizado correspondente ao CurrentUICulture do thread atual não possa ser encontrado ou o valor da chave correspondente não possa ser encontrado no arquivo de recurso localizado. Asp.net é baseado na chave do arquivo MyResource.resx. Se a chave Country não existir em MyResource.resx, mas a chave Country existir em MyResource.zh-cn.resx, o código acima também reportará um erro. ao compilar. Em segundo lugar, o Asp.net não reportará nenhuma exceção quando não conseguir encontrar o recurso localizado na área correspondente. Ele obterá automaticamente o valor do arquivo MyResource.resx, mas não alterará o CurrentUICulture do thread atual.
Ao criar arquivos de recursos locais no site, o VS.Net2005 criará automaticamente uma pasta App_LocalResources especificamente para armazenar arquivos de recursos locais. Os chamados arquivos de recursos locais são arquivos de recursos usados para um único arquivo de página no site. Seu método de nomenclatura geralmente é Default.aspx.resx e Default.aspx.zh-cn.resx. Agora adiciono três chaves Language, lblNavigation.Text e lblNavigation.ForeColor no arquivo de recursos padrão. Entre eles, defini azul para lblNavigation.ForeColor de Default.aspx.resx e vermelho para lblNavigation.ForeColor de Default.aspx.zh-cn.resx. Existem duas maneiras de obter conteúdo de arquivos de recursos locais em Default.aspx no arquivo de paginação:
(1).
(2).
Tenha cuidado ao usar o símbolo $ ao usar o primeiro método. Usar o segundo método é mais flexível e permite definir valores para muitas propriedades do controle de uma só vez.
Ainda há questões a serem observadas aqui: o arquivo de recurso local padrão da página deve ser criado, como Default.aspx.resx é obrigatório e Default.aspx.zh-cn.resx é obrigatório. Se você não criar um arquivo de recurso local padrão, mas usar arquivos de recursos locais na página, ao usar o primeiro método para vinculação, ocorrerá um erro de compilação ao usar o segundo método para vinculação, nenhum erro de compilação ocorrerá. ocorre um erro, mas as configurações dessas propriedades não têm efeito, como se não tivessem sido escritas.
4. Exibindo imagens localizadas
Exibir imagens localizadas também é um novo recurso do Asp.net2.0. No Asp.net2.0, os arquivos de recursos não estão limitados a combinações de pares de valores-chave do tipo string. Ele pode salvar vários tipos de arquivos. Use este recurso para localizar imagens. Na verdade, as chamadas imagens localizadas nada mais são do que colocar imagens preparadas para diferentes regiões em diferentes arquivos de recursos localizados. Por exemplo, coloque LitwareSlogan.jpg em MyResource.resx e coloque LitwareSlogan.cn.jpg em MyResource.zh-cn.resx.
Quando arquivos de recursos globais de diferentes versões localizadas contêm versões localizadas de arquivos de imagem, você pode personalizar um arquivo manipulador chamado MyLocalImage.ashx para carregá-lo condicionalmente com base na preferência de idioma do usuário. O código é mostrado a seguir.
Método de chamada na página:
Como escrever o manipulador de MyLocalImage.ashx:
public class MyLocalImage: IHttpHandler
{
public void ProcessRequest (contexto HttpContext)
{
context.Response.ContentType = "imagem/png";
string LanaguageReference = ((ProfileCommon)context.Profile).LanguagePreference;
if (!string.IsNullOrEmpty(LanaguageReference))
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(LanaguageReference);
}
Bitmap bm = Resources.Litware.LitwareSlogan;
Imagem MemoryStream = new MemoryStream();
bm.Save(imagem,ImageFormat.Png);
context.Response.BinaryWrite(image.GetBuffer());
}
}
A classe do manipulador customizado definida em MyLocalImage.ashx usa uma lógica semelhante à que você viu anteriormente no método InitializeCulture customizado para inicializar as configurações de CurrentUICulture para o encadeamento atual antes de recuperar o arquivo de imagem do arquivo de recurso global. Você pode estar se perguntando por que o CurrentUICulture do thread atual foi definido na classe base da página e precisa ser redefinido aqui. Isso ocorre porque o thread aqui não é o mesmo thread processado na classe base. Depois que o manipulador personalizado inicializar corretamente as configurações de CurrentUICulture, ele poderá acessar o arquivo de imagem por meio da classe de recurso fortemente tipada de MyResource.resx. Depois é só escrever os bits do arquivo de imagem no fluxo de resposta HTTP.