Criar um controle que tem uma aparência personalizável
Windows Presentation Foundation (WPF)oferece a capacidade de criar um controle cuja aparência pode ser personalizada. Por exemplo, você pode alterar a aparência de um CheckBox além, criando um novo fará definindo propriedades de ControlTemplate. A ilustração a seguir mostra um CheckBox que usa um padrão de ControlTemplate e um CheckBox que usa um personalizado ControlTemplate.
Uma caixa de seleção que usa o modelo de controle padrão
Uma caixa de seleção que usa um modelo de controle personalizado
Se você seguir o modelo de partes e estados quando você cria um controle, a aparência do controle será personalizável. Ferramentas de Designer, como o Microsoft Expression Blend suportam o modelo de partes e estados, portanto, quando você seguir esse modelo de seu controle ser personalizável nesses tipos de aplicativos. Este tópico aborda o modelo de partes e estados e como seguem quando você cria seu próprio controle. Este tópico usa um exemplo de um controle personalizado, NumericUpDown, para ilustrar a filosofia deste modelo. O NumericUpDown controle exibe um valor numérico, o qual um usuário pode aumentar ou diminuir, clicando em todos os botões. do controle A ilustração a seguir mostra a NumericUpDown controle que será discutido neste tópico.
Um controle personalizado do NumericUpDown
This topic contains the following sections:
Prerequisites
Partes e o modelo de estados
Definindo a estrutura Visual e o comportamento Visual de um controle em um ControlTemplate.
Usando partes do ControlTemplate no código
Fornecendo o contrato de controle
Complete Example
Prerequisites
Este tópico pressupõe que você saiba como criar uma nova ControlTemplate para um controle existente, estão familiarizados com quais são os elementos em um contrato de controle e entender os conceitos discutidos Personalizando a aparência de um controle existente, criando um ControlTemplate..
Observação
Para criar um controle que pode ter sua aparência personalizada, você deve criar um controle que herda do Control classe ou uma de suas subclasses diferente de UserControl.Um controle que herda de UserControl é um controle que pode ser criado rapidamente, mas não usa um ControlTemplate e não é possível personalizar sua aparência.
Partes e o modelo de estados
O modelo de partes e estados Especifica como definir a estrutura visual e o comportamento visual de um controle. Para seguir o modelo de partes e estados, você deve fazer o seguinte:
Definir a estrutura visual e o comportamento visual no ControlTemplate de controle.
Quando a lógica do controle interage com partes do modelo de controle, siga algumas melhores práticas.
Oferecem um contrato de controle para especificar o que deve ser incluído o ControlTemplate.
Quando você define a estrutura visual e o comportamento visual no ControlTemplate de um controle, os autores do aplicativo podem alterar a estrutura visual e o comportamento visual do seu controle, criando uma nova ControlTemplate em vez de escrever código. Você deve fornecer um contrato de controle que informa ao aplicativo autores que FrameworkElement objetos e estados devem ser definidos na ControlTemplate. Você deve seguir algumas práticas recomendadas ao interagir com as partes na ControlTemplate para que o controle manipula adequadamente incompleto ControlTemplate. Se você seguir essas três princípios, os autores de aplicativos poderão criar um ControlTemplate para o seu controle apenas tão facilmente quanto eles pode para os controles que vêm com o WPF. A seção a seguir explica cada uma destas recomendações em detalhes.
Definindo a estrutura Visual e o comportamento Visual de um controle em um ControlTemplate.
Quando você cria seu controle personalizado usando o modelo de partes e estados, você define a estrutura visual do controle e o comportamento visual no seu ControlTemplate em vez de na lógica. A estrutura visual de um controle é a composição de FrameworkElement os objetos que compõem o controle. O comportamento de visual é a maneira como o controle aparece quando ele estiver em um determinado estado. Para obter mais informações sobre como criar um ControlTemplate que especifica a estrutura visual e o comportamento visual de um controle, consulte Personalizando a aparência de um controle existente, criando um ControlTemplate..
No exemplo do NumericUpDown o controle, a estrutura visual inclui dois RepeatButton controles e uma TextBlock. Se você adicionar esses controles no código da NumericUpDown o controle--em seu construtor, por exemplo-- as posições desses controles seria inalterável. Em vez de definir a estrutura visual e o comportamento do visual do controle em seu código, você deve defini-lo a ControlTemplate. Em seguida, um desenvolvedor de aplicativos para personalizar a posição dos botões e TextBlock e especifique o que ocorre quando Value é negativo porque a ControlTemplate podem ser substituídos.
O exemplo a seguir mostra a estrutura visual da NumericUpDown controle, que inclui um RepeatButton para aumentar a Value, um RepeatButton para diminuir o Valuee um TextBlock para exibir Value.
<ControlTemplate TargetType="src:NumericUpDown">
<Grid Margin="3"
Background="{TemplateBinding Background}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border BorderThickness="1" BorderBrush="Gray"
Margin="7,2,2,2" Grid.RowSpan="2"
Background="#E0FFFFFF"
VerticalAlignment="Center"
HorizontalAlignment="Stretch">
<!--Bind the TextBlock to the Value property-->
<TextBlock Name="TextBlock"
Width="60" TextAlignment="Right" Padding="5"
Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type src:NumericUpDown}},
Path=Value}"/>
</Border>
<RepeatButton Content="Up" Margin="2,5,5,0"
Name="UpButton"
Grid.Column="1" Grid.Row="0"/>
<RepeatButton Content="Down" Margin="2,0,5,5"
Name="DownButton"
Grid.Column="1" Grid.Row="1"/>
<Rectangle Name="FocusVisual" Grid.ColumnSpan="2" Grid.RowSpan="2"
Stroke="Black" StrokeThickness="1"
Visibility="Collapsed"/>
</Grid>
</Grid>
</ControlTemplate>
Um comportamento visual da NumericUpDown controle é que o valor em uma fonte vermelha se for negativo. Se você alterar o Foreground da TextBlock no código quando o Value for negativo, o NumericUpDown sempre mostrará um valor negativo vermelho. Você especificar o comportamento visual do controle no ControlTemplate , adicionando VisualState objetos para o ControlTemplate. A exemplo a seguir mostra a VisualState objetos para o Positive e Negative Estados. Positivee Negative (o controle é sempre em exatamente um dos dois) são mutuamente exclusivo, portanto, o exemplo coloca o VisualState objetos em um único VisualStateGroup. Quando o controle entra na Negative estado, o Foreground da TextBlock transforma red. Quando o controle é a Positive estado, o Foreground retorna a ele valor original. Definindo VisualState objetos em um ControlTemplate será abordada posteriormente nas Personalizando a aparência de um controle existente, criando um ControlTemplate..
Observação
Certifique-se de definir o VisualStateManager.VisualStateGroups anexado a propriedade na raiz FrameworkElement da ControlTemplate.
<ControlTemplate TargetType="local:NumericUpDown">
<Grid Margin="3"
Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="ValueStates">
<!--Make the Value property red when it is negative.-->
<VisualState Name="Negative">
<Storyboard>
<ColorAnimation To="Red"
Storyboard.TargetName="TextBlock"
Storyboard.TargetProperty="(Foreground).(Color)"/>
</Storyboard>
</VisualState>
<!--Return the TextBlock's Foreground to its
original color.-->
<VisualState Name="Positive"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
Usando partes do ControlTemplate no código
A ControlTemplate autor pode omitir FrameworkElement ou VisualState objetos, intencionalmente ou por engano, mas a lógica do controle talvez precise essas partes funcione adequadamente. O modelo de partes e estados Especifica que o seu controle deve ser resiliente em relação a um ControlTemplate que está faltando FrameworkElement ou VisualState objetos. Seu controle deve não lançar uma exceção ou o relatório de erro se um FrameworkElement, VisualState, ou VisualStateGroup está faltando a partir de ControlTemplate. Esta seção descreve as práticas recomendadas para a interação com FrameworkElement objetos e gerenciamento de estados.
Prever a falta de objetos de FrameworkElement
Quando você define FrameworkElement objetos na ControlTemplate, a lógica do controle, talvez seja necessário interagir com alguns deles. Por exemplo, o NumericUpDown controle assina os botões Clickevento para aumentar ou diminuir Value e define o Text propriedade da TextBlock para Value. Se um personalizado ControlTemplate omite a TextBlock ou botões, é aceitável que o controle perde parte de sua funcionalidade, mas você deve certificar-se de que o controle não causa um erro. Por exemplo, se um ControlTemplate não contém os botões para alterar Value, o NumericUpDown perde essa funcionalidade, mas um aplicativo que usa o ControlTemplate continuará a executar.
As seguintes práticas garantirá que o seu controle responda corretamente à ausência de FrameworkElement objetos:
Definir o x:Name atributo para cada FrameworkElement que você precisa referenciar em código.
Definir propriedades particulares para cada FrameworkElement que você precisa interagir com.
Se inscrever e cancelar a inscrição de todos os eventos que o controle manipula no FrameworkElement acessador de conjunto. da propriedade
Definir o FrameworkElement a propriedades que você definiu na etapa 2 do OnApplyTemplate método. Esta é a mais antiga que a FrameworkElement na ControlTemplate está disponível para o controle. Uso o x:Name da FrameworkElement obtê-lo a partir de ControlTemplate.
Verifique se a FrameworkElement não é null antes de acessar seus participantes. Se for null, que não reportam um erro.
Os exemplos a seguir mostram como o NumericUpDown controle interage com FrameworkElement objetos de acordo com as recomendações na lista anterior.
No exemplo que define a estrutura visual da NumericUpDown controlar na ControlTemplate, o RepeatButton que aumenta a Value tem seu x:Name atributo definido como UpButton. O exemplo a seguir declara uma propriedade chamada UpButtonElement que representa o RepeatButton que é declarado na ControlTemplate. O set acessador primeiro cancela a inscrição para o botão Click evento se UpDownElement não é null, em seguida, define a propriedade e, em seguida, ele assina o Click de evento. Também é uma propriedade definida, mas não mostrados aqui, para os outros RepeatButton, chamado DownButtonElement.
Private m_upButtonElement As RepeatButton
Private Property UpButtonElement() As RepeatButton
Get
Return m_upButtonElement
End Get
Set(ByVal value As RepeatButton)
If m_upButtonElement IsNot Nothing Then
RemoveHandler m_upButtonElement.Click, AddressOf upButtonElement_Click
End If
m_upButtonElement = value
If m_upButtonElement IsNot Nothing Then
AddHandler m_upButtonElement.Click, AddressOf upButtonElement_Click
End If
End Set
End Property
private RepeatButton upButtonElement;
private RepeatButton UpButtonElement
{
get
{
return upButtonElement;
}
set
{
if (upButtonElement != null)
{
upButtonElement.Click -=
new RoutedEventHandler(upButtonElement_Click);
}
upButtonElement = value;
if (upButtonElement != null)
{
upButtonElement.Click +=
new RoutedEventHandler(upButtonElement_Click);
}
}
}
A exemplo a seguir mostra a OnApplyTemplate para o NumericUpDown de controle. O exemplo usa o GetTemplateChild método para obter o FrameworkElement objetos da ControlTemplate. Observe que o exemplo protege contra casos onde GetTemplateChild localiza um FrameworkElement com o nome especificado é que não é do tipo esperado. Também é uma prática recomendada para ignorar os elementos que possuem especificado x:Name , mas são do tipo errado.
Public Overloads Overrides Sub OnApplyTemplate()
UpButtonElement = TryCast(GetTemplateChild("UpButton"), RepeatButton)
DownButtonElement = TryCast(GetTemplateChild("DownButton"), RepeatButton)
UpdateStates(False)
End Sub
public override void OnApplyTemplate()
{
UpButtonElement = GetTemplateChild("UpButton") as RepeatButton;
DownButtonElement = GetTemplateChild("DownButton") as RepeatButton;
//TextElement = GetTemplateChild("TextBlock") as TextBlock;
UpdateStates(false);
}
Seguindo as práticas que são mostradas nos exemplos anteriores, garantir que seu controle continuará a ser executada quando o ControlTemplate está faltando uma FrameworkElement.
Use o VisualStateManager para gerenciar os estados
O VisualStateManager controla os estados de um controle e executa a lógica necessária para a transição entre estados. Quando você adiciona VisualState objetos para o ControlTemplate, adicioná-los a uma VisualStateGroup e adicionar o VisualStateGroup para o VisualStateManager.VisualStateGroups anexado a propriedade para que o VisualStateManager tem acesso a eles.
O exemplo a seguir se repete o exemplo anterior que mostra a VisualState objetos que corresponde à Positive e Negative Estados de controle. The Storyboard in the Negative VisualState turns the Foreground of the TextBlock red. Quando o NumericUpDown o controle está na Negative de estado no storyboard a Negative estado começa. Em seguida, a Storyboard na Negative estado pára quando o controle retorna para o Positive estado. O Positive VisualState não precisa conter um Storyboard porque quando o Storyboard para o Negative pára, o Foreground retorna à sua cor original.
<ControlTemplate TargetType="local:NumericUpDown">
<Grid Margin="3"
Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="ValueStates">
<!--Make the Value property red when it is negative.-->
<VisualState Name="Negative">
<Storyboard>
<ColorAnimation To="Red"
Storyboard.TargetName="TextBlock"
Storyboard.TargetProperty="(Foreground).(Color)"/>
</Storyboard>
</VisualState>
<!--Return the TextBlock's Foreground to its
original color.-->
<VisualState Name="Positive"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
Observe que o TextBlock recebe um nome, mas o TextBlock não estiver no contrato de controle para NumericUpDown porque a lógica do controle nunca faz referência a TextBlock. Elementos que são referenciados na ControlTemplate têm nomes, mas não precisa ser parte do contrato de controle, porque uma nova ControlTemplate para o controle talvez não precise fazer referência a esse elemento. Por exemplo, alguém que cria um novo ControlTemplate para NumericUpDown pode decidir não indicar que Value for negativo, alterando a Foreground. Nesse caso, nem o código nem o ControlTemplate referências a TextBlock pelo nome.
Lógica do controle é responsável pela alteração de estado do controle. O exemplo a seguir mostra que o NumericUpDown controlar chamadas a GoToState método para ir para o Positive estado quando Value é 0 ou maior e o Negative estado quando Value for menor que 0.
If Value >= 0 Then
VisualStateManager.GoToState(Me, "Positive", useTransitions)
Else
VisualStateManager.GoToState(Me, "Negative", useTransitions)
End If
if (Value >= 0)
{
VisualStateManager.GoToState(this, "Positive", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Negative", useTransitions);
}
O GoToState método executa a lógica necessária para iniciar e interromper os storyboards adequadamente. Quando um controle chama GoToState para alterar seu estado, o VisualStateManager faz o seguinte:
Se a VisualState que o controle será tem um Storyboard, o storyboard começa. Então, se a VisualState o controle é proveniente de tem um Storyboard, as extremidades do storyboard.
Se o controle já estiver no estado em que for especificado, GoToState não faz nada e retorna true.
Se o estado especificado não existe o ControlTemplate de control, GoToState não faz nada e retorna false.
Práticas recomendadas para trabalhar com o VisualStateManager.
É recomendável que você faça o seguinte para manter os estados do controle:
Use as propriedades para controlar seu estado.
Crie um método auxiliar para fazer a transição entre estados.
O NumericUpDown controle usa seu Value propriedade para controlar se ele está sendo o Positive ou Negative estado. O NumericUpDown o controle também define o Focused e UnFocused afirma, as faixas a IsFocused propriedade. Se você usar os estados que naturalmente não correspondem a uma propriedade do controle, você pode definir uma propriedade privada para controlar o estado.
Um único método que atualiza todos os estados centraliza chamadas para o VisualStateManager e mantém o seu código gerenciável. A exemplo a seguir mostra a NumericUpDown método do auxiliar do controle, UpdateStates. Quando Value é maior que ou igual a 0, o Control está sendo o Positive estado. Quando Value é menor que 0, o controle é a Negative estado. Quando IsFocused é true, o controle é a Focused estado; Caso contrário, trata-o Unfocused estado. O controle pode chamar UpdateStates sempre que ele precisa alterar seu estado, independentemente de quais alterações de estado.
Private Sub UpdateStates(ByVal useTransitions As Boolean)
If Value >= 0 Then
VisualStateManager.GoToState(Me, "Positive", useTransitions)
Else
VisualStateManager.GoToState(Me, "Negative", useTransitions)
End If
If IsFocused Then
VisualStateManager.GoToState(Me, "Focused", useTransitions)
Else
VisualStateManager.GoToState(Me, "Unfocused", useTransitions)
End If
End Sub
private void UpdateStates(bool useTransitions)
{
if (Value >= 0)
{
VisualStateManager.GoToState(this, "Positive", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Negative", useTransitions);
}
if (IsFocused)
{
VisualStateManager.GoToState(this, "Focused", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Unfocused", useTransitions);
}
}
Se você passar o nome de um estado para GoToState quando o controle está já nesse estado, GoToState não faz nada, portanto você não precisa procurar o estado atual. do controle Por exemplo, se Value muda de um número negativo para outro número negativo, o storyboard para o Negative estado não é interrompido e o usuário não verá uma alteração no controle.
O VisualStateManager usa VisualStateGroup objetos para determinar qual estado deseja sair quando você chamar GoToState. O controle é sempre em um estado para cada VisualStateGroup que está definido na sua ControlTemplate e deixa somente um estado quando ele vai para outro estado, da mesma VisualStateGroup. Por exemplo, o ControlTemplate da NumericUpDown controle define o Positive e Negative VisualState objetos em um VisualStateGroup e o Focused e Unfocused VisualState objetos em outro. (Você pode ver o Focused e Unfocused VisualState definido na Exemplo completo seção neste tópico, quando o controle vai da Positive estado para o Negative estado, ou vice-versa, o controle permanece em ambos o Focused ou Unfocused estado.
Há três locais típicos, onde o estado de um controle pode alterar:
Quando o ControlTemplate é aplicado a Control.
Quando uma propriedade é alterada.
Quando ocorre um evento.
Os exemplos a seguir demonstram a atualização do estado do NumericUpDown o controle desses casos.
Você deve atualizar o estado do controle no OnApplyTemplate método para que o controle aparece no correto estado quando o ControlTemplate é aplicado. O exemplo a seguir chama UpdateStates em OnApplyTemplate garantir que o controle está no estados apropriados. Por exemplo, suponha que você crie um NumericUpDown de controle e defina seu Foreground para verde e Value como -5. Se você não chamar UpdateStates quando o ControlTemplate é aplicado a NumericUpDown controle, o controle não está na Negative estado e o valor é verde, em vez de red. Você deve chamar UpdateStates para colocar o controle Negative estado.
Public Overloads Overrides Sub OnApplyTemplate()
UpButtonElement = TryCast(GetTemplateChild("UpButton"), RepeatButton)
DownButtonElement = TryCast(GetTemplateChild("DownButton"), RepeatButton)
UpdateStates(False)
End Sub
public override void OnApplyTemplate()
{
UpButtonElement = GetTemplateChild("UpButton") as RepeatButton;
DownButtonElement = GetTemplateChild("DownButton") as RepeatButton;
//TextElement = GetTemplateChild("TextBlock") as TextBlock;
UpdateStates(false);
}
Geralmente, você precisa atualizar os estados de um controle quando uma propriedade é alterada. O exemplo a seguir mostra todo o ValueChangedCallback método. Porque ValueChangedCallback é chamado quando Value alterações, as chamadas de método UpdateStates no caso de Value alterado de positivo para negativo ou vice versa. É aceitável para chamar UpdateStates quando Value é alterado, mas permanece positivo ou negativo, nesse caso, o controle não será alterada estados.
Private Shared Sub ValueChangedCallback(ByVal obj As DependencyObject,
ByVal args As DependencyPropertyChangedEventArgs)
Dim ctl As NumericUpDown = DirectCast(obj, NumericUpDown)
Dim newValue As Integer = CInt(args.NewValue)
' Call UpdateStates because the Value might have caused the
' control to change ValueStates.
ctl.UpdateStates(True)
' Call OnValueChanged to raise the ValueChanged event.
ctl.OnValueChanged(New ValueChangedEventArgs(NumericUpDown.ValueChangedEvent, newValue))
End Sub
private static void ValueChangedCallback(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
NumericUpDown ctl = (NumericUpDown)obj;
int newValue = (int)args.NewValue;
// Call UpdateStates because the Value might have caused the
// control to change ValueStates.
ctl.UpdateStates(true);
// Call OnValueChanged to raise the ValueChanged event.
ctl.OnValueChanged(
new ValueChangedEventArgs(NumericUpDown.ValueChangedEvent,
newValue));
}
Talvez você precise atualizar os estados quando ocorre um evento. O exemplo a seguir mostra que o NumericUpDown chamadas UpdateStates sobre o Control para lidar com o GotFocus evento.
Protected Overloads Overrides Sub OnGotFocus(ByVal e As RoutedEventArgs)
MyBase.OnGotFocus(e)
UpdateStates(True)
End Sub
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
UpdateStates(true);
}
O VisualStateManager o ajuda a gerenciar estados de. seu controle Usando o VisualStateManager, certifique-se que o seu controle corretamente as transições entre estados. Se você seguir as recomendações descritas nesta seção para trabalhar com o VisualStateManager, código do controle permanecerá legível e fácil manutenção.
Fornecendo o contrato de controle
Você fornecer um contrato de controle para que ControlTemplate autores saibam o que colocar no modelo. Um contrato de controle possui três elementos:
Os elementos visuais que usa a lógica do controle.
Os estados do controle e o grupo de cada estado pertence.
As propriedades públicas visualmente afetam o controle.
Alguém que cria um novo ControlTemplate precisa saber o que FrameworkElement objetos usa a lógica do controle, que tipo de cada objeto é e que seu nome is. A ControlTemplate autor também precisa saber o nome de cada estado possível, o controle pode ser e que VisualStateGroup o estado é pol.
Retornando para a NumericUpDown o controle de exemplo, espera a ControlTemplate para ter os seguintes FrameworkElement objetos:
A RepeatButton called UpButton.
A RepeatButton chamado DownButton.
O controle pode estar nos seguintes estados:
NoValueStates VisualStateGroup
Positive
Negative
NoFocusStates VisualStateGroup
Focused
Unfocused
Para especificar o que FrameworkElement o controle de objetos de espera, se você usar o TemplatePartAttribute, que especifica o nome e o tipo de elementos esperados. Para especificar os estados possíveis de um controle, use o TemplateVisualStateAttribute, que especifica o nome do estado e que VisualStateGroup ele pertence. Colocar o TemplatePartAttribute e TemplateVisualStateAttribute na definição de classe de controle.
Qualquer propriedade pública que afeta a aparência de seu controle também é uma parte do contrato de controle.
O exemplo a seguir especifica o FrameworkElement objeto e estados para o NumericUpDown de controle.
<TemplatePart(Name:="UpButtonElement", Type:=GetType(RepeatButton))> _
<TemplatePart(Name:="DownButtonElement", Type:=GetType(RepeatButton))> _
<TemplateVisualState(Name:="Positive", GroupName:="ValueStates")> _
<TemplateVisualState(Name:="Negative", GroupName:="ValueStates")> _
<TemplateVisualState(Name:="Focused", GroupName:="FocusedStates")> _
<TemplateVisualState(Name:="Unfocused", GroupName:="FocusedStates")> _
Public Class NumericUpDown
Inherits Control
Public Shared ReadOnly BackgroundProperty As DependencyProperty
Public Shared ReadOnly BorderBrushProperty As DependencyProperty
Public Shared ReadOnly BorderThicknessProperty As DependencyProperty
Public Shared ReadOnly FontFamilyProperty As DependencyProperty
Public Shared ReadOnly FontSizeProperty As DependencyProperty
Public Shared ReadOnly FontStretchProperty As DependencyProperty
Public Shared ReadOnly FontStyleProperty As DependencyProperty
Public Shared ReadOnly FontWeightProperty As DependencyProperty
Public Shared ReadOnly ForegroundProperty As DependencyProperty
Public Shared ReadOnly HorizontalContentAlignmentProperty As DependencyProperty
Public Shared ReadOnly PaddingProperty As DependencyProperty
Public Shared ReadOnly TextAlignmentProperty As DependencyProperty
Public Shared ReadOnly TextDecorationsProperty As DependencyProperty
Public Shared ReadOnly TextWrappingProperty As DependencyProperty
Public Shared ReadOnly VerticalContentAlignmentProperty As DependencyProperty
Private _Background As Brush
Public Property Background() As Brush
Get
Return _Background
End Get
Set(ByVal value As Brush)
_Background = value
End Set
End Property
Private _BorderBrush As Brush
Public Property BorderBrush() As Brush
Get
Return _BorderBrush
End Get
Set(ByVal value As Brush)
_BorderBrush = value
End Set
End Property
Private _BorderThickness As Thickness
Public Property BorderThickness() As Thickness
Get
Return _BorderThickness
End Get
Set(ByVal value As Thickness)
_BorderThickness = value
End Set
End Property
Private _FontFamily As FontFamily
Public Property FontFamily() As FontFamily
Get
Return _FontFamily
End Get
Set(ByVal value As FontFamily)
_FontFamily = value
End Set
End Property
Private _FontSize As Double
Public Property FontSize() As Double
Get
Return _FontSize
End Get
Set(ByVal value As Double)
_FontSize = value
End Set
End Property
Private _FontStretch As FontStretch
Public Property FontStretch() As FontStretch
Get
Return _FontStretch
End Get
Set(ByVal value As FontStretch)
_FontStretch = value
End Set
End Property
Private _FontStyle As FontStyle
Public Property FontStyle() As FontStyle
Get
Return _FontStyle
End Get
Set(ByVal value As FontStyle)
_FontStyle = value
End Set
End Property
Private _FontWeight As FontWeight
Public Property FontWeight() As FontWeight
Get
Return _FontWeight
End Get
Set(ByVal value As FontWeight)
_FontWeight = value
End Set
End Property
Private _Foreground As Brush
Public Property Foreground() As Brush
Get
Return _Foreground
End Get
Set(ByVal value As Brush)
_Foreground = value
End Set
End Property
Private _HorizontalContentAlignment As HorizontalAlignment
Public Property HorizontalContentAlignment() As HorizontalAlignment
Get
Return _HorizontalContentAlignment
End Get
Set(ByVal value As HorizontalAlignment)
_HorizontalContentAlignment = value
End Set
End Property
Private _Padding As Thickness
Public Property Padding() As Thickness
Get
Return _Padding
End Get
Set(ByVal value As Thickness)
_Padding = value
End Set
End Property
Private _TextAlignment As TextAlignment
Public Property TextAlignment() As TextAlignment
Get
Return _TextAlignment
End Get
Set(ByVal value As TextAlignment)
_TextAlignment = value
End Set
End Property
Private _TextDecorations As TextDecorationCollection
Public Property TextDecorations() As TextDecorationCollection
Get
Return _TextDecorations
End Get
Set(ByVal value As TextDecorationCollection)
_TextDecorations = value
End Set
End Property
Private _TextWrapping As TextWrapping
Public Property TextWrapping() As TextWrapping
Get
Return _TextWrapping
End Get
Set(ByVal value As TextWrapping)
_TextWrapping = value
End Set
End Property
Private _VerticalContentAlignment As VerticalAlignment
Public Property VerticalContentAlignment() As VerticalAlignment
Get
Return _VerticalContentAlignment
End Get
Set(ByVal value As VerticalAlignment)
_VerticalContentAlignment = value
End Set
End Property
End Class
[TemplatePart(Name = "UpButtonElement", Type = typeof(RepeatButton))]
[TemplatePart(Name = "DownButtonElement", Type = typeof(RepeatButton))]
[TemplateVisualState(Name = "Positive", GroupName = "ValueStates")]
[TemplateVisualState(Name = "Negative", GroupName = "ValueStates")]
[TemplateVisualState(Name = "Focused", GroupName = "FocusedStates")]
[TemplateVisualState(Name = "Unfocused", GroupName = "FocusedStates")]
public class NumericUpDown : Control
{
public static readonly DependencyProperty BackgroundProperty;
public static readonly DependencyProperty BorderBrushProperty;
public static readonly DependencyProperty BorderThicknessProperty;
public static readonly DependencyProperty FontFamilyProperty;
public static readonly DependencyProperty FontSizeProperty;
public static readonly DependencyProperty FontStretchProperty;
public static readonly DependencyProperty FontStyleProperty;
public static readonly DependencyProperty FontWeightProperty;
public static readonly DependencyProperty ForegroundProperty;
public static readonly DependencyProperty HorizontalContentAlignmentProperty;
public static readonly DependencyProperty PaddingProperty;
public static readonly DependencyProperty TextAlignmentProperty;
public static readonly DependencyProperty TextDecorationsProperty;
public static readonly DependencyProperty TextWrappingProperty;
public static readonly DependencyProperty VerticalContentAlignmentProperty;
public Brush Background { get; set; }
public Brush BorderBrush { get; set; }
public Thickness BorderThickness { get; set; }
public FontFamily FontFamily { get; set; }
public double FontSize { get; set; }
public FontStretch FontStretch { get; set; }
public FontStyle FontStyle { get; set; }
public FontWeight FontWeight { get; set; }
public Brush Foreground { get; set; }
public HorizontalAlignment HorizontalContentAlignment { get; set; }
public Thickness Padding { get; set; }
public TextAlignment TextAlignment { get; set; }
public TextDecorationCollection TextDecorations { get; set; }
public TextWrapping TextWrapping { get; set; }
public VerticalAlignment VerticalContentAlignment { get; set; }
}
Complete Example
O exemplo a seguir é de todo o ControlTemplate para o NumericUpDown de controle.
<!--This is the contents of the themes/generic.xaml file.-->
<ResourceDictionary
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VSMCustomControl">
<Style TargetType="{x:Type local:NumericUpDown}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:NumericUpDown">
<Grid Margin="3"
Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="ValueStates">
<!--Make the Value property red when it is negative.-->
<VisualState Name="Negative">
<Storyboard>
<ColorAnimation To="Red"
Storyboard.TargetName="TextBlock"
Storyboard.TargetProperty="(Foreground).(Color)"/>
</Storyboard>
</VisualState>
<!--Return the control to its initial state by
return the TextBlock's Foreground to its
original color.-->
<VisualState Name="Positive"/>
</VisualStateGroup>
<VisualStateGroup Name="FocusStates">
<!--Add a focus rectangle to highlight the entire control
when it has focus.-->
<VisualState Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="FocusVisual"
Storyboard.TargetProperty="Visibility" Duration="0">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!--Return the control to its initial state by
hiding the focus rectangle.-->
<VisualState Name="Unfocused"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border BorderThickness="1" BorderBrush="Gray"
Margin="7,2,2,2" Grid.RowSpan="2"
Background="#E0FFFFFF"
VerticalAlignment="Center"
HorizontalAlignment="Stretch">
<!--Bind the TextBlock to the Value property-->
<TextBlock Name="TextBlock"
Width="60" TextAlignment="Right" Padding="5"
Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:NumericUpDown}},
Path=Value}"/>
</Border>
<RepeatButton Content="Up" Margin="2,5,5,0"
Name="UpButton"
Grid.Column="1" Grid.Row="0"/>
<RepeatButton Content="Down" Margin="2,0,5,5"
Name="DownButton"
Grid.Column="1" Grid.Row="1"/>
<Rectangle Name="FocusVisual" Grid.ColumnSpan="2" Grid.RowSpan="2"
Stroke="Black" StrokeThickness="1"
Visibility="Collapsed"/>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
O exemplo a seguir mostra a lógica para a NumericUpDown.
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Controls.Primitives
Imports System.Windows.Input
Imports System.Windows.Media
<TemplatePart(Name:="UpButtonElement", Type:=GetType(RepeatButton))> _
<TemplatePart(Name:="DownButtonElement", Type:=GetType(RepeatButton))> _
<TemplateVisualState(Name:="Positive", GroupName:="ValueStates")> _
<TemplateVisualState(Name:="Negative", GroupName:="ValueStates")> _
<TemplateVisualState(Name:="Focused", GroupName:="FocusedStates")> _
<TemplateVisualState(Name:="Unfocused", GroupName:="FocusedStates")> _
Public Class NumericUpDown
Inherits Control
Public Sub New()
DefaultStyleKeyProperty.OverrideMetadata(GetType(NumericUpDown), New FrameworkPropertyMetadata(GetType(NumericUpDown)))
Me.IsTabStop = True
End Sub
Public Shared ReadOnly ValueProperty As DependencyProperty =
DependencyProperty.Register("Value", GetType(Integer), GetType(NumericUpDown),
New PropertyMetadata(New PropertyChangedCallback(AddressOf ValueChangedCallback)))
Public Property Value() As Integer
Get
Return CInt(GetValue(ValueProperty))
End Get
Set(ByVal value As Integer)
SetValue(ValueProperty, value)
End Set
End Property
Private Shared Sub ValueChangedCallback(ByVal obj As DependencyObject,
ByVal args As DependencyPropertyChangedEventArgs)
Dim ctl As NumericUpDown = DirectCast(obj, NumericUpDown)
Dim newValue As Integer = CInt(args.NewValue)
' Call UpdateStates because the Value might have caused the
' control to change ValueStates.
ctl.UpdateStates(True)
' Call OnValueChanged to raise the ValueChanged event.
ctl.OnValueChanged(New ValueChangedEventArgs(NumericUpDown.ValueChangedEvent, newValue))
End Sub
Public Shared ReadOnly ValueChangedEvent As RoutedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Direct,
GetType(ValueChangedEventHandler), GetType(NumericUpDown))
Public Custom Event ValueChanged As ValueChangedEventHandler
AddHandler(ByVal value As ValueChangedEventHandler)
Me.AddHandler(ValueChangedEvent, value)
End AddHandler
RemoveHandler(ByVal value As ValueChangedEventHandler)
Me.RemoveHandler(ValueChangedEvent, value)
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As RoutedEventArgs)
Me.RaiseEvent(e)
End RaiseEvent
End Event
Protected Overridable Sub OnValueChanged(ByVal e As ValueChangedEventArgs)
' Raise the ValueChanged event so applications can be alerted
' when Value changes.
MyBase.RaiseEvent(e)
End Sub
#Region "NUDCode"
Private Sub UpdateStates(ByVal useTransitions As Boolean)
If Value >= 0 Then
VisualStateManager.GoToState(Me, "Positive", useTransitions)
Else
VisualStateManager.GoToState(Me, "Negative", useTransitions)
End If
If IsFocused Then
VisualStateManager.GoToState(Me, "Focused", useTransitions)
Else
VisualStateManager.GoToState(Me, "Unfocused", useTransitions)
End If
End Sub
Public Overloads Overrides Sub OnApplyTemplate()
UpButtonElement = TryCast(GetTemplateChild("UpButton"), RepeatButton)
DownButtonElement = TryCast(GetTemplateChild("DownButton"), RepeatButton)
UpdateStates(False)
End Sub
Private m_downButtonElement As RepeatButton
Private Property DownButtonElement() As RepeatButton
Get
Return m_downButtonElement
End Get
Set(ByVal value As RepeatButton)
If m_downButtonElement IsNot Nothing Then
RemoveHandler m_downButtonElement.Click, AddressOf downButtonElement_Click
End If
m_downButtonElement = value
If m_downButtonElement IsNot Nothing Then
AddHandler m_downButtonElement.Click, AddressOf downButtonElement_Click
End If
End Set
End Property
Private Sub downButtonElement_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Value -= 1
End Sub
Private m_upButtonElement As RepeatButton
Private Property UpButtonElement() As RepeatButton
Get
Return m_upButtonElement
End Get
Set(ByVal value As RepeatButton)
If m_upButtonElement IsNot Nothing Then
RemoveHandler m_upButtonElement.Click, AddressOf upButtonElement_Click
End If
m_upButtonElement = value
If m_upButtonElement IsNot Nothing Then
AddHandler m_upButtonElement.Click, AddressOf upButtonElement_Click
End If
End Set
End Property
Private Sub upButtonElement_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Value += 1
End Sub
Protected Overloads Overrides Sub OnMouseLeftButtonDown(ByVal e As MouseButtonEventArgs)
MyBase.OnMouseLeftButtonDown(e)
Focus()
End Sub
Protected Overloads Overrides Sub OnGotFocus(ByVal e As RoutedEventArgs)
MyBase.OnGotFocus(e)
UpdateStates(True)
End Sub
Protected Overloads Overrides Sub OnLostFocus(ByVal e As RoutedEventArgs)
MyBase.OnLostFocus(e)
UpdateStates(True)
End Sub
#End Region
End Class
Public Delegate Sub ValueChangedEventHandler(ByVal sender As Object,
ByVal e As ValueChangedEventArgs)
Public Class ValueChangedEventArgs
Inherits RoutedEventArgs
Private _value As Integer
Public Sub New(ByVal id As RoutedEvent,
ByVal num As Integer)
_value = num
RoutedEvent = id
End Sub
Public ReadOnly Property Value() As Integer
Get
Return _value
End Get
End Property
End Class
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
namespace VSMCustomControl
{
[TemplatePart(Name = "UpButtonElement", Type = typeof(RepeatButton))]
[TemplatePart(Name = "DownButtonElement", Type = typeof(RepeatButton))]
[TemplateVisualState(Name = "Positive", GroupName = "ValueStates")]
[TemplateVisualState(Name = "Negative", GroupName = "ValueStates")]
[TemplateVisualState(Name = "Focused", GroupName = "FocusedStates")]
[TemplateVisualState(Name = "Unfocused", GroupName = "FocusedStates")]
public class NumericUpDown : Control
{
public NumericUpDown()
{
DefaultStyleKey = typeof(NumericUpDown);
this.IsTabStop = true;
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value", typeof(int), typeof(NumericUpDown),
new PropertyMetadata(
new PropertyChangedCallback(ValueChangedCallback)));
public int Value
{
get
{
return (int)GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
}
}
private static void ValueChangedCallback(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
NumericUpDown ctl = (NumericUpDown)obj;
int newValue = (int)args.NewValue;
// Call UpdateStates because the Value might have caused the
// control to change ValueStates.
ctl.UpdateStates(true);
// Call OnValueChanged to raise the ValueChanged event.
ctl.OnValueChanged(
new ValueChangedEventArgs(NumericUpDown.ValueChangedEvent,
newValue));
}
public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Direct,
typeof(ValueChangedEventHandler), typeof(NumericUpDown));
public event ValueChangedEventHandler ValueChanged
{
add { AddHandler(ValueChangedEvent, value); }
remove { RemoveHandler(ValueChangedEvent, value); }
}
protected virtual void OnValueChanged(ValueChangedEventArgs e)
{
// Raise the ValueChanged event so applications can be alerted
// when Value changes.
RaiseEvent(e);
}
private void UpdateStates(bool useTransitions)
{
if (Value >= 0)
{
VisualStateManager.GoToState(this, "Positive", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Negative", useTransitions);
}
if (IsFocused)
{
VisualStateManager.GoToState(this, "Focused", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Unfocused", useTransitions);
}
}
public override void OnApplyTemplate()
{
UpButtonElement = GetTemplateChild("UpButton") as RepeatButton;
DownButtonElement = GetTemplateChild("DownButton") as RepeatButton;
//TextElement = GetTemplateChild("TextBlock") as TextBlock;
UpdateStates(false);
}
private RepeatButton downButtonElement;
private RepeatButton DownButtonElement
{
get
{
return downButtonElement;
}
set
{
if (downButtonElement != null)
{
downButtonElement.Click -=
new RoutedEventHandler(downButtonElement_Click);
}
downButtonElement = value;
if (downButtonElement != null)
{
downButtonElement.Click +=
new RoutedEventHandler(downButtonElement_Click);
}
}
}
void downButtonElement_Click(object sender, RoutedEventArgs e)
{
Value--;
}
private RepeatButton upButtonElement;
private RepeatButton UpButtonElement
{
get
{
return upButtonElement;
}
set
{
if (upButtonElement != null)
{
upButtonElement.Click -=
new RoutedEventHandler(upButtonElement_Click);
}
upButtonElement = value;
if (upButtonElement != null)
{
upButtonElement.Click +=
new RoutedEventHandler(upButtonElement_Click);
}
}
}
void upButtonElement_Click(object sender, RoutedEventArgs e)
{
Value++;
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
Focus();
}
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
UpdateStates(true);
}
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
UpdateStates(true);
}
}
public delegate void ValueChangedEventHandler(object sender, ValueChangedEventArgs e);
public class ValueChangedEventArgs : RoutedEventArgs
{
private int _value;
public ValueChangedEventArgs(RoutedEvent id, int num)
{
_value = num;
RoutedEvent = id;
}
public int Value
{
get { return _value; }
}
}
}
Consulte também
Conceitos
Personalizando a aparência de um controle existente, criando um ControlTemplate.