tooltip wpf
Datos vinculantes del TextBlock.Inlines (7)
Podría agregar una Propiedad de dependencia a una Subclase de TextBlock
public class BindableTextBlock : TextBlock
{
public ObservableCollection<Inline> InlineList
{
get { return (ObservableCollection<Inline>)GetValue(InlineListProperty); }
set { SetValue(InlineListProperty, value); }
}
public static readonly DependencyProperty InlineListProperty =
DependencyProperty.Register("InlineList",typeof(ObservableCollection<Inline>), typeof(BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
BindableTextBlock textBlock = sender as BindableTextBlock;
ObservableCollection<Inline> list = e.NewValue as ObservableCollection<Inline>;
list.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(textBlock.InlineCollectionChanged);
}
private void InlineCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
int idx = e.NewItems.Count -1;
Inline inline = e.NewItems[idx] as Inline;
this.Inlines.Add(inline);
}
}
}
Mi aplicación WPF recibe una secuencia de mensajes de un servicio de back-end que necesito mostrar en la UI. Estos mensajes varían mucho y deseo tener un diseño visual diferente (formatos de cadena, colores, fuentes, íconos, lo que sea, etc.) para cada mensaje.
Esperaba solo poder crear un en línea (Ejecutar, TextBlock, Cursiva, etc.) para cada mensaje y, de alguna manera, ponerlos en un ObservableCollection<>
y usar la magia de WPF Data Binding en mi TextBlock.Inlines en la UI. No pude encontrar cómo hacer esto, ¿es esto posible?
En la versión 4 de WPF podrá enlazar a un objeto Run, que puede resolver su problema.
He resuelto este problema en el pasado anulando un ItemsControl y mostrando el texto como elementos en ItemsControl. Mira algunos de los tutoriales que el Dr. WPF ha hecho sobre este tipo de cosas: http://www.drwpf.com
Esto no es posible porque la propiedad TextBlock.Inlines
no es una propiedad de dependencia. Solo las propiedades de dependencia pueden ser el objetivo de un enlace de datos.
Dependiendo de los requisitos exactos de su diseño, puede hacer esto usando un ItemsControl
, con su ItemsPanel
establecido en WrapPanel
y su ItemsSource
configurado en su colección. (Es posible que se requiera algo de experimentación aquí porque un Inline
no es un UIElement
, por lo que su renderización predeterminada probablemente se hará usando ToString()
lugar de mostrarse).
Alternativamente, puede necesitar construir un nuevo control, por ejemplo, MultipartTextBlock
, con una propiedad PartsSource
y un TextBlock
como su plantilla predeterminada. Cuando se configuró PartsSource
, su control adjuntaría un manejador de eventos CollectionChanged
(directamente o mediante CollectionChangedEventManager) y actualizaría la colección TextBlock.Inlines
del código a medida que PartsSource
colección de PartsSource
.
En cualquier caso, puede ser necesario tener precaución si su código está generando elementos en Inline
directamente (porque un Inline
no se puede usar en dos lugares al mismo tiempo). Alternativamente, puede querer exponer un modelo abstracto de texto, fuente, etc. (es decir, un modelo de vista) y crear los objetos en Inline
reales a través de una DataTemplate
. Esto también puede mejorar la capacidad de prueba, pero obviamente agrega complejidad y esfuerzo.
Gracias Frank por tu solución. Tuve que hacer un par de cambios menores para que funcione para mí.
public class BindableTextBlock : TextBlock
{
public ObservableCollection<Inline> InlineList
{
get { return (ObservableCollection<Inline>) GetValue(InlineListProperty); }
set { SetValue(InlineListProperty, value); }
}
public static readonly DependencyProperty InlineListProperty =
DependencyProperty.Register("InlineList", typeof (ObservableCollection<Inline>), typeof (BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
BindableTextBlock textBlock = (BindableTextBlock) sender;
textBlock.Inlines.Clear();
textBlock.Inlines.AddRange((ObservableCollection<Inline>) e.NewValue);
}
}
Me doy cuenta de que esta pregunta es muy antigua, pero de todos modos pensé en compartir una solución alternativa. Utiliza comportamientos de WPF / propiedades adjuntas:
public static class TextBlockExtensions
{
public static IEnumerable<Inline> GetBindableInlines ( DependencyObject obj )
{
return (IEnumerable<Inline>) obj.GetValue ( BindableInlinesProperty );
}
public static void SetBindableInlines ( DependencyObject obj, IEnumerable<Inline> value )
{
obj.SetValue ( BindableInlinesProperty, value );
}
public static readonly DependencyProperty BindableInlinesProperty =
DependencyProperty.RegisterAttached ( "BindableInlines", typeof ( IEnumerable<Inline> ), typeof ( TextBlockExtensions ), new PropertyMetadata ( null, OnBindableInlinesChanged ) );
private static void OnBindableInlinesChanged ( DependencyObject d, DependencyPropertyChangedEventArgs e )
{
var Target = d as TextBlock;
if ( Target != null )
{
Target.Inlines.Clear ();
Target.Inlines.AddRange ( (System.Collections.IEnumerable) e.NewValue );
}
}
}
En tu XAML, utilízalo así:
<TextBlock MyBehaviors:TextBlockExtensions.BindableInlines="{Binding Foo}" />
Esto le ahorra tener que heredar de TextBlock. También podría funcionar usando un ObservableCollection en lugar de IEnumerable , en ese caso necesitaría suscribirse a los cambios de colección.
Si recibo su requerimiento correctamente, puede verificar manualmente los próximos mensajes y para cada mensaje puede agregar un elemento a la propiedad TextBlock.Inlines. No tomará ningún enlace de datos. He hecho esto con lo siguiente:
public string MyBindingPath
{
get { return (string)GetValue(MyBindingPathProperty); }
set { SetValue(MyBindingPathProperty, value); }
}
// Using a DependencyProperty as the backing store for MyBindingPath. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyBindingPathProperty =
DependencyProperty.Register("MyBindingPath", typeof(string), typeof(Window2), new UIPropertyMetadata(null, OnPropertyChanged));
private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
(sender as Window2).textBlock.Inlines.Add(new Run(e.NewValue.ToString()));
}
Imports System.Collections.ObjectModel
Imports System.Collections.Specialized
Public Class BindableTextBlock
Inherits TextBlock
Public Property InlineList As ObservableCollection(Of Inline)
Get
Return GetValue(InlineListProperty)
End Get
Set(ByVal value As ObservableCollection(Of Inline))
SetValue(InlineListProperty, value)
End Set
End Property
Public Shared ReadOnly InlineListProperty As DependencyProperty = _
DependencyProperty.Register("InlineList", _
GetType(ObservableCollection(Of Inline)), GetType(BindableTextBlock), _
New UIPropertyMetadata(Nothing, AddressOf OnInlineListPropertyChanged))
Private Shared Sub OnInlineListPropertyChanged(sender As DependencyObject, e As DependencyPropertyChangedEventArgs)
Dim textBlock As BindableTextBlock = TryCast(sender, BindableTextBlock)
Dim list As ObservableCollection(Of Inline) = TryCast(e.NewValue, ObservableCollection(Of Inline))
If textBlock IsNot Nothing Then
If list IsNot Nothing Then
'' Add in the event handler for collection changed
AddHandler list.CollectionChanged, AddressOf textBlock.InlineCollectionChanged
textBlock.Inlines.Clear()
textBlock.Inlines.AddRange(list)
Else
textBlock.Inlines.Clear()
End If
End If
End Sub
'''''' <summary>
'''''' Adds the items to the inlines
'''''' </summary>
'''''' <param name="sender"></param>
'''''' <param name="e"></param>
'''''' <remarks></remarks>
Private Sub InlineCollectionChanged(sender As Object, e As NotifyCollectionChangedEventArgs)
Select Case e.Action
Case NotifyCollectionChangedAction.Add
Me.Inlines.AddRange(e.NewItems)
Case NotifyCollectionChangedAction.Reset
Me.Inlines.Clear()
Case NotifyCollectionChangedAction.Remove
For Each Line As Inline In e.OldItems
If Me.Inlines.Contains(Line) Then
Me.Inlines.Remove(Line)
End If
Next
End Select
End Sub
End Class
Creo que es posible que necesite algún código adicional en el controlador PropertyChanged, para inicializar textBlock.Inlines si la colección enlazada ya tiene contenido y para borrar cualquier contexto existente.