Pergunta

Antecedentes: Estou usando WPF e C # (3.5) e estou trabalhando em um aplicativo que permite que um usuário visualiza um formulário / janela / usercontrol que já faz parte de um compilado de montagem. Quando eles vêem isso, eles devem ser capazes de clicar em qualquer controle (botões, caixas de texto, até mesmo etiquetas), um pouco editor de pop-up deve aparecer pelo controle onde podem em seguida, digite uma dica de ferramenta, HelpID, etc., para esse controle.

O longo e curto do que: Eu preciso imitar uma visão projeto básico no WPF. O que significa que precisa fazer pelo menos o seguinte:

  • Coloque usercontrol / janela de um determinado conjunto de (nenhum problema)
  • Instantiate que usercontrol / janela do (nenhum problema)
  • Limpar todos os EventHandlers subscritas para todos os seus controles
  • Atribua minha própria "ShowEditorPopup" EventHandler a cada controle (não deve ser um problema)

Primeiro, se alguém tiver sugestões sobre uma rota mais fácil ou melhor tomar, por favor me avise. (Aparentemente não há DesignHost tipo de componente para WPF (como eu li .NET 2 tem), de modo que está fora.)

Eu estou preso no item em negrito - limpando quaisquer EventHandlers subscritas. Depois de cavar em torno de alguns e ficar em refletor, eu vim acima com este pedaço fresco de código perigoso (aqui, eu só estou tentando obter todos os EventHandlers para um único botão chamado someButton definido no XAML):

<Button Name="someButton" Click="someButton_Click"/>

Aqui está o código (você pode executá-lo a partir do someButton_Click eventHandler se você quiser):

public void SomeFunction()
{
// Get the control's Type
Type someButtonType = ((UIElement)someButton).GetType();

// Dig out the undocumented (yes, I know, it's risky) EventHandlerStore
// from the control's Type
PropertyInfo EventHandlersStoreType =  
        someButtonType.GetProperty("EventHandlersStore",  
        BindingFlags.Instance | BindingFlags.NonPublic);

// Get the actual "value" of the store, not just the reflected PropertyInfo
Object EventHandlersStore = EventHandlersStoreType.GetValue(someButton, null);

// Get the store's type ...
Type storeType = EventHandlersStore.GetType();

// ... so we can pull out the store's public method GetRoutedEventHandlers
MethodInfo GetEventHandlers =  
        storeType.GetMethod("GetRoutedEventHandlers",  
        BindingFlags.Instance | BindingFlags.Public);

// Reflector shows us that the method's sig is this:
// public RoutedEventHandlerInfo[] GetRoutedEventHandlers(RoutedEvent routedEvent);

// So set up the RoutedEvent param
object[] Params = new object[] { ButtonBase.ClickEvent as RoutedEvent };
// I've also seen this for the param, but doesn't seem to make a difference:
// object[] Params = new object[] { someButton.ClickEvent };

// And invoke it ... and watch it crash!
GetEventHandlers.Invoke(someButton, Params);
}

Ele funciona até o Invoke, que retorna: Objeto não corresponde tipo de destino (ou seja, os meus parâmetros ou objeto de destino estão bagunçados). Eu descobri que você pode resolver isso com:

GetEventHandlers.Invoke(Activator.CreateInstance(someButton.GetType()), Params);
// Also doesn't work...

Quando eu definir um relógio no GetEventHandlers MethodInfo, isso parece ótimo, ele só não gosta do que eu estou passando isso quando eu chamar o Invoke.

Eu sinto que estou no último passo de como obter uma lista dos manipuladores RoutedEvent (como boa GetInvocationList de idade (), que não funciona para RoutedEvents WPF, aparentemente). De lá, ele vai ser bastante simples para remover esses manipuladores de cada controle e ter uma forma eventless, que pode então adicionar os meus próprios eventos para.

Qualquer pistas? Novamente, se há uma maneira melhor / mais fácil de fazer a tarefa global, deixe-me saber:)

Foi útil?

Solução

E se você tomar uma abordagem diferente. Você poderia chamar EventManager.RegisterClassHandler () para todos os eventos, e em seguida no seu manipulador (assumindo que o evento é para um controle na superfície de design, e não faz parte do seu UI) marcar o evento como tratado. Isso deve impedir que ele seja encaminhado para os controles em sua superfície de design, desde manipuladores de classe são chamados antes de manipuladores de eventos padrão.

Você ainda precisará usar o reflexo para obter a lista de eventos fornecidos por um controle, mas pelo menos desta forma você não estaria usando reflexão para remover os eventos. Além disso, se a montagem de carregar também registra um manipulador de classe (provavelmente antes de seu código faz), a deles seria chamado em primeiro lugar, mas eu acho que isso seria uma ocorrência rara.

Outras dicas

Se você usar GetEventHandlers.Invoke(EventHandlersStore , Params) parece funcionar bem e não falhar.

Usando o código de cima eu fiz isso:

// Get the control's Type
Type controlViewType = ((UIElement)control).GetType();

// Dig out the undocumented (yes, I know, it's risky) EventHandlerStore
// from the control's Type
PropertyInfo EventHandlersStoreType =
controlViewType.GetProperty("EventHandlersStore",
BindingFlags.Instance | BindingFlags.NonPublic);

// Get the actual "value" of the store, not just the reflected PropertyInfo
Object EventHandlersStore = EventHandlersStoreType.GetValue(tree, null);
var miGetRoutedEventHandlers 
EventHandlersStore.GetType().GetMethod("GetRoutedEventHandlers", 
BindingFlags.Public | BindingFlags.Instance);
RoutedEventHandlerInfo[] res =   
(RoutedEventHandlerInfo[])miGetRoutedEventHandlers.Invoke(EventHandlersStore, 
new object[] { CheckedTreeViewItem.CheckedEvent });

Uma vez que você tem que, em seguida, o único problema é que agora você tem a informação método, então você precisa para obter uma instância para o objeto que implementa esse método. Normalmente manipuladores de eventos são definidos na janela ou objeto Page. Então, para obtê-lo: var-mãe = VisualTreeHelper.GetParent (controlo); while (! (controle é Janela) &&! (controle é a página)) { pai = VisualTreeHelper.GetParent (pai); }

E com essa instância você pode então invocar a dividi evento:

res.[0].Handler.Method.Invoke(parent, new object[] { control, new RoutedEventArgs() }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top