Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Windows Presentation Foundation (WPF), görünümü özelleştirilebilen bir denetim oluşturmanızı sağlar. Örneğin, yeni bir CheckBox oluşturarak bir ControlTemplate'nin görünümünü ayar özelliklerinin yapabileceğinden daha fazla değiştirebilirsiniz. Aşağıdaki çizimde, varsayılan bir CheckBox kullanan bir ControlTemplate ve özel bir CheckBox kullanan bir ControlTemplate 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ımcı araçları parça ve durum modelini desteklediğinden, 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, NumericUpDownbu 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 çizim, bu konuda ele alınan NumericUpDown denetimini göstermektedir.
Özel bir NumericUpDown denetimi
Bu konu aşağıdaki bölümleri içerir:
Önkoş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.
Uyarı
Görünümünü özelleştirebileceğiniz bir denetim oluşturmak için, Control veya onun alt sınıflarında biri hariç, UserControl sınıfının ya da alt sınıflarından birini devralan bir denetim oluşturmanız gerekir. Bir UserControl öğesinden devralınan kontrol, hızlı bir şekilde oluşturulabilir, ancak bir ControlTemplate kullanmaz ve görünümünü özelleştiremezsiniz.
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 ControlTemplate içindeki görsel yapısını ve davranışını tanımlayın.
Denetiminizin mantığı denetim şablonunun bölümleriyle etkileşime geçtiğinde bazı en iyi yöntemleri izleyin.
ControlTemplate öğesine nelerin dahil edilmesi gerektiğini belirtmek için bir kontrol 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ına hangi FrameworkElement nesnelerin ve durumların ControlTemplate içinde tanımlanması gerektiğini bildiren bir denetim sözleşmesi sağlamanız gerekir. ControlTemplate içindeki parçalarla etkileşim kurarken, denetiminizin tamamlanmamış bir ControlTemplate öğesini düzgün şekilde işlemesi için en iyi uygulamalardan bazılarını takip etmelisiniz. Bu üç ilkeyi izlerseniz, uygulama yazarları WPF ile birlikte gelen kontroller için olduğu gibi, kontrolünüz için de bir ControlTemplate kolaylıkla 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. Kontrolün ekran yapısını ve kodun içindeki görsel davranışını tanımlamak yerine, bunları ControlTemplate içinde tanımlamalısınız. Ardından bir uygulama geliştiricisi, düğmelerin konumunu özelleştirir ve TextBlock değiştirilebileceği için Value negatif olduğunda hangi davranışın ortaya çıktığını belirtir.
Aşağıdaki örnek, kontrolün NumericUpDown görsel yapısını göstermektedir. Bu yapı, RepeatButton artırmak için bir Value, RepeatButton azaltmak için bir Value ve TextBlock görüntülemek için bir Value içerir.
<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. Kodda Foreground negatif olduğunda, TextBlock değerini Value ile değiştirirseniz, NumericUpDown her zaman kırmızı negatif bir değeri gösterir.
ControlTemplate'ye VisualState nesneleri ekleyerek, ControlTemplate denetiminin görsel davranışını belirtirsiniz. Aşağıdaki örnek, VisualState nesnelerinin Positive ve Negative durumları için gösterildiğini belirtilmektedir.
Positive ve Negative birbirini dışlayıcıdır (kontrol her zaman yalnızca ikisinden birindedir), bu nedenle örnek, VisualState nesneleri tek bir VisualStateGroup içine yerleştirir. Denetim Negative durumuna geçtiğinde, Foreground'nin TextBlock rengi kırmızıya dönüşür. Denetim Positive durumundayken, Foreground özgün değerine döner. içindeki nesneleri tanımlama VisualState konusu, ControlTemplate bölümünde daha ayrıntılı olarak ele alınmıştı.
Uyarı
kök VisualStateManager.VisualStateGroups dizininde FrameworkElement 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 nesneleri bilerek veya yanlışlıkla FrameworkElement veya VisualState atlayabilir, ancak denetiminiz bu bölümlerin düzgün çalışmasına ihtiyaç duyabilir. Parça ve durum modeli, denetiminizin eksik ControlTemplate veya FrameworkElementVisualState nesneler olduğunda dayanıklı olması gerektiğini belirtir. Denetiminiz FrameworkElement öğesinde VisualState, VisualStateGroup veya ControlTemplate eksikse bir özel durum oluşturmamalı veya hata bildirmemelidir. Bu bölümde, nesnelerle FrameworkElement etkileşim kurmak ve durumları yönetmek için önerilen yöntemler açıklanmaktadır.
Eksik FrameworkElement Nesnelerini Öngör
FrameworkElement nesnelerini ControlTemplate içinde tanımladığınızda, denetiminizin mantığının bazılarıyla 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 Text olarak TextBlockayarlarValue. Özel ControlTemplate öğesi TextBlock veya düğmeleri atladığında, denetimin bazı işlevlerini kaybetmesi kabul edilebilir bir durumdur, ancak denetiminizin hataya neden olmadığından emin olmanız gerekir. Örneğin, bir ControlTemplate değişiklik yapmak için Value düğmelerini içermiyorsa, NumericUpDown bu işlevini kaybeder, ancak ControlTemplate kullanan bir uygulama çalışmaya devam eder.
Aşağıdaki uygulamalar, denetiminizin eksik FrameworkElement nesnelere düzgün yanıt vermesini sağlar:
Her bir
x:Nameöğesini kodda referans vermeniz gerektiğinde FrameworkElement özniteliğini ayarlayın.Etkileşim kurmanız gereken her FrameworkElement biri için özel özellikler tanımlayın.
Kontrolünüzün FrameworkElement özelliğinin küme erişimcisindeki işlediği olaylara abone olun ve abonelikten çıkın.
2. adımda tanımladığınız FrameworkElement özellikleri OnApplyTemplate yönteminde ayarlayın. Bu, FrameworkElement'ın ControlTemplate içindeki denetime en erken sunulma zamanıdır.
x:Nameöğesini FrameworkElement'den ControlTemplate almak için kullanın.Şunu kontrol edin ki FrameworkElement üyelerine erişmeden önce
nulldeğildir. Eğernullise, hata bildirmeyin.
Aşağıdaki örnekler, önceki listedeki önerilere uygun olarak NumericUpDown denetiminin FrameworkElement nesnelerle etkileşimde nasıl bulunduğunu göstermektedir.
NumericUpDown içindeki ControlTemplate kontrolünün görsel yapısını tanımlayan örnekte, RepeatButton değerini artıran Value, x:Name özniteliği UpButton olarak ayarlanmıştır. Aşağıdaki örnek, UpButtonElement içinde bildirilen RepeatButton öğesini temsil eden, ControlTemplate adlı bir özellik bildirir. Önce setClick değilse erişimci, düğmenin UpDownElement olayının aboneliğini iptal eder, ardından özelliği ayarlar ve sonra null olayına abone olur. Diğer RepeatButton için tanımlanmış, ancak burada gösterilmeyen DownButtonElement adlı 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, OnApplyTemplate denetimi için NumericUpDown'yı gösterir. Örnek, GetTemplateChild içindeki FrameworkElement nesneleri almak için ControlTemplate yöntemini kullanır. Örnek, GetTemplateChild'ın beklenen türde olmayan belirtilen ada sahip bir FrameworkElement bulduğu durumlara karşı önlem alır. Belirtilen x:Name'e sahip ancak yanlış türde olan öğeleri yoksaymak da iyi bir uygulamadır.
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ı takip ederek, ControlTemplate eksik bir FrameworkElement olduğunda denetiminizin çalışmaya devam edeceğinden 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. VisualState nesneleri ControlTemplate öğesine eklediğinizde, bunları VisualStateGroup öğesine ekler ve VisualStateGroup öğesini VisualStateManager.VisualStateGroups ekli özelliğine eklersiniz, böylece VisualStateManager bunlara erişebilir.
Aşağıdaki örnek, kontrolün VisualState ve Positive durumlarına karşılık gelen Negative nesnelerini gösteren önceki örneği yineler.
Storyboard içindeki NegativeVisualState, Foreground'ün TextBlock'ünü kırmızıya çevirir.
NumericUpDown denetim durumundayken, Negative durumunda olan görsel taslak Negative başlar.
Storyboard
Negative durumundayken, kontrol Positive durumuna döndüğünde durur.
Positive
VisualState öğesinin Storyboard içermesi gerekmez çünkü Storyboard için Negative durduğunda, Foreground özgün rengine döner.
<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>
TextBlock öğesine bir ad verildiğine dikkat edin, ancak TextBlock kontrolü için NumericUpDown kontrol sözleşmesinde TextBlock yoktur çünkü kontrolün mantığı NumericUpDown öğesine hiçbir zaman başvurmaz.
ControlTemplate içinde atıfta bulunulan öğelerin adları vardır, ancak yeni bir ControlTemplate için denetim sözleşmesinin parçası olmak zorunda değillerdir çünkü bu öğeye atıfta bulunulmasına gerek kalmayabilir. Örneğin, ControlTemplate için yeni bir NumericUpDown oluşturan kişi, Value değiştirerek Foreground'nin negatif olduğunu göstermemeye karar verebilir. Bu durumda, ne kod ne de ControlTemplateTextBlock adına atıfta bulunur.
Denetimin mantığı, denetimin durumunu değiştirmekle sorumludur. Aşağıdaki örnek, NumericUpDown denetiminin, GoToState 0 veya daha büyük olduğunda Positive yöntemini çağırarak Value durumuna geçtiğini ve GoToState 0'dan küçük olduğunda Value durumuna geçtiğini 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
GoToState yöntemi, görsel taslakları uygun şekilde başlatmak ve durdurmak için gereken işlemi gerçekleştirir. Bir denetim durumunu değiştirmek için GoToState’yi çağırdığında, VisualStateManager aşağıdakileri 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 ControlTemplate içindeki
controlbulunmadığında, GoToState hiçbir eylem gerçekleştirmez vefalsedöndürür.
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.
NumericUpDown denetimi, Value özelliğini kullanarak Positive veya Negative durumunda olup olmadığını izler. Denetim ayrıca NumericUpDown ve Focused durumlarını, UnFocused özelliğini izleyen IsFocused tanımlar. 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 Value eşit olduğundaControl, Positive durumundadır.
Value 0'dan küçük olduğunda, denetim Negative durumundadır.
IsFocused
true olduğunda, denetim Focused durumundadır; aksi takdirde, Unfocused durumundadır. 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, Value, bir negatif sayıdan başka bir negatif sayıya değişirse, Negative durumu için taslak kesintiye uğramaz ve kullanıcı denetimde bir değişiklik görmez.
VisualStateManager, VisualStateGroup nesnelerini, GoToState çağırdığınızda hangi durumdan çıkılacağını belirlemek için kullanır. Kontrol, içinde VisualStateGroup tanımlanan her ControlTemplate için her zaman bir durumdadır ve yalnızca aynı VisualStateGroup içinde başka bir duruma geçtiğinde bir durumdan ayrılır. Örneğin, ControlTemplate denetiminin NumericUpDown öğesi, bir Positive içinde Negative ile VisualStateVisualStateGroup nesnelerini ve diğerinde Focused ile UnfocusedVisualState nesnelerini tanımlar. (tr-TR: Focused ve UnfocusedVisualState değerlerini bu konudaki Tam Örnek bölümünde görebilirsiniz. Denetim, Positive durumundan Negative durumuna geçerken veya tam tersi durumda, denetim ya Focused ya da Unfocused 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.
OnApplyTemplate yönteminde denetimi güncelleyerek ControlTemplate uygulandığında denetimin doğru durumda görünmesini sağlamalısınız. Aşağıdaki örnek, denetimin uygun durumda olduğunu sağlamak için UpdateStates öğesini OnApplyTemplate içinde çağırır. Örneğin, bir NumericUpDown denetim oluşturduğunuzu ve ardından bunu Foreground yeşil ve Value -5 olarak ayarladığınızı varsayalım. Eğer UpdateStatesControlTemplate kontrolüne uygulandığında NumericUpDown çağrısı yapmazsanız, kontrol Negative durumunda değildir ve değer kırmızı yerine yeşil olur. Denetimi UpdateStates duruma getirmek için aramanız Negative 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 durumunu güncelleştirmeniz gerekir. Aşağıdaki örnek, ValueChangedCallback yönteminin tamamını göstermektedir.
ValueChangedCallback
Value değiştiğinde çağrıldığından, yöntem UpdateStates pozitiften negatife veya tersine geçtiğinde Value çağırır.
UpdateStates değiştiğinde ancak pozitif veya negatif kalmaya devam ettiğinde, Value çağrısı yapmak kabul edilebilir çünkü bu durumda kontrol durumu değişmez.
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 örnek, NumericUpDown'nün UpdateStates üzerindeki Control'i GotFocus olayını ele almak için çağırdığını göstermektedir.
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
VisualStateManager, denetiminizin 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.
Kontrol 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 kamu özellikleri.
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.
NumericUpDown örneğine dönersek, denetim öğesinin ControlTemplate'nin aşağıdaki FrameworkElement nesnelere sahip olmasını beklediğini göreceğiz.
RepeatButton adında bir
UpButtonBir RepeatButton olarak adlandırılan
DownButton.
Denetim aşağıdaki durumlarda olabilir:
İçinde
ValueStatesVisualStateGroupPositiveNegative
İçinde
FocusStatesVisualStateGroupFocusedUnfocused
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, durumun adını belirten TemplateVisualStateAttribute öğesini ve ait olduğu VisualStateGroup öğesini kullanırsınız. TemplatePartAttribute ve TemplateVisualStateAttribute öğelerini kontrolün sınıf tanımına yerleştirin.
Denetiminizin görünümünü etkileyen tüm kamu malları da denetim sözleşmesinin bir parçasıdır.
Aşağıdaki örnek, FrameworkElement nesnesini ve NumericUpDown denetimi için durumları 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, ControlTemplate kontrolü için tüm NumericUpDown'yu göstermektedir.
<!--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 bakınız
.NET Desktop feedback