Rédacteurs valeur de propriété pour les contrôles Silverlight
Présentation
Cela fait partie de la série sur les changements de conception du temps de mise en œuvre de Silverlight Toolkit Mars 2009 Communiqué . Ce post explique comment améliorer l'expérience de la modification de propriétés pour les contrôles Silverlight en utilisant l'éditeur valeur de la propriété et le convertisseur de type. Je vais commencer par décrire l'architecture globale des biens d'édition dans WPF / Silverlight cadre de l'extensibilité du concepteur, puis d'utiliser des exemples dans Silverlight Toolkit Mars 2009 Communiqué de démontrer comment il est fait, et des problèmes uniques / astuces en matière de développement Silverlight temps de conception.
Propriété d'édition d'architecture
Visuellement l'édition des propriétés d'objet est une partie importante de designers. Les concepteurs ne savent généralement pas comment rendre les propriétés de type personnalisé (struct, classe ou interface), et encore moins de fournir une interface d'édition de Nice de l'utilisateur. Les développeurs de contrôles ont généralement besoin de fournir TypeConverter , PropertyValueEditor , ou les deux, pour fournir le rendu / édition sérialisation XAML pour l'interface utilisateur et les propriétés des types personnalisés.
Le cadre extensibilité du concepteur définit trois types de rédacteur en chef valeur de la propriété: éditeur en ligne, éditeur étendu, et l'éditeur de dialogue, chacun mis en œuvre par une classe:
Interface utilisateur de modification de ces éditeurs sont définis par DataTemplate . La propriété en cours d'édition est exposé à l'éditeur que DataContext du PropertyValue type. Editeur UI normalement lier à la propriété sous-jacente étant édités via l'une des trois propriétés de PropertyValue : Valeur , StringValue , ou Collection .
PropertyValueEditor
PropertyValueEditor titulaire d'un éditeur en ligne unique défini par InlineEditorTemplate propriété. Éditeur en ligne apparaît à l'intérieur de la fenêtre de propriétés. Voici un exemple simple de InlineEditorTemplate qui utilise une zone de texte à afficher et d'éditer une propriété:
x:Key ="TextBoxEditor" > <X DataTemplate: Key = "TextBoxEditor"> Text ="{Binding Path=Value}" /> <Texte TextBox = "{Binding Path = valeur}" /> > </ DataTemplate>
ExtendedPropertyValueEditor
x:Key ="inlineEditor" > <X DataTemplate: Key = "inlineEditor"> Content ="..." Command ="{x:Static PropertyEditing:PropertyValueEditorCommands.ShowDialogEditor}" /> <Contenu Bouton ="..." Command = "{x: PropertyEditing statique: PropertyValueEditorCommands.ShowDialogEditor}" /> > </ DataTemplate> x:Name ="slider" Value ="{Binding Path=Value}" /> <Slider X: Nom = "slider" value = "{Binding Path = valeur}" /> > </ DataTemplate>
DialogPropertyValueEditor
DialogPropertyValueEditor a deux éditeurs aussi: l'éditeur en ligne héritée de PropertyValueEditor , et un éditeur de dialogue supplémentaire définie par DialogEditorTemplate propriété. L'éditeur de dialogue est généralement surgi par l'éditeur en ligne via PropertyValueEditorCommands . ShowDialogEditor commande. Voici un exemple simple:
x:Key ="inlineEditor" > < Button Content ="..." Command ="{x:Static <DataTemplate x: Key = "inlineEditor"> Contenu Bouton <="..." Command = "{x: Static ="Center" HorizontalAlignment ="Right" Margin ="0,0,4,4" /> < TextBox Text ="{Binding Nom: "VerticalAlignment =" center "HorizontalAlignment =" Droite "Margin =" 0,0,4,4 "/> <TextBox Text =" {Binding
Mettre en œuvre éditeur de propriétés personnalisé
Pour mettre en œuvre un éditeur de propriétés personnalisé pour un contrôle Silverlight:
- implémenter une classe de propriétés personnalisées éditeur
- hériter de PropertyValueEditor, ExtendedPropertyValueEditor ou DialogPropertyValueEditor
- définir son modèle de rédacteur (s): InlineEditorTemplate , ExtendedEditorTemplate , et / ou DialogEditorTemplate ;
- Associé de l'éditeur de propriété personnalisé avec une propriété d'un contrôle Silverlight via un appel AddCustomAttributes, quelque chose comme
- Un éditeur de propriété correctement mis en œuvre valeur doit satisfaire aux exigences suivantes:
- L'éditeur de valeur de la propriété doit être conçu de sorte que l'éditeur en ligne et de pièces éditeur étendu peut être utilisé indépendamment.
- Un éditeur de valeur de la propriété ne doit pas stocker l'état. Éditeurs de valeur de propriété sont apatrides, pourrait être mis en cache par une mise en œuvre d'hôte, et peut être réutilisé à travers les valeurs des propriétés multiples.
- Un éditeur de valeur de la propriété ne doit pas supposer que seule une partie éditeur de valeur (voir / inline / étendue) de contrôle est actif à un instant donné. Par exemple, une boîte de dialogue aurait pu voir la partie, une partie en ligne, et une partie interface utilisateur étendue actifs au même moment.
- Un contrôle mis en œuvre dans le cadre d'un éditeur de valeur de la propriété ne doit pas stocker l'état. Un contrôle mis en œuvre dans le cadre d'un éditeur de valeur ne doit pas supposer qu'il ne sera lié à une valeur de propriété. Les contrôles peuvent être recyclés à changer les valeurs des propriétés différentes. Toute information qui est mise en cache doit être rincé si le modèle de données est mise à jour.
- Un contrôle mis en œuvre dans le cadre d'un éditeur de valeur de la propriété ne doit pas faire de suppositions sur l'hôte ou son contrôle parent. Les mécanismes de communication qui devraient seulement être utilisés sont les PropertyValue modèle de données, en passant par le DataContext , et l'ensemble standard de commandes.
- Vous pouvez lire plus sur l'architecture d'édition de propriété et de cadre de l'extensibilité du concepteur sur le site MSDN.
Référence WPF et Silverlight assemblées
Un assemblage de conception est un assemblage de .NET / WPF chargé par Visual Studio ou de fusion, mais il doit faire référence à des assemblées Silverlight (au moins l'assemblage Silverlight contrôle, il fournit des fonctionnalités de conception pour le temps). Cela peut créer une ambiguïté de référence pour l'assemblage de projet conception: il a parfois besoin de faire référence au même type pleinement qualifié dans les deux WPF et Silverlight, disons dans les deux System.Windows.FrameworkElement PresentationFramework.dll de WPF et de Silverlight System.Windows.dll. Pour éviter la confusion de Visual Studio, vous pouvez utiliser un alias extern pour distinguer les références WPF et Silverlight.
Par exemple, voir la capture d'écran ci-dessous pour le projet Controls.DataVisualization.Toolkit.Design dans Silverlight 3 Toolkit en Mars 2009 Communiqué :
- les références de projet à la fois PresentationFramework et System.Windows, mais System.Windows est sous un alias Silverlight, au lieu de l'alias global par défaut. La référence System.Windows dans Silverlight est un alias persisté dans csproj fichier ci-dessous.:
Include ="System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, <Référence Inclure = "System.Windows, Version = 2.0.5.0, Culture = neutral, PublicKeyToken = 7cec85d7bea7798e,
- dans le code source, nous ajoutons
extern alias Silverlight; utilisant SSW = Silverlight:: System.Windows;
et la référence FrameworkElement Silverlight via SSO:: FrameworkElement.
TextBoxEditor
Il ya un éditeur en ligne TextBoxEditor dans le projet de Controls.DataVisualization.Toolkit.Design pour afficher et éditer la propriété Titre du type d'objet pour [Zone | Bar | Bulle | Colonne | Ligne | Tarte | Scatter] Série et le contrôle graphique, comme indiqué ci-dessous:
Texte remplie dans le champ Titre de la fenêtre Propriétés de droite montre automatiquement en XAML et les vues de conception dans le milieu.
La mise en œuvre de TextBoxEditor suivi les étapes décrites précédemment:
- TextBoxEditor est une sous-classe de PropertyValueEditor. Il fixe InlineEditorTemplate dans son constructeur par défaut. Ici, j'ai utilisé le code XAML au lieu de construire le DataTemplate , parce que quelque part, je ne peut pas obtenir XAML à construire, et tout en XAML peut être réécrit en C # de toute façon.
- Il est associé à Chart.Title dans ChartMetadata.cs (voir Mise en oeuvre Feature Time Design dans Silverlight Toolkit pour plus d'informations sur les fichiers Metadata.cs *)
TextBoxEditor.cs:
/ / (C) Copyright Microsoft Corporation. / / Cette source est soumis à la licence Microsoft Public (Ms-PL). / / S'il vous plaît voir http://go.microsoft.com/fwlink/?LinkID=131993 pour plus de détails. / / Tous les autres droits réservés. using System; utilisant Microsoft.Windows.Design.PropertyEditing; utilisant System.Windows; utilisant System.Windows.Data; namespace System.Windows.Controls.DataVisualization.Design { / / / <summary> / / / Simple éditeur en ligne TextBox. / / / </ Summary> public partiel de classe TextBoxEditor: PropertyValueEditor { / / / <summary> / / / Préserver le prototype du constructeur à partir PropertyValueEditor. / / / </ Summary> / / / <param Template name="inlineEditorTemplate"> éditeur en ligne. </ Param> publique TextBoxEditor (DataTemplate inlineEditorTemplate) : Base (inlineEditorTemplate) {} / / / <summary> / / Constructeur par défaut / construit le TextBox par défaut éditeur en ligne modèle. / / / </ Summary> publique TextBoxEditor () { (TextBox)); FrameworkElementFactory textBox = new FrameworkElementFactory (typeof (TextBox)); Reliure Reliure Reliure = new (); ); binding.Path = new PropertyPath ("Value"); binding.Mode = BindingMode.TwoWay; textBox.SetBinding (TextBox.TextProperty, reliure); DataTemplate DataTemplate dt = new (); dt.VisualTree = zone de texte; InlineEditorTemplate = dt; } } }
CultureInfoEditor
Mon collègue RJ écrit CultureInfoEditor éditeur en ligne pour des biens TimePicker.Culture, parce que le défaut de montage d'expérience pour CultureInfo dans Blend mène à nulle XAML. Ci-dessous copie d'écran montre l'CultureInfoEditor utilise une zone de liste déroulante pour afficher tous les CultureInfo et génère droit XAML.
CultureInfoEditor est un exemple plus complexe que TextBoxEditor, montre vraiment comment la propriété sous-jacente est associé à l'éditeur via la propriété DataContext.
CultureInfoEditor.cs:
/ / (C) Copyright Microsoft Corporation. / / Cette source est soumis à la licence Microsoft Public (Ms-PL). / / S'il vous plaît voir http://go.microsoft.com/fwlink/?LinkID=131993 pour plus de détails. / / Tous les autres droits CultureInfoEditor : PropertyValueEditor { /// <summary> /// The ComboBox being used to edit the value. /// </summary> private ComboBox _owner; /// <summary> /// Preserve the constructor prototype from PropertyValueEditor. /// </summary> /// <param name="inlineEditorTemplate">Inline editor template.</param> public CultureInfoEditor(DataTemplate inlineEditorTemplate) : base (inlineEditorTemplate) { } /// <summary> /// Default constructor builds a ComboBox inline editor template. /// </summary> public CultureInfoEditor() { // not using databinding here because Silverlight does not support // the WPF CultureConverter that is used by Blend. FrameworkElementFactory comboBox = new FrameworkElementFactory( typeof (ComboBox)); comboBox.AddHandler( ComboBox.LoadedEvent, new RoutedEventHandler( (sender, e) => { _owner = (ComboBox) sender; _owner.SelectionChanged += EditorSelectionChanged; INotifyPropertyChanged data = _owner.DataContext as INotifyPropertyChanged; if (data != null ) { data.PropertyChanged += DatacontextPropertyChanged; } _owner.DataContextChanged += CultureDatacontextChanged; })); comboBox.SetValue(ComboBox.IsEditableProperty, false ); comboBox.SetValue(ComboBox.DisplayMemberPathProperty, "DisplayName" ); comboBox.SetValue(ComboBox.ItemsSourceProperty, CultureInfo.GetCultures(CultureTypes.SpecificCultures)); DataTemplate dt = new DataTemplate(); dt.VisualTree = comboBox; InlineEditorTemplate = dt; } /// <summary> /// Handles the SelectionChanged event of the owner control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.Windows.Controls.SelectionChangedEventArgs"/> /// instance containing the event data.</param> private void EditorSelectionChanged( object sender, SelectionChangedEventArgs e) { // serialize with name. object DataContext = _owner.DataContext; DataContext .GetType() .GetProperty( "Value" , BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty) .SetValue(DataContext, ((CultureInfo)_owner.SelectedItem).Name, new object [] { }); } /// <summary> /// Handles the PropertyChanged event of the context object. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param> private void DatacontextPropertyChanged( object sender, PropertyChangedEventArgs e) { // deserialize from name. if (e.PropertyName == "Value" ) { object value = sender .GetType() .GetProperty( "Value" , BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty) .GetValue(sender, new object [] { }); if ( value != null ) { if ( value is string ) { CultureInfo setCulture = new CultureInfo( value .ToString()); _owner.SelectedItem = setCulture; } } } } /// <summary> /// Called when the context is changed. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> private void CultureDatacontextChanged( object sender, DependencyPropertyChangedEventArgs e) { INotifyPropertyChanged old = e.OldValue as INotifyPropertyChanged; if (old != null ) { old.PropertyChanged -= DatacontextPropertyChanged; } INotifyPropertyChanged newDataContext = e.NewValue as INotifyPropertyChanged; if (newDataContext != null ) { newDataContext.PropertyChanged += DatacontextPropertyChanged; } } } } {/ / / <summary> / / / Rédacteur pour CultureInfo / / / </ summary> / / / <remarks> ne supporte pas actuellement contraignante à partir de XAML à l'éditeur </ remarks> public class CultureInfoEditor:.. PropertyValueEditor {/ / / <summary> / / / Le ComboBox être utilisé pour modifier la valeur / / / </ summary> _owner ComboBox privé;.. / / / <summary> / / / Préserver le prototype du constructeur à partir PropertyValueEditor / / / </ summary> / / / <param template name="inlineEditorTemplate"> éditeur en ligne </ param> public CultureInfoEditor (DataTemplate inlineEditorTemplate):. base (inlineEditorTemplate) {} / / / <summary> / / / Par défaut constructeur construit un modèle éditeur de ComboBox en ligne. / / / </ summary> CultureInfoEditor publique () {/ / ne pas utiliser le databinding ici parce que Silverlight ne prend pas en charge / / les CultureConverter WPF qui est utilisé par Mélange FrameworkElementFactory comboBox = new FrameworkElementFactory (typeof (ComboBox));. comboBox.AddHandler ( ComboBox.LoadedEvent, nouvelle RoutedEventHandler ((expéditeur, e) => {_owner = (ComboBox) sender; _owner.SelectionChanged + = EditorSelectionChanged; données INotifyPropertyChanged = _owner.DataContext que INotifyPropertyChanged;! if (data = null) {+ data.PropertyChanged = DatacontextPropertyChanged;} _owner.DataContextChanged + = CultureDatacontextChanged;})); CultureInfo.GetCultures comboBox.SetValue (ComboBox.ItemsSourceProperty,; comboBox.SetValue (ComboBox.IsEditableProperty, false); comboBox.SetValue (ComboBox.DisplayMemberPathProperty, "DisplayName") (CultureTypes.SpecificCultures)); DataTemplate DataTemplate dt = new (); dt.VisualTree = comboBox; InlineEditorTemplate = dt;.} / / / <summary> / / / Gère l'événement SelectionChanged du contrôle propriétaire / / / </ summary > / / / <param name="sender"> La source de l'événement. </ param> / / / <param name="e"> Le <voir cref="System.Windows.Controls.SelectionChangedEventArgs"/> / / / instance contenant les données d'événement </ param> private void EditorSelectionChanged (object sender, SelectionChangedEventArgs e) {/ / avec le nom de sérialiser objet DataContext = _owner.DataContext;... DataContext GetType () GetProperty ("Valeur", BindingFlags.. .. Public | BindingFlags.Instance | BindingFlags.GetProperty) SetValue (DataContext, ((CultureInfo) _owner.SelectedItem) Nom, new object [] {});} / / / <summary> / / / Gère l'événement PropertyChanged de l' objet de contexte. / / / </ summary> / / / <param name="sender"> La source de l'événement. </ param> / / / <param name="e"> Le <voir cref = "Système. ComponentModel.PropertyChangedEventArgs "/> par exemple contenant les données d'événement. </ param> private void DatacontextPropertyChanged (object sender, PropertyChangedEventArgs e) {/ / désérialiser à partir du nom. if (e.PropertyName ==" Valeur ") {valeur de l'objet sender =. .. GetType () GetProperty ("Valeur", BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty) GetValue (expéditeur, new object [] {}); if (valeur = null!) {if (valeur chaîne) { CultureInfo setCulture = new CultureInfo (valeur ToString ().); _owner.SelectedItem = setCulture;.}}}} / / / <summary> / / / Appelé lorsque le contexte est modifié / / / </ summary> / / / < param name = "expéditeur"> L'expéditeur. </ param> / / / <param name="e"> L'instance cref="System.Windows.DependencyPropertyChangedEventArgs"/> <voir contenant les données d'événement. </ param> private nulle CultureDatacontextChanged (object sender, DependencyPropertyChangedEventArgs e) {INotifyPropertyChanged ancien = e.OldValue que INotifyPropertyChanged, si (ancien = null!) {old.PropertyChanged -= DatacontextPropertyChanged;} INotifyPropertyChanged newDataContext = e.NewValue que INotifyPropertyChanged;! if (newDataContext = null ) {newDataContext.PropertyChanged + = DatacontextPropertyChanged;}}}}
ExpandableObjectConverter
Tel que discuté au début, en plus personnalisée éditeurs valeur de la propriété, parfois, vous pouvez utiliser le convertisseur de type approprié pour fournir une expérience d'édition et de bonne sérialisation XAML. Un exemple est ColumnSeries.DependentRangeAxis: elle est de type IRangeAxis, Blend ne sais pas comment le modifier, il montre DependentRangeAxis en lecture seule dans le panneau Propriétés. En associant ExpandableObjectConverter d'ColumnSeries.DependentRangeAxis:
b.AddCustomAttributes ( Extensions.GetMemberName <ColumnSeries> (x => x.DependentRangeAxis), (ExpandableObjectConverter))); TypeConverterAttribute nouvelles (typeof (ExpandableObjectConverter))); b.AddCustomAttributes ( Extensions.GetMemberName <ColumnSeries> (x => x.IndependentAxis), (ExpandableObjectConverter))); TypeConverterAttribute nouvelles (typeof (ExpandableObjectConverter)));
Mélange affiche un bouton Nouveau à côté de cette propriété dans le panneau de propriétés; quand on clique dessus, il apparaît la boîte de dialogue Sélectionner un objet, filtrée avec IRangeAxis type de la propriété:
Conclusion
Blend et Silverlight ensemble permettent aux concepteurs de créer l'interface utilisateur étonnante contre les contrôles réel directement, il est donc très important que les développeurs de contrôles de prendre l'expérience créatrice dans le cadre de la conception du contrôle global et la mise en œuvre. Avoir un éditeur de propriétés personnalisé qui fournit l'interface utilisateur agréable et génère édition correcte XAML est un élément important de l'expérience créatrice. Espérons que ce post vous aide à comprendre comment créer des éditeurs de propriétés personnalisées. Merci!








Les commentaires récents