Özelleştirilebilir Görünümü olan Denetim Oluşturma
Windows Presentation Foundation (WPF), görünümü özelleştirilebilen bir denetim oluşturmanızı sağlar. Örneğin, yeni ControlTemplatebir CheckBox oluşturarak bir ayarının görünümünü özelliklerin ne yapacağının ötesinde değiştirebilirsiniz. Aşağıdaki çizimdeCheckBox, varsayılan ControlTemplate ve özel ControlTemplatekullanan bir CheckBox gösterilmektedir.
Varsayılan denetim şablonunu kullanan bir Onay Kutusu
Özel denetim şablonu kullanan bir CheckBox
Denetim oluştururken parça ve durum modelini izlerseniz, denetiminizin görünümü özelleştirilebilir. Visual Studio için Blend gibi Tasarım Aracı araçlar parça ve durum modelini destekler, bu nedenle bu modeli takip ettiğinizde denetiminiz bu tür uygulamalarda özelleştirilebilir. Bu konu başlığında parçalar ve durumlar modeli ve kendi denetiminizi oluştururken nasıl izleyebileceğiniz açıklanır. Bu konuda, NumericUpDown
bu modelin felsefesini göstermek için bir özel denetim örneği kullanılır. Denetim NumericUpDown
, bir kullanıcının denetimin düğmelerine tıklayarak artırabileceği veya azaltabileceği sayısal bir değer görüntüler. Aşağıdaki çizimde, bu konuda ele alınan denetim gösterilmektedir NumericUpDown
.
Özel bir NumericUpDown denetimi
Bu konu, aşağıdaki bölümleri içerir:
Ön koşullar
Bu konu başlığında, var olan bir denetim için nasıl yeni ControlTemplate bir denetim oluşturulacağını bildiğiniz, denetim sözleşmesindeki öğelerin ne olduğu hakkında bilgi sahibi olduğunuz ve Denetim için şablon oluşturma başlığında açıklanan kavramları anladığınız varsayılır.
Dekont
Görünümünü özelleştirebilecek bir denetim oluşturmak için sınıfından veya dışındaki UserControlalt sınıflarından Control birini devralan bir denetim oluşturmanız gerekir. öğesinden UserControl devralınan denetim, hızlı bir şekilde oluşturulabilen, ancak kullanmadığı ve görünümünü özelleştiremeyeceğiniz bir ControlTemplate denetimdir.
Parça ve Durum Modeli
Parçalar ve durumlar modeli, bir denetimin görsel yapısını ve görsel davranışını tanımlamayı belirtir. Parça ve durum modelini izlemek için aşağıdakileri yapmanız gerekir:
Bir denetimin görsel yapısını ve görsel davranışını ControlTemplate tanımlayın.
Denetiminizin mantığı denetim şablonunun bölümleriyle etkileşime geçtiğinde bazı en iyi yöntemleri izleyin.
öğesine nelerin dahil ControlTemplateedilmesi gerektiğini belirtmek için bir denetim sözleşmesi sağlayın.
Bir denetimin görsel yapısını ve görsel davranışını ControlTemplate tanımladığınızda, uygulama yazarları kod yazmak yerine yeni ControlTemplate bir oluşturma yoluyla denetiminizin görsel yapısını ve görsel davranışını değiştirebilir. Uygulama yazarlarının içinde hangi FrameworkElement nesnelerin ve durumların tanımlanması ControlTemplategerektiğini bildiren bir denetim sözleşmesi sağlamanız gerekir. Denetiminizin tamamlanmamış ControlTemplatebir öğeyi düzgün bir şekilde işlemesi ControlTemplate için içindeki parçalarla etkileşim kurarken en iyi yöntemlerden bazılarını izlemeniz gerekir. Bu üç ilkeyi izlerseniz, uygulama yazarları WPF ile birlikte gelen denetimler için olabildiğince kolay bir şekilde denetiminiz için bir ControlTemplate oluşturabilir. Aşağıdaki bölümde bu önerilerin her biri ayrıntılı olarak açıklanmaktadır.
ControlTemplate'da Denetimin Görsel Yapısını ve Görsel Davranışını Tanımlama
Parçalar ve durumlar modelini kullanarak özel denetiminizi oluşturduğunuzda, denetimin görsel yapısını ve görsel davranışını ControlTemplate mantığı yerine içinde tanımlarsınız. Denetimin görsel yapısı, denetimi oluşturan nesnelerin bileşimidir FrameworkElement . Görsel davranış, denetimin belirli bir durumdayken görünme şeklidir. Bir denetimin görsel yapısını ve görsel davranışını belirten bir oluşturma ControlTemplate hakkında daha fazla bilgi için bkz . Denetim için şablon oluşturma.
Denetim örneğinde NumericUpDown
görsel yapısı iki RepeatButton denetim ve bir TextBlockiçerir. Bu denetimleri denetimin NumericUpDown
koduna eklerseniz (örneğin, oluşturucusunda) bu denetimlerin konumları değiştirilemez. Denetimin görsel yapısını ve kodundaki görsel davranışını tanımlamak yerine içinde tanımlamanız ControlTemplategerekir. Ardından bir uygulama geliştiricisi düğmelerin konumunu özelleştirmek ve TextBlock değiştirilebileceği için negatif olduğunda Value
hangi davranışın ControlTemplate ortaya çıktığını belirtir.
Aşağıdaki örnekte, denetimin NumericUpDown
görsel yapısı gösterilmektedir. Bu, öğesini artırmak Value
için , RepeatButton azaltmak Value
için ve görüntülemek Value
için öğesini TextBlock içerirRepeatButton.
<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>
Denetimin NumericUpDown
görsel davranışı, değerin negatifse kırmızı yazı tipinde olmasıdır. negatif olduğunda Value
kodun TextBlock değerini değiştirirsenizForeground, NumericUpDown
her zaman kırmızı negatif bir değer gösterilir. öğesine nesneler ekleyerek VisualState içindeki ControlTemplate denetimin ControlTemplategörsel davranışını belirtirsiniz. Aşağıdaki örnekte ve durumlarının VisualState nesneleri gösterilmektedirPositive
.Negative
Positive
ve Negative
birbirini dışlar (denetim her zaman tam olarak ikiden birindedir), bu nedenle örnek nesneleri tek VisualStateGroupbir içine yerleştirirVisualState. Denetim duruma geçtiğinde Negative
, Foreground denetimin TextBlock rengi kırmızıya dönüşür. Denetim durumundayken Positive
, Foreground özgün değerine döner. içindeki nesneleri tanımlama VisualState konusu, Denetim için şablon oluşturma bölümünde daha ayrıntılı olarak ele alınmıştı.ControlTemplate
Dekont
kök FrameworkElement dizininde VisualStateManager.VisualStateGroups ekli özelliği ayarladığınızdan ControlTemplateemin olun.
<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>
Kodda ControlTemplate Bölümlerini Kullanma
Bir ControlTemplate yazar bilerek veya yanlışlıkla nesneleri atlayabilir FrameworkElement veya VisualState atlayabilir, ancak denetiminizin mantığının bu bölümlerin düzgün çalışması gerekebilir. Parça ve durum modeli, denetiminizin eksik FrameworkElement olan veya ControlTemplateVisualState nesnelere dayanıklı olması gerektiğini belirtir. denetiminiz bir özel durum oluşturmamalı veya içinde bir , VisualStateveya VisualStateGroup eksikse ControlTemplatehata FrameworkElementbildirmemelidir. Bu bölümde, nesnelerle FrameworkElement etkileşim kurmak ve durumları yönetmek için önerilen yöntemler açıklanmaktadır.
Eksik FrameworkElement Nesnelerini Tahmin
içinde ControlTemplatenesneleri tanımladığınızdaFrameworkElement, denetiminizin mantığının bazı nesnelerle etkileşim kurması gerekebilir. Örneğin, NumericUpDown
denetim düğmelerin Click olayına abone olarak değerini artırır veya azaltır Value
ve özelliğini TextBlock olarak Value
ayarlarText. Özel ControlTemplate bir öğe veya düğmelerini atlarsa TextBlock , denetimin bazı işlevlerini kaybetmesi kabul edilebilir bir durumdur, ancak denetiminizin hataya neden olmadığından emin olmanız gerekir. Örneğin , değiştirilecek NumericUpDown
Value
düğmeleri içermiyorsaControlTemplate, işlevi kaybolur, ancak kullanan ControlTemplate bir uygulama çalışmaya devam eder.
Aşağıdaki uygulamalar, denetiminizin eksik FrameworkElement nesnelere düzgün yanıt vermesini sağlar:
Kodda
x:Name
başvurmanız gereken her FrameworkElement birinin özniteliğini ayarlayın.Etkileşim kurmanız gereken her FrameworkElement biri için özel özellikler tanımlayın.
Denetiminizin özelliğin küme erişimcisinde işlediği olaylara FrameworkElement abone olun ve aboneliği kaldırın.
yönteminin FrameworkElement 2. adımında OnApplyTemplate tanımladığınız özellikleri ayarlayın. bu, içindeki ControlTemplate öğesinin FrameworkElement denetimi için en erken kullanılabilir olduğu durumdur.
x:Name
FrameworkElement'den ControlTemplatealmak için öğesini kullanın.öğesinin FrameworkElement üyelerine erişmeden önce olup olmadığını
null
denetleyin. isenull
hata bildirmeyin.
Aşağıdaki örneklerde, önceki listedeki önerilere uygun olarak denetimin nesnelerle FrameworkElement nasıl NumericUpDown
etkileşimde olduğu gösterilmektedir.
içindeki denetimin görsel yapısını NumericUpDown
tanımlayan örnekte, artan Value
öğesinin özniteliği olarak UpButton
ayarlanmıştırx:Name
.ControlTemplateRepeatButton Aşağıdaki örnek, içinde ControlTemplatebildirilen öğesini RepeatButton temsil eden adlı UpButtonElement
bir özellik bildirir. Erişimciset
, değilse UpDownElement
null
önce düğmenin Click olayının aboneliğini iptal eder, ardından özelliği ayarlar ve ardından olaya abone olurClick. Burada, adlı DownButtonElement
diğer RepeatButtoniçin tanımlanmış ancak gösterilmeyen bir özellik de vardır.
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);
}
}
}
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
Aşağıdaki örnek, denetimin öğesini OnApplyTemplateNumericUpDown
gösterir. Örnek, GetTemplateChild içindeki nesneleri ControlTemplatealmak FrameworkElement için yöntemini kullanır. Örnek, beklenen türde olmayan belirtilen ada sahip bir FrameworkElement bulduğu durumlarda GetTemplateChild koruma sağlar. Belirtilen ancak yanlış türde olan öğeleri x:Name
yoksaymak da en iyi yöntemdir.
public override void OnApplyTemplate()
{
UpButtonElement = GetTemplateChild("UpButton") as RepeatButton;
DownButtonElement = GetTemplateChild("DownButton") as RepeatButton;
//TextElement = GetTemplateChild("TextBlock") as TextBlock;
UpdateStates(false);
}
Public Overloads Overrides Sub OnApplyTemplate()
UpButtonElement = TryCast(GetTemplateChild("UpButton"), RepeatButton)
DownButtonElement = TryCast(GetTemplateChild("DownButton"), RepeatButton)
UpdateStates(False)
End Sub
Önceki örneklerde gösterilen uygulamaları izleyerek, denetiminizin eksik FrameworkElementolduğunda çalışmaya devam edeceğinden ControlTemplate emin olursunuz.
Durumları Yönetmek için VisualStateManager Kullanma
denetimin VisualStateManager durumlarını izler ve durumlar arasında geçiş yapmak için gereken mantığı gerçekleştirir. öğesine nesneleri ControlTemplateeklediğinizdeVisualState, bunları öğesine VisualStateGroup eklersiniz ve ekli özelliğe eklersiniz VisualStateGroupVisualStateManager.VisualStateGroups, böylece VisualStateManager bunlara erişim sahibi olursunuz.
Aşağıdaki örnek, denetimin ve Negative
durumlarına Positive
karşılık gelen nesneleri gösteren önceki örneği yinelerVisualState. in Storyboard kırmızıyı Negative
VisualState çevirir ForegroundTextBlock . NumericUpDown
Denetim durumundaykenNegative
, durumdaki görsel taslak Negative
başlar. StoryboardNegative
Ardından, denetim duruma döndüğünde durumundaki Positive
durdurulur. Positive
VisualState için durduğunda Negative
Storyboard özgün rengine döndürdüğünden öğesinin Foreground içermesi Storyboard gerekmez.
<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>
öğesine bir ad verildiğini TextBlock , ancak TextBlock denetiminin mantığı hiçbir zaman öğesine başvurmadığı için NumericUpDown
öğesinin için denetim sözleşmesinde TextBlockolmadığını unutmayın. içinde ControlTemplate başvuruda bulunan öğelerin adları vardır, ancak denetim için yeni ControlTemplate bir öğenin bu öğeye başvurması gerekmeyebileceği için denetim sözleşmesinin parçası olması gerekmez. Örneğin, için NumericUpDown
yeni ControlTemplate bir oluşturan kişi, değerini değiştirerek Foregroundnegatif olduğunu Value
belirtmemeye karar verebilir. Bu durumda, ne kod ne de ControlTemplate ada göre başvurur TextBlock .
Denetimin mantığı, denetimin durumunu değiştirmekle sorumludur. Aşağıdaki örnek, denetimin NumericUpDown
0 veya daha büyük olduğunda Value
duruma gitmek Positive
için yöntemini ve Negative
0'dan küçük olduğunda durumunu çağırdığını GoToStateValue
gösterir.
if (Value >= 0)
{
VisualStateManager.GoToState(this, "Positive", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Negative", useTransitions);
}
If Value >= 0 Then
VisualStateManager.GoToState(Me, "Positive", useTransitions)
Else
VisualStateManager.GoToState(Me, "Negative", useTransitions)
End If
yöntemi, GoToState görsel taslakları uygun şekilde başlatmak ve durdurmak için gereken mantığı gerçekleştirir. Bir denetim durumunu değiştirmek için çağırdığında GoToState aşağıdakileri VisualStateManager yapar:
Denetimin VisualState bir Storyboard'i olacaksa, görsel taslak başlar. Ardından, denetimin VisualState geldiği öğede bir Storyboardvarsa görsel taslak sona erer.
Denetim zaten belirtilen durumdaysa hiçbir GoToState işlem gerçekleştirmez ve döndürür
true
.Belirtilen durum içinde ControlTemplate
control
GoToState yoksa hiçbir eylem gerçekleştirmez ve döndürür.false
VisualStateManager ile Çalışmak için En İyi Yöntemler
Denetiminizin durumlarını korumak için aşağıdakileri yapmanız önerilir:
Durumunu izlemek için özellikleri kullanın.
Durumlar arasında geçiş yapmak için bir yardımcı yöntem oluşturun.
Denetim, NumericUpDown
veya Negative
durumunda olup olmadığını Positive
izlemek için özelliğini kullanırValue
. Denetim ayrıca NumericUpDown
özelliğini izleyen ve UnFocused
durumlarını IsFocused da tanımlarFocused
. Denetimin bir özelliğine doğal olarak karşılık olmayan durumlar kullanırsanız, durumu izlemek için özel bir özellik tanımlayabilirsiniz.
Tüm durumları güncelleştiren tek bir yöntem, çağrıları VisualStateManager merkezileştirir ve kodunuzu yönetilebilir durumda tutar. Aşağıdaki örnek, denetimin NumericUpDown
yardımcı yöntemi olan UpdateStates
öğesini gösterir. 0'dan büyük veya 0'a Positive
eşit olduğundaValue
, Control durumundadır. 0'dan küçük olduğunda Value
denetim durumundadır Negative
. olduğundaIsFocused, denetim durumundadırFocused
; aksi takdirde, durumundadırUnfocused
.true
Denetim, hangi durumun değiştiğinden bağımsız olarak durumunu değiştirmesi gerektiğinde çağırabilir UpdateStates
.
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);
}
}
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
Denetim zaten bu durumdayken bir durum adı GoToState geçirirseniz hiçbir GoToState şey yapmaz, bu nedenle denetimin geçerli durumunu denetlemeniz gerekmez. Örneğin, bir negatif sayıdan başka bir negatif sayıya değişirse Value
, durum için görsel taslak Negative
kesintiye uğramaz ve kullanıcı denetimde bir değişiklik görmez.
, VisualStateManager çağırdığınızda GoToStatehangi durumdan çıkabileceğinizi belirlemek için nesneleri kullanırVisualStateGroup. Denetimi, içinde ControlTemplate tanımlanan her VisualStateGroup biri için her zaman bir durumdadır ve yalnızca aynı VisualStateGroupdurumdan başka bir duruma geçtiğinde bir durumdan ayrılır. Örneğin, denetimin öğesi bir içindeki VisualStateGroup ve Negative
VisualState nesnelerini ve diğerindeki Focused
ve Unfocused
VisualState nesnelerini tanımlarPositive
.NumericUpDown
ControlTemplate (ve değerini bu konudaki Tam Örnek bölümünde görebilirsiniz Unfocused
VisualStateFocused
. Denetim durumundan Positive
duruma Negative
geçtiğinde veya tam tersi durumda kaldığında, denetim ya da Unfocused
Focused
durumunda kalır.
Bir denetimin durumunun değişebileceği üç tipik yer vardır:
öğesine ControlTemplate uygulandığında Control.
Bir özellik değiştiğinde.
Bir olay oluştuğunda.
Aşağıdaki örneklerde, bu durumlarda denetimin durumunun NumericUpDown
güncelleştirilmesi gösterilmektedir.
uygulandığında OnApplyTemplate denetimin doğru durumda görünmesi için yöntemindeki denetimin ControlTemplate durumunu güncelleştirmeniz gerekir. Aşağıdaki örnek, denetimin uygun durumlarda olduğundan emin olmak için öğesini çağırır UpdateStates
OnApplyTemplate . Örneğin, bir NumericUpDown
denetim oluşturduğunuzu ve ardından bunu Foreground yeşil ve Value
-5 olarak ayarladığınızı varsayalım. denetimine uygulandığında NumericUpDown
ControlTemplate çağrı UpdateStates
yapmazsanız, denetim durumunda değildir Negative
ve değer kırmızı yerine yeşil olur. Denetimi Negative
duruma getirmek için aramanız UpdateStates
gerekir.
public override void OnApplyTemplate()
{
UpButtonElement = GetTemplateChild("UpButton") as RepeatButton;
DownButtonElement = GetTemplateChild("DownButton") as RepeatButton;
//TextElement = GetTemplateChild("TextBlock") as TextBlock;
UpdateStates(false);
}
Public Overloads Overrides Sub OnApplyTemplate()
UpButtonElement = TryCast(GetTemplateChild("UpButton"), RepeatButton)
DownButtonElement = TryCast(GetTemplateChild("DownButton"), RepeatButton)
UpdateStates(False)
End Sub
Genellikle bir özellik değiştiğinde denetimin durumlarını güncelleştirmeniz gerekir. Aşağıdaki örnekte yöntemin tamamı ValueChangedCallback
gösterilmektedir. Değişiklik ValueChangedCallback
yapıldığında Value
çağrıldığından, yöntem pozitiften negatife veya tersine değişmesi durumunda Value
çağrı yaparUpdateStates
. Değişiklikler olduğunda Value
çağrı UpdateStates
yapmak kabul edilebilir ancak pozitif veya negatif kalır, çünkü bu durumda denetim durumları değiştirmez.
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));
}
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
Bir olay gerçekleştiğinde durumları da güncelleştirmeniz gerekebilir. Aşağıdaki örnekte, olayı işlemek için üzerindeki çağrılar UpdateStates
gösterilmektedir.GotFocusNumericUpDown
Control
protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
UpdateStates(true);
}
Protected Overloads Overrides Sub OnGotFocus(ByVal e As RoutedEventArgs)
MyBase.OnGotFocus(e)
UpdateStates(True)
End Sub
denetiminizin VisualStateManager durumlarını yönetmenize yardımcı olur. kullanarak VisualStateManager, denetiminizin durumlar arasında doğru şekilde geçişini sağlarsınız. ile VisualStateManagerçalışmak için bu bölümde açıklanan önerileri izlerseniz, denetiminizin kodu okunabilir ve sürdürülebilir kalır.
Denetim Sözleşmesini Sağlama
Yazarların ControlTemplate şablona ne ekleyeceklerini bilmesi için bir denetim sözleşmesi sağlarsınız. Denetim sözleşmesinin üç öğesi vardır:
Denetimin mantığının kullandığı görsel öğeler.
Denetimin durumları ve her durumun ait olduğu grup.
Denetimi görsel olarak etkileyen ortak özellikler.
Yeni ControlTemplate bir öğe oluşturan birinin denetimin mantığının hangi FrameworkElement nesneleri kullandığını, her nesnenin ne tür olduğunu ve adının ne olduğunu bilmesi gerekir. Bir ControlTemplate yazarın denetimin içinde bulunabileceği her olası durumun adını ve VisualStateGroup durumunu da bilmesi gerekir.
Örneğine döndüğünüzdeNumericUpDown
, denetim öğesinin aşağıdaki FrameworkElement nesnelere sahip olmasını beklerControlTemplate:
RepeatButton adlı bir
UpButton
ad.A RepeatButton çağrılır
DownButton.
Denetim aşağıdaki durumlarda olabilir:
İçinde
ValueStates
VisualStateGroupPositive
Negative
İçinde
FocusStates
VisualStateGroupFocused
Unfocused
Denetimin beklediği FrameworkElement nesneleri belirtmek için, beklenen öğelerin adını ve türünü belirten öğesini kullanırsınız TemplatePartAttribute. Bir denetimin olası durumlarını belirtmek için, durumunun adını ve ait olduğu VisualStateGroup adı belirten öğesini kullanırsınızTemplateVisualStateAttribute. ve TemplateVisualStateAttribute öğesini TemplatePartAttribute denetimin sınıf tanımına yerleştirin.
Denetiminizin görünümünü etkileyen tüm ortak özellikler de denetim sözleşmesinin bir parçasıdır.
Aşağıdaki örnek, denetimin FrameworkElement nesnesini ve durumlarını NumericUpDown
belirtir.
[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; }
}
<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 TextAlignmentProperty As DependencyProperty
Public Shared ReadOnly TextDecorationsProperty As DependencyProperty
Public Shared ReadOnly TextWrappingProperty As DependencyProperty
Public Property TextAlignment() As TextAlignment
Public Property TextDecorations() As TextDecorationCollection
Public Property TextWrapping() As TextWrapping
End Class
Tam Örnek
Aşağıdaki örnek, denetimin tamamıdır ControlTemplateNumericUpDown
.
<!--This is the contents of the themes/generic.xaml file.-->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://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>
Aşağıdaki örnekte için mantığı gösterilmektedir NumericUpDown
.
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; }
}
}
}
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
Public Sub New(ByVal id As RoutedEvent,
ByVal num As Integer)
Value = num
RoutedEvent = id
End Sub
Public ReadOnly Property Value() As Integer
End Class
Ayrıca bkz.
.NET Desktop feedback