J'ai un contrôle utilisateur personnalisé avec une zone de texte et j'aimerais exposer la ligne de base (du texte dans la zone de texte) à l'extérieur du contrôle personnalisé. Je sais que vous créez un concepteur (hérité de ControlDesigner) et que vous surchargez SnapLines pour accéder aux lignes d'accrochage, mais je me demande comment obtenir la ligne de base du texte d'un contrôle que j'ai exposé par mon contrôle utilisateur personnalisé.
Réponses
Trop de publicités?Comme une mise à jour de la réponse de Miral voici quelques-unes des "étapes manquantes", pour quelqu'un de nouveau qui cherche comment faire ceci :) Le code C# ci-dessus est presque prêt à l'emploi, à l'exception de quelques valeurs à changer pour référencer le UserControl qui sera modifié.
Références possibles nécessaires :
System.Design (@robyaw)
Usages nécessaires :
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
Sur votre UserControl, vous avez besoin de l'attribut suivant :
[Designer(typeof(MyCustomDesigner))]
Ensuite, vous avez besoin d'une classe "designer" qui aura la surcharge SnapLines :
private class MyCustomerDesigner : ControlDesigner {
public override IList SnapLines {
get {
/* Code from above */
IList snapLines = base.SnapLines;
// *** This will need to be modified to match your user control
MyControl control = Control as MyControl;
if (control == null) { return snapLines; }
// *** This will need to be modified to match the item in your user control
// This is the control in your UC that you want SnapLines for the entire UC
IDesigner designer = TypeDescriptor.CreateDesigner(
control.textBoxValue, typeof(IDesigner));
if (designer == null) { return snapLines; }
// *** This will need to be modified to match the item in your user control
designer.Initialize(control.textBoxValue);
using (designer)
{
ControlDesigner boxDesigner = designer as ControlDesigner;
if (boxDesigner == null) { return snapLines; }
foreach (SnapLine line in boxDesigner.SnapLines)
{
if (line.SnapLineType == SnapLineType.Baseline)
{
// *** This will need to be modified to match the item in your user control
snapLines.Add(new SnapLine(SnapLineType.Baseline,
line.Offset + control.textBoxValue.Top,
line.Filter, line.Priority));
break;
}
}
}
return snapLines;
}
}
}
}
Je viens d'avoir un besoin similaire, et je l'ai résolu de la manière suivante :
public override IList SnapLines
{
get
{
IList snapLines = base.SnapLines;
MyControl control = Control as MyControl;
if (control == null) { return snapLines; }
IDesigner designer = TypeDescriptor.CreateDesigner(
control.textBoxValue, typeof(IDesigner));
if (designer == null) { return snapLines; }
designer.Initialize(control.textBoxValue);
using (designer)
{
ControlDesigner boxDesigner = designer as ControlDesigner;
if (boxDesigner == null) { return snapLines; }
foreach (SnapLine line in boxDesigner.SnapLines)
{
if (line.SnapLineType == SnapLineType.Baseline)
{
snapLines.Add(new SnapLine(SnapLineType.Baseline,
line.Offset + control.textBoxValue.Top,
line.Filter, line.Priority));
break;
}
}
}
return snapLines;
}
}
De cette façon, il crée en fait un sous-concepteur temporaire pour la sous-commande afin de déterminer où se trouve la "vraie" ligne de base.
Cela a semblé raisonnablement performant lors des tests, mais si la performance devient un problème (et si la zone de texte interne ne bouge pas), la plupart de ce code peut être extrait vers la méthode Initialize.
Cela suppose également que la zone de texte est un enfant direct du UserControl. Si d'autres contrôles affectant la mise en page se trouvent sur le chemin, le calcul du décalage devient un peu plus compliqué.
Merci à tous ceux qui m'ont aidé. C'était difficile à avaler. L'idée d'avoir une sous-classe privée dans chaque UserControl n'était pas très agréable.
Je suis venu avec cette classe de base pour aider
[Designer(typeof(UserControlSnapLineDesigner))]
public class UserControlBase : UserControl
{
protected virtual Control SnapLineControl { get { return null; } }
private class UserControlSnapLineDesigner : ControlDesigner
{
public override IList SnapLines
{
get
{
IList snapLines = base.SnapLines;
Control targetControl = (this.Control as UserControlBase).SnapLineControl;
if (targetControl == null)
return snapLines;
using (ControlDesigner controlDesigner = TypeDescriptor.CreateDesigner(targetControl,
typeof(IDesigner)) as ControlDesigner)
{
if (controlDesigner == null)
return snapLines;
controlDesigner.Initialize(targetControl);
foreach (SnapLine line in controlDesigner.SnapLines)
{
if (line.SnapLineType == SnapLineType.Baseline)
{
snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset + targetControl.Top,
line.Filter, line.Priority));
break;
}
}
}
return snapLines;
}
}
}
}
Ensuite, faites dériver votre UserControl de cette base :
public partial class MyControl : UserControlBase
{
protected override Control SnapLineControl
{
get
{
return txtTextBox;
}
}
...
}
Merci encore d'avoir publié cet article.
VB.Net Version :
Remarque : vous devez modifier le txtDescription
à la Textbox ou à un autre nom de contrôle interne que vous utilisez. et ctlUserControl
à votre usercontrol
nom
<Designer(GetType(ctlUserControl.MyCustomDesigner))> _
Partial Public Class ctlUserControl
'...
'Your Usercontrol class specific code
'...
Class MyCustomDesigner
Inherits ControlDesigner
Public Overloads Overrides ReadOnly Property SnapLines() As IList
Get
' Code from above
Dim lines As IList = MyBase.SnapLines
' *** This will need to be modified to match your user control
Dim control__1 As ctlUserControl = TryCast(Me.Control, ctlUserControl)
If control__1 Is Nothing Then Return lines
' *** This will need to be modified to match the item in your user control
' This is the control in your UC that you want SnapLines for the entire UC
Dim designer As IDesigner = TypeDescriptor.CreateDesigner(control__1.txtDescription, GetType(IDesigner))
If designer Is Nothing Then
Return lines
End If
' *** This will need to be modified to match the item in your user control
designer.Initialize(control__1.txtDescription)
Using designer
Dim boxDesigner As ControlDesigner = TryCast(designer, ControlDesigner)
If boxDesigner Is Nothing Then
Return lines
End If
For Each line As SnapLine In boxDesigner.SnapLines
If line.SnapLineType = SnapLineType.Baseline Then
' *** This will need to be modified to match the item in your user control
lines.Add(New SnapLine(SnapLineType.Baseline, line.Offset + control__1.txtDescription.Top, line.Filter, line.Priority))
Exit For
End If
Next
End Using
Return lines
End Get
End Property
End Class
End Class
Vous êtes sur la bonne voie. Vous devrez remplacer la propriété SnapLines dans votre designr et faire quelque chose comme ceci :
Public Overrides ReadOnly Property SnapLines() As System.Collections.IList
Get
Dim snapLinesList As ArrayList = TryCast(MyBase.SnapLines, ArrayList)
Dim offset As Integer
Dim ctrl As MyControl = TryCast(Me.Control, MyControl)
If ctrl IsNot Nothing AndAlso ctrl.TextBox1 IsNot Nothing Then
offset = ctrl.TextBox1.Bottom - 5
End If
snapLinesList.Add(New SnapLine(SnapLineType.Baseline, offset, SnapLinePriority.Medium))
Return snapLinesList
End Get
End Property
Dans cet exemple, le usercontrol contient une zone de texte. Le code ajoute un nouveau snapline qui représente la ligne de base de la zone de texte. L'important est de calculer correctement le décalage.