Mais

Adicionando STAThread a um suplemento ESRI


Estou um pouco preso no momento, tenho um c # addin, que como parte de um dos métodos, contém o objeto SavefileDialog. O código também contém um Timer. As etapas para o usuário são:

  • Clique no mapa
  • Geometria desenhada
  • Mapa atualizado
  • O cronômetro espera 5 segundos (tempo suficiente para o mapa ser atualizado)
  • É exibida uma caixa de diálogo que faz uma pergunta ao usuário
  • Se eles selecionarem sim, o objeto SaveFileDialog aparecerá.

Parece que, antes de o SaveFileDialog aparecer, recebo o seguinte erro

O segmento atual deve ser definido para o modo de compartimento de segmento único (STA) antes que as chamadas OLE possam ser feitas. Certifique-se de que sua função principal tenha STAThreadAttribute marcado nela.

Onde, em meu código, coloco o atributo [STAThread], pois não tenho uma função principal. O addin é uma barra de ferramentas e um botão.


Em vez de usar um Timer (que é executado em um thread separado), que tal usar os eventos embutidos: IActiveViewEvents.ViewRefreshed (iniciar a atualização) e então IActiveViewEvents.AfterDraw (terminar a atualização). Se você ouvir o AfterDraw com a fase de desenhoesriViewDrawPhase.esriViewForegroundouesriViewDrawPhase.esriViewAll(talvez seja necessário experimentar um pouco para ver o que funciona para você), então o evento é disparado depois que o mapa é atualizado, independentemente de quanto tempo leva.

Tenha um booleano (globalmente) que diga 'Chamei esta atualização' ou a caixa de diálogo para salvar aparecerá sempre que a visualização for atualizada:

// bool global pIveCalledThisRefresh = false; // no formulário load private void Form1_Load (object sender, EventArgs e) {IApplication m_application = ArcMap.Application; IMxDocument gDoc = (IMxDocument) m_application.Document; IScreenDisplay gScreenDis = gDoc.ActiveView.ScreenDisplay; ((IActiveViewEvents_Event) gDoc.FocusMap) .AfterDraw + = new IActiveViewEvents_AfterDrawEventHandler (My_AfterDraw); } // no evento do botão private void Go_Click (object sender, EventArgs e) {pIveCalledThisRefresh = true; // faça algumas coisas e atualize o mapa pIveCalledThisRefresh = false; // atualizar de volta ao normal} // após o evento de desenho void My_AfterDraw (IDisplay Display, fase esriViewDrawPhase) {if (! pIveCalledThisRefresh) return; // pula as atualizações, a menos que você as tenha causado if (phase == esriViewDrawPhase.esriViewForeground) // experimente isso, veja qual fase você deseja {if (MyDialog.ShowDialog () == System.Windows.Forms.DialogResult.OK) {// salve}}}

Se você ainda quiser esperar após a conclusão da atualização, obtenha a hora como um objeto DateTime, adicione alguns segundos e compare com DateTime.Compare () com umDoEvents ()no circuito:

void My_AfterDraw (IDisplay Display, fase esriViewDrawPhase) {if (! pIveCalledThisRefresh) return; // pula as atualizações, a menos que você as tenha causado if (phase == esriViewDrawPhase.esriViewForeground) // experimente isso, veja qual fase você deseja {pSaveTime = DateTime.Now; pSaveTime.AddSeconds (5.0); // 5 segundos arbitrários fazem {// um loop de nada System.Windows.Forms.Application.DoEvents (); // permite que os outros processos façam suas coisas} while (DateTime.Compare (pSaveTime, DateTime.Now)> 0); if (MyDialog.ShowDialog () == System.Windows.Forms.DialogResult.OK) {// faça seu salvamento}}}

Tudo isso é executado no mesmo encadeamento, o que irá aliviar o seu objeto Esri que cruza os encadeamentos, o que não é permitido porque todos os objetos Esri não são seguros para encadeamentos.


Se você estiver usando System.Timers.Timer ou System.Threading.Timer, o código que é disparado pelo cronômetro ocorre em um thread de trabalho. Este tópico não será marcado como STAThread e, portanto, não pode usar o SaveFileDialog.

Você pode usar System.Windows.Forms.Timer, que é síncrono e no thread de IU principal, ou usar Control.Invoke para executar seu código no thread principal (ou IDispatcher.Invoke se estiver usando WPF em vez de WinForms).


Quando o STAThreadAttribute é aplicado, ele altera o estado de compartimento do segmento atual para um segmento único. Sem entrar em uma grande discussão sobre COM e threading, este atributo garante o mecanismo de comunicação entre o thread atual e outros threads que podem querer se comunicar com ele via COM. Quando você usa o Windows Forms, dependendo do recurso que está usando, ele pode estar usando a interoperabilidade COM para se comunicar com os componentes do sistema operacional. Bons exemplos disso são a área de transferência e as caixas de diálogo de arquivo.

Dito de outra forma, a comunicação com os componentes do sistema operacional, como a área de transferência, é feita por meio do COM e isso requer a alteração do estado de compartimento do thread usando um thread STA.


O atributo STAThread é usado para indicar ao COM que o aplicativo deve usar um single-threaded apartment (STA). Esta afeta apenas objetos COM, e só é necessário porque certas caixas de diálogo que usam COM (como FileOpenDialog) precisam que o modelo de threading correto seja especificado.

Pelo que sei, muito pouco (ou nenhum) desenvolvimento de jogos fará uso de COM, com exceção das caixas de diálogo do Windows Forms e, portanto, isso não terá nenhum impacto, é simplesmente necessário para essas caixas de diálogo.

Se você fizer uso de objetos COM, então você maio deseja examinar os modelos de threading COM para entender completamente a implicação dessa configuração; no entanto, a versão resumida é que em um STA todos os métodos COM serão executados em um único thread (o thread de interface do usuário, neste caso), mesmo que tenha sido invocado em outro fio.

Atualizar: O DirectX está disponível como um conjunto de objetos COM, no entanto, não tenho certeza de qual modelo de threading eles exigem.


  1. Você tem uma propriedade estática pública UserName que pode ser definida por qualquer pessoa, em qualquer lugar em seu código. Você não detalhou o uso desta propriedade.

Se for o formulário de logon que deve atualizá-lo e nunca mais será atualizado, defina a classe de programa para definir este campo.

Se for outra classe que atualiza este campo, talvez esta propriedade não pertença aqui.

Você tem algum código duplicado, então deve haver uma maneira de fazer melhor.

Do meu ponto de vista, você está fazendo muitas coisas diferentes em seu método Main, talvez você possa colocar essas operações em métodos separados: Init, Login, Start.

Você coloca o Presenter para MainForm na propriedade Tag, que eu acho que pertence a uma propriedade específica. Dessa forma o MainForm pode controlar quem tem acesso a ele, caso contrário qualquer objeto que tenha acesso ao MainForm pode recuperar o Tag e consequentemente o Presenter. Outro problema é que a propriedade Tag é um objeto, então você terá que convertê-la toda vez que quiser utilizá-la.


1 resposta 1

Não sou veementemente contra o uso de regiões, mas sou contra eles lado de dentro de métodos. Eu considero isso um cheiro de código definitivo. As regiões dentro dos métodos devem ser extraídas em seus próprios métodos e chamadas. Por exemplo

Agora, se uma mudança precisa ser feita em como qualquer uma dessas coisas é configurada, você pode ir diretamente para esse método em vez de pesquisar em centenas de linhas de código a parte da configuração que está procurando.

OK. Deixe-me reformular isso. Métodos de extração. Métodos de extração em todos os lugares. Seu método keyboardHook_KeyDown tem 59 linhas. Eu tenho que rolar de várias vezes para ver todo o método. Somos humanos, não podemos segurar muito em nossas cabeças de uma vez. Extrair métodos não é apenas remover a repetição de nosso código. É para tornar os métodos individualmente responsáveis ​​e pequenos o suficiente para serem compreendidos à primeira vista. Se quisermos o detalhe, mergulhamos nos métodos menores. Caso contrário, teremos uma visão geral rápida do que está acontecendo sem nos preocupar com os detalhes.

Sua classe de configurações é muito melhor nesse aspecto, mas há outros problemas com ela.

  • Todos os métodos são estáticos, então você também pode tornar a própria classe estática. Não faz sentido jamais haver exemplos disso, não existe estado.
  • É sem dúvida preferível usar uma configuração XML em vez do Registro do Windows. Consulte O que é App.config em C # .Net e como faço para usá-lo? No entanto, não há nada de errado em usar o registro. Pode ser considerado uma questão de preferência.

Se você decidir manter o registro, deverá alterar o local para o qual está registrando suas chaves.

Dê uma olhada nas outras chaves em Software. Observe como todos (a maioria) deles estão na forma de Company NameOfSoftware . Você deve seguir esta convenção. Isso torna infinitamente menos provável que você encontre um conflito de "namespace" com alguma outra parte do software. Como este é um software de código aberto, recomendo usar seu nome de usuário do github.


Interface ISpatialReferenceFactory3

Fornece acesso a membros que criam datums verticais ou sistemas de coordenadas.

Disponibilidade de produto

Quando usar

ISpatialReferenceFactory3 fornece métodos para suporte do sistema de coordenadas verticais e converte referências espaciais de baixa precisão existentes em uma referência espacial de alta precisão (ou vice-versa).

Membros

Descrição
ConstructHighPrecisionSpatialReference Constrói uma referência espacial de alta precisão com base em uma referência espacial existente. xy / z / mDoubler é o número de vezes que a respectiva precisão deve ser duplicada. Um valor zero encontrará a duplicação máxima para essa precisão.
ConstructLowPrecisionSpatialReference Construa uma referência espacial de baixa precisão com o mesmo fator de escala da entrada de alta precisão, mas com uma extensão de domínio diferente. Se a extensão de domínio calculada não puder cobrir a extensão de dados especificada, um erro será retornado.
CreateDatum Cria um datum predefinido.
CreateESRISpatialReference Cria um sistema de referência espacial e o define a partir do buffer ESRISpatialReference especificado.
CreateESRISpatialReferenceFromPRJ Cria uma referência espacial a partir de uma sequência PRJ.
CreateESRISpatialReferenceFromPRJFile Cria uma referência espacial a partir de um arquivo PRJ.
CreateESRISpatialReferenceInfo Cria um sistema de referência espacial e o define a partir do buffer ESRISpatialReference especificado.
CreateESRISpatialReferenceInfoFromPRJ Cria uma referência espacial a partir de uma sequência PRJ.
CreateESRISpatialReferenceInfoFromPRJFile Cria uma referência espacial a partir de um arquivo PRJ.
CreateGeographicCoordinateSystem Cria um sistema de coordenadas geográficas predefinido.
CreateGeoTransformation Cria uma transformação predefinida entre sistemas de coordenadas geográficas.
CreateParameter Cria um parâmetro predefinido.
CreatePredefinedAngularUnits Cria uma lista de unidades angulares predefinidas.
CreatePredefinedDatums Cria uma lista de uma lista de datums predefinidos.
CreatePredefinedGeographicTransformations Cria uma lista de transformações geográficas predefinidas.
CreatePredefinedLinearUnits Cria uma lista de unidades lineares predefinidas.
CreatePredefinedPrimeMeridians Cria uma lista de meridianos principais predefinidos.
CreatePredefinedProjections Cria uma lista de projeções predefinidas.
CreatePredefinedSpheroids Cria uma lista de esferóides predefinidos.
CreatePredefinedVerticalCoordinateSystems Cria uma lista de sistemas de coordenadas verticais predefinidos.
CreatePredefinedVerticalDatums Cria uma lista de datums verticais predefinidos.
CreatePrimeMeridian Cria um meridiano principal predefinido.
CreateProjectedCoordinateSystem Cria um sistema de coordenadas projetadas predefinido.
CreateProjection Cria uma projeção predefinida.
CreateSpatialReference Cria uma referência espacial predefinida de um srID.
CreateSpheroid Cria um esferóide predefinido.
CreateUnit Cria uma unidade de medida predefinida.
CreateVerticalCoordinateSystem Cria um sistema de coordenadas verticais predefinido a partir de uma enumeração ou código de ID.
CreateVerticalCoordinateSystemFromESRISpatialReference Cria um sistema de coordenadas verticais a partir de seu formato de string.
CreateVerticalDatum Cria um datum vertical predefinido a partir de uma enumeração ou código de ID.
ExportESRISpatialReferenceInfoToPRJFile Exporta uma referência espacial para um arquivo PRJ.
ExportESRISpatialReferenceToPRJFile Exporta uma referência espacial para um arquivo PRJ.
GeoTransformationDefaults Retorna uma lista de transformações geográficas padrão.
GetPredefinedGeographicTransformations Retorna uma lista de transformações geográficas predefinidas.

Interfaces herdadas

Interfaces Descrição
ISpatialReferenceFactory2 Fornece acesso a membros que criam diferentes tipos de componentes de referência espacial.
ISpatialReferenceFactory Fornece acesso a membros que criam diferentes tipos de componentes de referência espacial.

CoClasses que implementam ISpatialReferenceFactory3

Observações

O ISpatialReferenceFactory3 interface possui dois métodos que são úteis ao trabalhar com referências espaciais de baixa e alta precisão. ConstructHighPrecisionSpatialReference cria uma referência espacial de alta precisão a partir de uma referência espacial de baixa precisão. Usar este método garantirá que os valores das coordenadas se encaixem exatamente na nova malha de grade mais densa. Cada interseção da grade original é uma interseção da nova grade. ConstructLowPrecisionSpatialReference criará uma referência espacial de baixa precisão a partir de uma referência de alta precisão existente. Você pode exigir que o novo valor de resolução seja mantido, geralmente às custas da extensão do domínio XY.

Ao usar essa interface em um aplicativo de console, você deve marcar a rotina como single-threaded. Uma classe pode ser marcada como single-threaded adicionando o atributo [STAThread] imediatamente antes da declaração de sua rotina.

[STAThread]
static void Main (string [] args)
<
>

Ao usar essa interface em um aplicativo de console, você deve marcar a rotina como single-threaded. Uma classe pode ser marcada como single-threaded adicionando o atributo [STAThread] imediatamente antes da declaração de sua rotina.


Qual é a melhor maneira de construir uma fábrica usando NInject?

Estou bastante confortável com a injeção de dependência usando NInject no MVC3. Enquanto trabalhava em um aplicativo MVC3, desenvolvi um Controller Creation Factory customizado usando NInject, de forma que qualquer controlador criado terá dependências injetadas nele por meio deste Controller Factory.

Agora que estou começando a desenvolver um aplicativo do Windows, quero usar a injeção de dependência em todo o aplicativo. ou seja, cada objeto deve ser criado por meio de NInject, para facilitar o teste de unidade. Por favor, me oriente para garantir que todos os objetos criados devem passar pelo NInject Factory apenas.

Por exemplo, se em qualquer formulário de janela no evento Button_Click eu escrevo:

e TestClass tem qualquer dependência de, digamos, ITest, então ela deve ser resolvida automaticamente. Eu sei que posso usar:

Mas acho tedioso fazer isso toda vez que quero criar um objeto. Ele também força o desenvolvedor a criar o objeto de uma maneira particular. Pode ser melhorado?

Posso ter um repositório central para a criação de objetos e, em seguida, cada criação de objetos usará automaticamente esse repositório?


Você pode criar um arquivo de manifesto e definir o aplicativo para exigir privilégios administrativos. Isso acionará o prompt do usuário do UAC com a tela esmaecida quando seu aplicativo for executado sem a necessidade de qualquer código de sua parte.

Consulte MSDN para obter os detalhes sangrentos:

Este arquivo pode ser criado usando qualquer editor de texto. O arquivo de manifesto do aplicativo deve ter o mesmo nome do arquivo executável de destino com uma extensão .manifest.

Há algumas coisas que eu mudaria em seu código para torná-lo mais claro ou combinar bem com os padrões.

Eu mudaria o nome desse método para RunningAsAdmin e a variável Principle deve ser minúscula para corresponder ao esquema de nomenclatura padrão para variáveis ​​de camelCasing.

também eu mudaria a estrutura de sua instrução if then no Método Principal.

em vez de negar o método booleano, eu o deixaria funcionar, você está verificando de uma forma ou de outra de qualquer maneira, então a negação parece errada

Precisamos Elevate para retornar um booleano? pelo que posso ver, ele realmente não faz nada com o valor verdadeiro / falso.

Vamos apenas torná-lo um método vazio e deixá-lo fazer as coisas e sair de lá, sem instruções de retorno.


. executable.exe paste -a --million "argumentos"

Variações de comando: colar | comando

Cole o conteúdo da área de transferência do Windows na entrada do comando de comando especificado.

colar | comando | grampo

Cole o conteúdo da área de transferência do Windows na entrada do comando de comando especificado e copie sua saída para a área de transferência do Windows.

Por exemplo, cole | sort | clipe classifica o conteúdo da área de transferência do Windows. Requer: clipe

colar> nome do arquivo

colar> exemplo.txt

Cola o conteúdo da área de transferência do Windows no arquivo de nome de arquivo especificado.

colar | classificar> nome do arquivo

Classifica o conteúdo da área de transferência do Windows e salva a saída classificada no arquivo de nome de arquivo especificado.


1 resposta 1

Pelo que entendi, um aplicativo raiz de composição e simples onde a composição ocorre - de preferência, o mais próximo possível do ponto de entrada. Esta classe não encapsula uma "raiz de composição", ela meramente envolve um IKernel e, por fim, priva a raiz de composição real de toda a flexibilidade e poder que o Ninject oferece, literalmente forçando Um Caminho Verdadeiro de acessar o kernel.

A questão é que a mera existência dessa classe levanta um Grande Bandeira Vermelha: porque existe, e como está armazenando uma instância de kernel de nível de tipo (estático), é extremamente fácil ter esta linha de código em qualquer lugar do projeto:

. e você o tem, na classe ApplicationShellController - e uma vez que você expôs métodos estáticos, você nem mesmo precisa distribuí-lo como uma dependência . mas é um localizador de serviço no entanto, porque ApplicationShellController tem um dependência no tipo CompositionRoot - ou seja, a raiz não está mais na raiz!

Parece que o bit LINQ é mais complicado do que deveria ser. ResolveAll & ltT & gt retorna um IEnumerable & ltT & gt, portanto, Cast & ltT & gt é redundante. Então o Select não é projetando qualquer coisa, e Onde poderia ser o predicado para FirstOrDefault:

Se a intenção é retornar 1 controlador, porém, e a intenção é que só haja sempre 1 controlador para lidar com o caminho fornecido, então você deve usar .SingleOrDefault em vez de .FirstOrDefault, para tornar a intenção mais clara (e o método lançaria uma exceção se houvesse dois ou mais controladores para um determinado caminho, em vez de retornar, bem, algum controlador aleatório de qualquer tipo que a coisa parece lhe dar).

Se eu entendi sua arquitetura corretamente, você realmente não precisa de uma nova instância de um controlador toda vez que esse método é chamado.

Mas é isso que você está recebendo.

Acho que o construtor ApplicationShellController deve ser assim:

O Ninject vê a dependência IEnumerable & ltIDocumentController & gt como uma oportunidade para uma multi-ligação: ele resolverá automaticamente uma instância de tudo que se liga a IDocumentController e a passará.

Você não precisa do atributo [Injetar] aqui. Não use atributos [Injetar]. Imagine que você tem sua raiz de composição em um conjunto completamente diferente - o único conjunto em sua solução que precisa uma referência a Ninject, é a montagem com a raiz da composição. Ter uma dependência de Ninject em qualquer outro lugar deve ser proibido, seu código deve ser escrito de uma maneira completamente agnóstica de contêiner IoC: ter um atributo [Inject] em seu código de negócios sugere que seu código de negócios é capaz de funcionar com um IKernel em qualquer dado tempo, e você não quer dar essa impressão - seja apenas porque com o tempo, o código será mantido por alguém (sim, futuro você é outra pessoa!), e é sempre uma boa ideia enviar uma mensagem clara para isso futuro mantenedor, que o contêiner IoC vive e morre em seu próprio cantinho e não viaja para lugar nenhum.

Claro que existem exceções. por exemplo, você pode querer usar a extensão Ninject.Logging e injetar pelo construtor um ILogger em qualquer tipo habilitado para registro.

Então, eu removeria a classe CompositionRoot e moveria o IKernel direto para o ponto de entrada, referenciando o convenções extensão e faça algo assim:

A extensão de convenções pode fazer muito mais do que ligar automaticamente todas as interfaces em uma única instrução - isso foi apenas para ilustrar o ponto que você não precisa chamar manualmente .Bind & ltISmurf & gt () para ligar explicitamente cada smurf na vila.

Por exemplo, você pode impor uma convenção de nomenclatura em um determinado namespace, de modo que qualquer coisa que não termine com "Controller" não se vincule a IDocumentController, por exemplo.

Resolvendo para IApplicationShellView, Ninject chegará ao tipo ApplicationShellView. E há um problema:

Não há nada para injetar! A visão está completamente acoplada ao controlador de concreto! Mas espere, por que o modo de exibição precisa saber sobre seu controlador pai?

Você precisa inverter a dependência aqui (Inversão de controle certo?), e em vez de ter a vista Perguntando o controlador para faça alguma coisa, tenha a vista contar o controlador deveria estar fazendo algo porque o usuário clicou em um botão.

WinForms é tudo sobre eventos que eu usaria um evento.

Inverter o relacionamento entre o controlador e a visão acaba de quebrar a dependência circular: agora tudo que o controlador precisa fazer é tratar os eventos da visão!


Assista o vídeo: cara mengisi menu pendidik di aplikasi dpw fkdt addin Jabar (Outubro 2021).