Je veux dessiner des numéros de ligne dans la gauche d'un ListBox, très similaire à ce que AvalonEdit fait avec LineNumberMargin. Lorsque ShowLineNumbers
est vrai, il crée LineNumberMargin
comme this.Dessinez le numéro de ligne des éléments ListBox dans un contrôle séparé
De toute façon, j'ai jeté un oeil à la façon dont ils le font, et compris, et maintenant j'essaie d'appliquer quelque chose de similaire, mais en utilisant un ListBox (et ses éléments) comme source pour le dessin.
Mon contrôle fonctionne comme ceci: J'ai un ItemsControl séparé ancré à gauche de ListBox. Chaque élément ItemsControl est un UIElement. DesignerLineNumberMargin
est l'un des éléments ItemsControl et lorsque la ItemsSource de la ListBox est définie, j'attache le ListBox au DesignerLineNumberMargin
. Lorsque DesignerLineNumberMargin
est affiché, je parcourt les éléments de ListBox et dessine les numéros de ligne.
DesignerLineNumberMargin.cs
public interface IMetadataAware
{
void Attach(ItemsControl control);
void Detach(ItemsControl control);
}
public class DesignerLineNumberMargin : FrameworkElement, IMetadataAware
{
private ItemsControl control;
private Typeface typeface;
private double emSize;
private int maxLineNumberLength = 2;
static DesignerLineNumberMargin()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DesignerLineNumberMargin),
new FrameworkPropertyMetadata(typeof(DesignerLineNumberMargin)));
}
protected override Size MeasureOverride(Size availableSize)
{
typeface = CreateTypeface();
emSize = (double)GetValue(TextBlock.FontSizeProperty);
var text = CreateText(new string('9', maxLineNumberLength));
return new Size(text.Width, 0);
}
private FormattedText CreateText(string text)
{
return
new FormattedText(
text,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
emSize,
(Brush)GetValue(Control.ForegroundProperty));
}
protected override void OnRender(DrawingContext drawingContext)
{
if (control == null)
return;
var renderSize = RenderSize;
var foreground = (Brush)GetValue(Control.ForegroundProperty);
for (int index = 0; index < control.Items.Count; index++)
{
var item = control.Items[index];
var container = (FrameworkElement)control.ItemContainerGenerator.ContainerFromItem(item);
var text = CreateText((index + 1).ToString(CultureInfo.CurrentCulture));
//var y = container.Height;
var y = RenderSize.Height/(double)control.Items.Count;
drawingContext.DrawText(text, new Point(renderSize.Width - text.Width, y + index));
}
}
private Typeface CreateTypeface()
{
var element = this;
return new Typeface(
(FontFamily)element.GetValue(TextBlock.FontFamilyProperty),
(FontStyle)element.GetValue(TextBlock.FontStyleProperty),
(FontWeight)element.GetValue(TextBlock.FontWeightProperty),
(FontStretch)element.GetValue(TextBlock.FontStretchProperty));
}
public void Attach(ItemsControl control)
{
this.control = control;
var descriptor = TypeDescriptor.GetProperties(control)["ItemsSource"];
descriptor.AddValueChanged(control, OnItemsSourceChanged);
}
private void OnItemsSourceChanged(object sender, EventArgs e)
{
if (this.control.ItemsSource is INotifyCollectionChanged)
(this.control.ItemsSource as INotifyCollectionChanged).CollectionChanged += CollectionChanged;
}
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
InvalidateVisual();
}
public void Detach(ItemsControl control)
{
if (this.control == control)
{
var descriptor = TypeDescriptor.GetProperties(control)["ItemsSource"];
descriptor.RemoveValueChanged(control, OnItemsSourceChanged);
if (this.control.ItemsSource is INotifyCollectionChanged)
(this.control.ItemsSource as INotifyCollectionChanged).CollectionChanged -= CollectionChanged;
this.control = null;
}
InvalidateVisual();
}
}
Le problème pour moi est de trouver la coordonnée y. Lorsque OnRender
est appelé, je ne connais pas le Height
du ListBoxItem
: Hauteur, ActualHeight, DesiredSize est toujours 0.
Toute autre idée?