Mas como fazer um binding do byte[] para um controle Image? Eu nem tinha trabalhado com imagens ainda, que dirá enviar uma pro serviço. Vamos então à como eu consegui chegar na solução.
Primeiro, como fazer binding? Bom, a Image possui um atributo Source, que pode receber um objeto BitmapImage. Como eu recebo um objeto byte[], eu precisava instanciar uma BitmapImage desta forma. Como meu cadastro não tinha nenhuma imagem ainda, tive que implementar a carga primeiro. Aí começou o drama. O Silverlight implementa um modelo de sandbox para execução no browser, que ao menos pra me atrapalhar, funcionou redondinho. Não posso chamar métodos de System.IO por exemplo. Então veio a calhar o IsolatedStorageFile. Descobri o caminho no file system, xcopy umas imagens pra lá, e voilá! Já posso ver umas imagens na tela.
Agora, não seria prático acessar somente a isolated storage. Então, próximo passo. O Silverlight tem um componente OpenFileDialog, que permite acessar qualquer imagem do sistema de arquivos, semelhante ao que se tem em HTML também. A segurança está no fato de que é o usuário que escolhe o caminho do arquivo. Basicamente qualquer arquivo está na lista-negra. Arquivos na isolated storage e arquivos selecionados pelo usuário são inseridos na lista-branca.
Fui descobrir sem-querer querendo que a dialog impõe outra restrição de segurança: dialogs somente podem ser iniciadas pelo usuário. Fazendo debug, eu encontrei este erro. O presenter exibe a dialog. (Nota para mim mesmo: rever isto, pois o presenter não deve usar um recurso puramente da view). Mas quem chama o presenter, através de evento, é o handler do Button.Click, e o call stack está lá pra não me deixar mentir. O problema, vejam só, é que coloquei um breakpoint na linha do ShowDialog(), isto faz o Silverlight se confundir. Botei o breakpoint depois e tudo funcionou.
A carga da imagem é feita assim:
// ClientePresenter: IClientePresenter void view_CarregarLogo() { OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "Imagens (*.png)|*.png"; // nunca coloque breakpoint na linha a seguir!! if (dlg.ShowDialog() == true) { using (FileStream file = dlg.File.OpenRead()) { byte[] bytes = new byte[file.Length]; file.Read(bytes, 0, bytes.Length); view.LogoDoCliente = bytes; } } } // EditCliente: IClienteView public byte[] LogoDoCliente { get { return ClienteSelecionado.Logo; } set { AlterarLogoNaTela(value); // modificar entidade ClienteSelecionado.Logo = value; } } private void AlterarLogoNaTela(byte[] value) { if (value != null && value.Length != 0) { var bmp = new BitmapImage(); using (MemoryStream strm = new MemoryStream(value)) { bmp.SetSource(strm); } // Image no XAML LogoImage.Source = bmp; } else { LogoImage.Source = null; } }
Só falta enviar para o WCF. Funcionou de cara, pois carregava a imagem para um byte[] e usava isto para atualizar tanto a tela como a propriedade na entidade. Mas dava problema a mandar imagens maiores de aproximadamente 16KB. Aí entra em cena os limites do WCF. Veja um exemplo de como configurar o .config:
<binding name="BasicHttpBindingConfig" maxBufferSize="655360" maxReceivedMessageSize="655360"> <readerQuotas maxArrayLength="65536"/> </binding> <serviceBehaviors> <behavior name=""> <dataContractSerializer maxItemsInObjectGraph="65536"/> </behavior> </serviceBehaviors>
Resumo:
- Não coloque breakpoint na linha que exibe uma dialog
- Não chame métodos com o atributo SecurityCriticalAttribute
- Não acesse o sistema de arquivos sem solicitar o arquivo via file dialog
- Não acesse um serviço WCF sem configurar reader quotas, MaxItemsInObjectGraph
- Carregue os bytes da imagem para um MemoryStream, instancie um BitmapImage, e chame SetSource com a stream
- O BitmapImage aceita uma stream com um PNG, já com BMP não funcionou
- Use um padrão arquitetural como MVP, é muito bom mesmo
Nenhum comentário:
Postar um comentário