É possível definir o código por trás de um dicionário de recursos no WPF para manipulação de eventos?

147

É possível definir o código por trás de um dicionário de recursos no WPF. Por exemplo, em um controle de usuário de um botão, você o declara em XAML. O código de manipulação de eventos para o clique do botão é feito no arquivo de código atrás do controle. Se eu fosse criar um modelo de dados com um botão, como escrever o código do manipulador de eventos para ele, clique no botão dentro do dicionário de recursos.

Crippeoblade
fonte
1
A maneira correta de fazer isso é usar um comando, que também permite ativar e desativar o botão, enquanto você pode fazê-lo da maneira que algumas respostas sugeriram que ele cheira a um hack.
Aran Mulholland

Respostas:

209

Acho que o que você está perguntando é que deseja um arquivo code-behind para um ResourceDictionary. Você pode fazer isso totalmente! De fato, você faz da mesma maneira que para uma janela:

Digamos que você tenha um ResourceDictionary chamado MyResourceDictionary. No seu arquivo MyResourceDictionary.xaml, coloque o atributo x: Class no elemento raiz, da seguinte maneira:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    x:Class="MyCompany.MyProject.MyResourceDictionary"
                    x:ClassModifier="public">

Em seguida, crie um código por trás do arquivo chamado MyResourceDictionary.xaml.cs com a seguinte declaração:

namespace MyCompany.MyProject
{
    partial class MyResourceDictionary : ResourceDictionary
    { 
       public MyResourceDictionary()
       {
          InitializeComponent();
       }     
       ... // event handlers ahead..
    }
}

E você terminou. Você pode colocar o que quiser no código por trás: métodos, propriedades e manipuladores de eventos.

== Atualização para aplicativos do Windows 10 ==

E caso você esteja jogando com o UWP, há mais uma coisa a ter em atenção:

<Application x:Class="SampleProject.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:rd="using:MyCompany.MyProject">
<!-- no need in x:ClassModifier="public" in the header above -->

    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>

                <!-- This will NOT work -->
                <!-- <ResourceDictionary Source="/MyResourceDictionary.xaml" />-->

                <!-- Create instance of your custom dictionary instead of the above source reference -->
                <rd:MyResourceDictionary />

            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>

</Application>
ageektrapped
fonte
7
Como um adendo à resposta do ageektrapped: Certifique-se de colocar o nome completo da sua codebehind class no atributo x: Class. x:Class="MyCompany.MyProject.MySubFolder1.MyResourceDictionary"Caso contrário, se você simplesmente colocar x: Class = "MyResourceDictionary", o analisador xaml não encontrará sua classe.
Viggity 11/02/09
29
Certifique-se de fornecer um construtor padrão na classe parcial codebehind e verifique se ele chama InitializeComponent (). (No meu caso eu estava usando MEF para exportar o dicionário de recursos.)
Scott Whitlock
4
Snippet de código atualizado para comentários votados. Eu senti que era necessário completar a resposta; um erro comum. Eu fiz isso agora mesmo :) Reverta se você não gosta. Obrigado pela resposta.
Gishu 19/05/10
2
Note-se que (pelo menos em wp8.1) isso não é mais válido e que você tem que criar um usercontrol personalizado que suas referências ResourceDictionary
Jared
9
Você também precisará definir a Ação de Compilação no arquivo XAML do ResourceDictionary como "Página", caso contrário, a chamada InitializeComponent () não será compilada. (Arquivos ResourceDictionary XAML são geralmente definido como "Recursos" por padrão.)
user1454265
9

Eu discordo de "ageektrapped" ... usar o método de uma classe parcial não é uma boa prática. Qual seria o propósito de separar o Dicionário da página?

Em um code-behind, você pode acessar o elemento ax: Name usando:

Button myButton = this.GetTemplateChild("ButtonName") as Button;
if(myButton != null){
   ...
}

Você pode fazer isso no método OnApplyTemplate se desejar conectar-se aos controles quando o controle personalizado for carregado. O OnApplyTemplate precisa ser substituído para fazer isso. Essa é uma prática comum e permite que seu estilo fique desconectado do controle. (O estilo não deve depender do controle, mas o controle deve depender de um estilo).

Phobis
fonte
7
Phobis Acho que o objetivo de separar o Dicionário da página é sobre a reutilização e legibilidade da página principal xaml. A solução acima funcionou para mim também.
22610 cleftheris
5

Gishu - embora isso possa parecer uma prática "geralmente não encorajada" Aqui está uma das razões pelas quais você pode querer fazê-lo:

O comportamento padrão das caixas de texto quando elas obtêm o foco é que o cursor seja colocado na mesma posição em que estava quando o controle perdeu o foco. Se você preferir em todo o aplicativo que, quando o usuário guia para qualquer caixa de texto que todo o conteúdo da caixa de texto tenha sido destacado, a adição de um manipulador simples no dicionário de recursos funcionaria.

Qualquer outro motivo pelo qual você queira que o comportamento padrão da interação do usuário seja diferente do comportamento pronto para uso parece ser um bom candidato para um código atrasado em um dicionário de recursos.

Concordo totalmente que qualquer coisa específica da funcionalidade do aplicativo não deve estar em um código atrás de um dicionário de recursos.

Pete Maher
fonte
0

XAML é para a construção de gráficos de objetos que não contêm código.
Um modelo de dados é usado para indicar como um objeto de usuário personalizado deve ser renderizado na tela ... (por exemplo, se for um item da caixa de listagem) o comportamento não faz parte da área de especialização de um modelo de dados. Redesenhar a solução ...

Gishu
fonte
Conclusão: Você recomendaria usar o recurso dic com código atrasado ou não? Eu nunca usei, estou duvidando.
Shimmy Weitzhandler 30/03/09
1
Eu não - para mim, não parece certo. Um dicionário deve retornar valores para chaves específicas. No caso do OP, agrupar o código com o modelo de dados. Prefiro tentar uma abordagem diferente. Use o modelo de comando, por exemplo. Preciso de mais detalhes sobre o problema do OP para recomendar uma solução diferente.
Gishu
1
Discordo completamente. Com o MVVM, há um cenário em que o código atrasado é extremamente útil: desenvolver propriedades anexadas. Faça com que funcione com o código por trás e, em seguida, mova-o para uma propriedade anexada. Isso é muito mais rápido do que simplesmente desenvolver a propriedade anexada do zero, a menos que você tenha um cérebro do tamanho de Manhattan.
Contango 6/06