Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Uma propriedade anexada é um conceito XAML. As propriedades anexadas normalmente são definidas como uma forma especializada de propriedade de dependência. Este tópico explica como implementar uma propriedade anexada como uma propriedade de dependência e como definir a convenção do acessador necessária para que sua propriedade anexada seja utilizável no XAML.
Pré-requisitos
Presumimos que você entenda as propriedades de dependência da perspectiva de um consumidor de propriedades de dependência existentes e que tenha lido a visão geral das propriedades de dependência. Você também deve ter lido a visão geral das propriedades anexadas. Para seguir os exemplos neste tópico, você também deve entender o XAML e saber como escrever um aplicativo básico do Windows Runtime usando C++, C#ou Visual Basic.
Cenários para propriedades anexadas
Você pode criar uma propriedade anexada quando há um motivo para ter um mecanismo de configuração de propriedade disponível para classes diferentes da classe de definição. Os cenários mais comuns para isso são o layout e o suporte a serviços. Exemplos de propriedades de layout existentes são Canvas.ZIndex e Canvas.Top. Em um cenário de layout, elementos que existem como elementos filhos de elementos controladores de layout podem expressar individualmente os requisitos de layout para seus elementos pai, definindo cada um um valor de propriedade que o pai estabelece como uma propriedade anexada. Um exemplo do cenário de suporte a serviços na API do Windows Runtime é o conjunto de propriedades anexadas do ScrollViewer, como ScrollViewer.IsZoomChainingEnabled.
Aviso
Uma limitação existente da implementação XAML do Windows Runtime é que você não pode animar uma propriedade anexada personalizada.
Registrando uma propriedade anexada personalizada
Se você estiver definindo a propriedade anexada estritamente para uso em outros tipos, a classe em que a propriedade está registrada não precisará derivar de DependencyObject. Mas você precisa ter o parâmetro de destino para acessadores usar DependencyObject se seguir o modelo típico de ter sua propriedade anexada também como uma propriedade de dependência, para que você possa usar o repositório de propriedades de backup.
Defina sua propriedade anexada como uma propriedade de dependência declarando uma propriedade públicaestáticade leitura do tipo DependencyProperty. Você define essa propriedade usando o valor retornado do método RegisterAttached . O nome da propriedade deve corresponder ao nome da propriedade anexada que você especificar como o parâmetro de nomeRegisterAttached, com a cadeia de caracteres "Property" adicionada ao final. Essa é a convenção estabelecida para nomear os identificadores das propriedades de dependência em relação às propriedades que elas representam.
A área principal em que a definição de uma propriedade anexada personalizada difere de uma propriedade de dependência personalizada é na forma como você define os acessadores ou wrappers. Em vez de usar a técnica de wrapper descrita nas propriedades de dependência personalizadas, você deve também fornecer métodos estáticos GetPropertyName e SetPropertyName como acessadores para a propriedade anexada. Os acessadores são usados principalmente pelo analisador XAML, embora qualquer outro chamador também possa usá-los para definir valores em cenários não XAML.
Importante
Se você não definir os acessadores corretamente, o processador XAML não poderá acessar sua propriedade anexada e qualquer pessoa que tentar usá-la provavelmente receberá um erro do analisador XAML. Além disso, as ferramentas de design e codificação geralmente dependem das convenções "*Property" para nomear identificadores quando encontram uma propriedade de dependência personalizada em uma montagem referenciada.
Accessors
A assinatura do acessador GetPropertyName deve ser essa.
public static
valueTypeGetPropertyName(DependencyObject target)
Para o Microsoft Visual Basic, é isso.
Public Shared Function Get
PropertyName(ByVal target As DependencyObject) As valueType)
O objeto de destino pode ser de um tipo mais específico em sua implementação, mas deve derivar de DependencyObject. O valor retornado ValueType também pode ser de um tipo mais específico em sua implementação. O tipo básico Object é aceitável, mas muitas vezes você desejará que sua propriedade anexada imponha a segurança de tipo. O uso da tipagem nas assinaturas de getter e setter é uma técnica recomendada para segurança de tipo.
A assinatura do acessador SetPropertyName deve ser esta.
public static void Set
PropertyName(DependencyObject target ,valueType value)
Para o Visual Basic, é isso.
Public Shared Sub Set
PropertyName(ByVal target As DependencyObject, ByVal value AsvalueType)
O objeto de destino pode ser de um tipo mais específico em sua implementação, mas deve derivar de DependencyObject. O objeto value e seu valueType podem ser de um tipo mais específico em sua implementação. Lembre-se de que o valor desse método é a entrada proveniente do processador XAML ao encontrar sua propriedade vinculada no código XAML. Deve haver conversão de tipo ou suporte de extensão de marcação existente para o tipo que você usa, para que o tipo apropriado possa ser criado a partir de um valor de atributo (que, em última análise, é apenas uma cadeia de caracteres). O tipo de objeto básico é aceitável, mas muitas vezes você desejará mais segurança de tipo. Para fazer isso, coloque a verificação de tipo nos métodos de acesso.
Observação
Também é possível definir uma propriedade anexada em que o uso pretendido é por meio da sintaxe do elemento de propriedade. Nesse caso, você não precisa de conversão de tipo para os valores, mas precisa garantir que os valores pretendidos possam ser construídos em XAML. VisualStateManager.VisualStateGroups é um exemplo de uma propriedade anexada existente que dá suporte apenas ao uso de elementos de propriedade.
Exemplo de código
Este exemplo mostra como registrar uma propriedade de dependência (usando o método RegisterAttached), assim como os métodos de acesso Get e Set, para uma propriedade anexada personalizada. No exemplo, o nome da propriedade anexada é IsMovable. Portanto, os acessadores devem ser nomeados GetIsMovable e SetIsMovable. O detentor da propriedade anexada é uma classe de serviço chamada GameService, que não possui sua própria interface do usuário; seu único propósito é fornecer serviços de propriedades anexadas quando a propriedade GameService.IsMovable for utilizada.
Definir a propriedade anexada em C++/CX é um pouco mais complexo. Você precisa decidir como fatorar entre o cabeçalho e o arquivo de código. Além disso, você deve expor o identificador como uma propriedade com apenas um acessador get , por motivos discutidos nas propriedades de dependência personalizadas. No C++/CX, você deve definir explicitamente essa relação de campo de propriedade em vez de depender da palavra-chave readonly do .NET e do suporte implícito de propriedades simples. Você também precisa executar o registro da propriedade anexada em uma função auxiliar que só é executada uma vez, quando o aplicativo é iniciado pela primeira vez, mas antes que as páginas XAML que precisam da propriedade anexada sejam carregadas. O local típico para invocar suas funções auxiliares de registro de propriedade para todas e quaisquer propriedades de dependência ou anexadas é de dentro do construtor da classe App / Application no código do arquivo app.xaml.
public class GameService : DependencyObject
{
public static readonly DependencyProperty IsMovableProperty =
DependencyProperty.RegisterAttached(
"IsMovable",
typeof(Boolean),
typeof(GameService),
new PropertyMetadata(false)
);
public static void SetIsMovable(UIElement element, Boolean value)
{
element.SetValue(IsMovableProperty, value);
}
public static Boolean GetIsMovable(UIElement element)
{
return (Boolean)element.GetValue(IsMovableProperty);
}
}
Public Class GameService
Inherits DependencyObject
Public Shared ReadOnly IsMovableProperty As DependencyProperty =
DependencyProperty.RegisterAttached("IsMovable",
GetType(Boolean),
GetType(GameService),
New PropertyMetadata(False))
Public Shared Sub SetIsMovable(ByRef element As UIElement, value As Boolean)
element.SetValue(IsMovableProperty, value)
End Sub
Public Shared Function GetIsMovable(ByRef element As UIElement) As Boolean
GetIsMovable = CBool(element.GetValue(IsMovableProperty))
End Function
End Class
// GameService.idl
namespace UserAndCustomControls
{
[default_interface]
runtimeclass GameService : Windows.UI.Xaml.DependencyObject
{
GameService();
static Windows.UI.Xaml.DependencyProperty IsMovableProperty{ get; };
static Boolean GetIsMovable(Windows.UI.Xaml.DependencyObject target);
static void SetIsMovable(Windows.UI.Xaml.DependencyObject target, Boolean value);
}
}
// GameService.h
...
static Windows::UI::Xaml::DependencyProperty IsMovableProperty() { return m_IsMovableProperty; }
static bool GetIsMovable(Windows::UI::Xaml::DependencyObject const& target) { return winrt::unbox_value<bool>(target.GetValue(m_IsMovableProperty)); }
static void SetIsMovable(Windows::UI::Xaml::DependencyObject const& target, bool value) { target.SetValue(m_IsMovableProperty, winrt::box_value(value)); }
private:
static Windows::UI::Xaml::DependencyProperty m_IsMovableProperty;
...
// GameService.cpp
...
Windows::UI::Xaml::DependencyProperty GameService::m_IsMovableProperty =
Windows::UI::Xaml::DependencyProperty::RegisterAttached(
L"IsMovable",
winrt::xaml_typename<bool>(),
winrt::xaml_typename<UserAndCustomControls::GameService>(),
Windows::UI::Xaml::PropertyMetadata{ winrt::box_value(false) }
);
...
// GameService.h
#pragma once
#include "pch.h"
//namespace WUX = Windows::UI::Xaml;
namespace UserAndCustomControls {
public ref class GameService sealed : public WUX::DependencyObject {
private:
static WUX::DependencyProperty^ _IsMovableProperty;
public:
GameService::GameService();
void GameService::RegisterDependencyProperties();
static property WUX::DependencyProperty^ IsMovableProperty
{
WUX::DependencyProperty^ get() {
return _IsMovableProperty;
}
};
static bool GameService::GetIsMovable(WUX::UIElement^ element) {
return (bool)element->GetValue(_IsMovableProperty);
};
static void GameService::SetIsMovable(WUX::UIElement^ element, bool value) {
element->SetValue(_IsMovableProperty,value);
}
};
}
// GameService.cpp
#include "pch.h"
#include "GameService.h"
using namespace UserAndCustomControls;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Documents;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Interop;
using namespace Windows::UI::Xaml::Media;
GameService::GameService() {};
GameService::RegisterDependencyProperties() {
DependencyProperty^ GameService::_IsMovableProperty = DependencyProperty::RegisterAttached(
"IsMovable", Platform::Boolean::typeid, GameService::typeid, ref new PropertyMetadata(false));
}
Definindo sua propriedade de anexo personalizada no XAML
Depois de definir sua propriedade anexada e incluir seus membros de suporte como parte de um tipo personalizado, você precisa disponibilizar as definições para uso no XAML. Para fazer isso, você deve mapear um namespace XAML que fará referência ao namespace de código que contém a classe relevante. Nos casos em que você definiu a propriedade anexada como parte de uma biblioteca, você deve incluir essa biblioteca como parte do pacote do aplicativo.
Um mapeamento de namespace XML para XAML normalmente é colocado no elemento raiz de uma página XAML. Por exemplo, para a classe nomeada GameService no namespace UserAndCustomControls que contém as definições de propriedade anexada mostradas nos snippets de código anteriores, o mapeamento pode ter esta aparência.
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:uc="using:UserAndCustomControls"
... >
Usando o mapeamento, você pode definir sua GameService.IsMovable propriedade anexada em qualquer elemento que corresponda à definição de destino, incluindo um tipo existente definido pelo Windows Runtime.
<Image uc:GameService.IsMovable="True" .../>
Se você estiver definindo a propriedade em um elemento que também está dentro do mesmo namespace XML mapeado, ainda deverá incluir o prefixo no nome da propriedade anexada. Isso ocorre porque o prefixo qualifica o tipo de proprietário. O atributo da propriedade anexada não pode ser considerado dentro do mesmo namespace XML que o elemento em que o atributo está incluído, mesmo que, por regras XML normais, os atributos possam herdar o namespace dos elementos. Por exemplo, se você estiver definindo GameService.IsMovable em um tipo personalizado de ImageWithLabelControl (definição não mostrada) e mesmo que ambos tenham sido definidos no mesmo namespace de código mapeado para o mesmo prefixo, o XAML ainda seria assim.
<uc:ImageWithLabelControl uc:GameService.IsMovable="True" .../>
Observação
Se você estiver escrevendo uma interface do usuário XAML com C++/CX, deverá incluir o cabeçalho para o tipo personalizado que define a propriedade anexada, sempre que uma página XAML usar esse tipo. Cada página XAML possui um cabeçalho de código subjacente associado (.xaml.h). É aí que você deve incluir (usando #include) o cabeçalho para a definição do tipo de proprietário da propriedade anexada.
Definindo sua propriedade anexada personalizada de forma imperativa
Você também pode acessar uma propriedade anexada personalizada do código imperativo. O código a seguir mostra como.
<Image x:Name="gameServiceImage"/>
// MainPage.h
...
#include "GameService.h"
...
// MainPage.cpp
...
MainPage::MainPage()
{
InitializeComponent();
GameService::SetIsMovable(gameServiceImage(), true);
}
...
Tipo de valor de uma propriedade anexada personalizada
O tipo usado como o tipo de valor de uma propriedade anexada personalizada afeta o uso, a definição ou o uso e a definição. O tipo de valor da propriedade anexada é declarado em vários locais: nas assinaturas dos métodos do acessador Get e Set e também como o parâmetro propertyType da chamada RegisterAttached .
O tipo de valor mais comum para propriedades anexadas (personalizadas ou não) é uma cadeia de caracteres simples. Isso ocorre porque as propriedades anexadas geralmente se destinam ao uso de atributo XAML e o uso de uma cadeia de caracteres como o tipo de valor mantém as propriedades leves. Outras primitivas que têm métodos de conversão nativa para cadeia de caracteres, como inteiro, double ou um valor de enumeração, também são comuns como tipos de valor para propriedades anexadas. Você pode usar outros tipos de valor, aqueles que não dão suporte à conversão de cadeia de caracteres nativa, como o valor da propriedade anexada. No entanto, isso implica fazer uma escolha sobre o uso ou a implementação:
- Você pode deixar a propriedade anexada como ela está, mas a propriedade anexada só pode dar suporte ao uso em que a propriedade anexada é um elemento de propriedade e o valor é declarado como um elemento de objeto. Nesse caso, o tipo de propriedade precisa dar suporte ao uso de XAML como um elemento de objeto. Para classes de referência existentes do Windows Runtime, verifique a sintaxe XAML para garantir que o tipo dê suporte ao uso do elemento de objeto XAML.
- Você pode deixar a propriedade anexada como está, mas usá-la apenas em um atributo usando uma técnica de referência XAML, como um Binding ou StaticResource, que pode ser expressa como uma cadeia de caracteres.
Mais informações sobre o exemplo Canvas.Left
Em exemplos anteriores de usos de propriedade anexada, mostramos diferentes maneiras de definir a propriedade anexada Canvas.Left . Mas o que isso muda sobre como um Canvas interage com seu objeto e quando isso acontece? Examinaremos mais a fundo este exemplo específico, pois, se você implementar uma propriedade anexada, será interessante ver como uma classe típica que possui propriedades anexadas pretende utilizar esses valores quando os encontra em outros objetos.
A função principal de um Canvas é ser um contêiner de layout de posição absoluta na interface do usuário. Os filhos de um Canvas são armazenados em uma propriedade definida de classe base Children. De todos os painéis , o Canvas é o único que usa o posicionamento absoluto. Ele teria sobrecarregado o modelo de objeto do tipo UIElement comum ao adicionar propriedades que podem ser apenas pertinentes para o Canvas e aqueles casos específicos de UIElement em que são elementos filho de um UIElement. Definir as propriedades de controle de layout de um Canvas como propriedades anexadas que qualquer UIElement pode usar mantém o modelo de objeto mais limpo.
Para ser um painel prático, o Canvas tem um comportamento que substitui os métodos Measure and Arrange no nível da estrutura. É aqui que o Canvas verifica valores de propriedade anexados em seus filhos. Parte dos padrões Medida e Organização é um loop que itera sobre qualquer conteúdo, e um painel tem a propriedade Children que torna explícito o que deve ser considerado elemento filho de um painel. Portanto, o comportamento de layout de tela itera por esses filhos e faz chamadas estáticas Canvas.GetLeft e Canvas.GetTop em cada filho para ver se essas propriedades anexadas contêm um valor não padrão (o padrão é 0). Esses valores são usados para posicionar de forma absoluta cada filho no espaço de layout disponível do Canvas de acordo com os valores específicos fornecidos por cada filho, sendo aplicados usando o Arrange.
O código se parece com esse pseudocódigo.
protected override Size ArrangeOverride(Size finalSize)
{
foreach (UIElement child in Children)
{
double x = (double) Canvas.GetLeft(child);
double y = (double) Canvas.GetTop(child);
child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
}
return base.ArrangeOverride(finalSize);
// real Canvas has more sophisticated sizing
}
Observação
Para obter mais informações sobre como os painéis funcionam, consulte a visão geral dos painéis personalizados XAML.
Tópicos relacionados
Windows developer