2011-03-09 13 views
11

estoy usando Silverlight para Windows Phone 7.Databinding TextBlock ejecuta en Silverlight/WP7

Quiero mostrar la primera parte de un texto en un TextBlock en negrita, y el resto en letra normal. El texto completo debe envolver. Quiero que la parte en negrita contenga texto de una propiedad en mi ViewModel y que el texto plano contenga texto de una propiedad diferente.

El TextBlock se define en un DataTemplate asociado a un LongListSelector.

Mi primer intento fue:

<TextBlock TextWrapping="Wrap"> 
    <TextBlock.Inlines> 
    <Run Text="{Binding Property1}" FontWeight="Bold"/> 
    <Run Text="{Binding Property2}"/> 
    </TextBlock.Inlines> 
</TextBlock> 

Esta falla en tiempo de ejecución con el "AG_E_RUNTIME_MANAGED_UNKNOWN_ERROR" espectacularmente inútil. Este es un problema conocido porque el elemento Ejecutar no es un FrameworkElement y no puede vincularse.

Mi siguiente intento fue poner marcadores de posición en su lugar, y luego actualizarlos en el código: (sí, estoy desesperado)

<TextBlock Loaded="TextBlockLoaded" TextWrapping="Wrap"> 
    <TextBlock.Inlines> 
     <Run FontWeight="Bold">Placeholder1</Run> 
     <Run>Placeholder2</Run> 
    </TextBlock.Inlines> 
</TextBlock> 

En el código subyacente:

private void TextBlockLoaded(object sender, RoutedEventArgs e) 
{ 
    var textBlock = (TextBlock)sender; 
    var viewModel = (ViewModel)textBlock.DataContext; 
    var prop1Run = (Run)textBlock.Inlines[0]; 
    var prop2Run = (Run)textBlock.Inlines[1]; 
    prop1Run.Text = viewModel.Property1; 
    prop2Run.Text = viewModel.Property2; 
} 

Esto parecía para trabajar, pero debido a que estoy utilizando el LongListSelector, aunque los elementos se reciclan, el controlador de eventos Loaded codebehind no reinicializa las ejecuciones, por lo que muy rápidamente se muestra el texto incorrecto ...

He analizado el uso del evento Linked de LongListSelector (que ya uso para liberar las imágenes que se muestran en la lista), pero no veo cómo puedo usar eso para reinicializar las propiedades de texto de Runs.

Cualquier ayuda apreciada!

+2

+1. Buena pregunta, me he preguntado sobre esto también. – Stan

Respuesta

5

Finalmente encontré una solución que me funciona.

Como menciono en el comentario, el approach de Paul Stovell no funcionaría.

En lugar de eso utiliza un enfoque similar para añadir una propiedad asociada a la TextBlock, con destino a DataContext del TextBlock y propiedades adjuntas en las pistas, lo que indica qué propiedades ViewModel que deberían estar obligados a:

<TextBlock TextWrapping="Wrap" 
      Views:BindableRuns.Target="{Binding}"> 
    <TextBlock.Inlines> 
     <Run FontWeight="Bold" Views:BindableRuns.Target="Property1"/> 
     <Run Views:BindableRuns.Target="Property2"/> 
    </TextBlock.Inlines> 
</TextBlock> 

Entonces en el evento modificado de la propiedad Attach TextBox Target (datacontext), actualizo Runs y me suscribo para recibir notificaciones de cambios en las propiedades de TextBox Target. Cuando una propiedad TextBox Target cambia, actualicé el texto de la ejecución asociada en consecuencia.

public static class BindableRuns 
{ 
    private static readonly Dictionary<INotifyPropertyChanged, PropertyChangedHandler> 
     Handlers = new Dictionary<INotifyPropertyChanged, PropertyChangedHandler>(); 

    private static void TargetPropertyPropertyChanged(
            DependencyObject dependencyObject, 
            DependencyPropertyChangedEventArgs e) 
    { 
     if(!(dependencyObject is TextBlock)) return; 

     var textBlock = (TextBlock)dependencyObject; 
     AddHandler(e.NewValue as INotifyPropertyChanged, textBlock); 
     RemoveHandler(e.OldValue as INotifyPropertyChanged); 
     InitializeRuns(textBlock, e.NewValue); 
    } 

    private static void AddHandler(INotifyPropertyChanged dataContext, 
            TextBlock textBlock) 
    { 
     if (dataContext == null) return; 

     var propertyChangedHandler = new PropertyChangedHandler(textBlock); 
     dataContext.PropertyChanged += propertyChangedHandler.PropertyChanged; 
     Handlers[dataContext] = propertyChangedHandler; 
    } 

    private static void RemoveHandler(INotifyPropertyChanged dataContext) 
    { 
     if (dataContext == null || !Handlers.ContainsKey(dataContext)) return; 

     dataContext.PropertyChanged -= Handlers[dataContext].PropertyChanged; 
     Handlers.Remove(dataContext); 
    } 

    private static void InitializeRuns(TextBlock textBlock, object dataContext) 
    { 
     if (dataContext == null) return; 

     var runs = from run in textBlock.Inlines.OfType<Run>() 
        let propertyName = (string)run.GetValue(TargetProperty) 
        where propertyName != null 
        select new { Run = run, PropertyName = propertyName }; 


     foreach (var run in runs) 
     { 
      var property = dataContext.GetType().GetProperty(run.PropertyName); 
      run.Run.Text = (string)property.GetValue(dataContext, null); 
     } 
    } 

    private class PropertyChangedHandler 
    { 
     private readonly TextBlock _textBlock; 
     public PropertyChangedHandler(TextBlock textBlock) 
     { 
      _textBlock = textBlock; 
     } 

     public void PropertyChanged(object sender, 
            PropertyChangedEventArgs propertyChangedArgs) 
     { 
      var propertyName = propertyChangedArgs.PropertyName; 
      var run = _textBlock.Inlines.OfType<Run>() 
       .Where(r => (string) r.GetValue(TargetProperty) == propertyName) 
       .SingleOrDefault(); 
      if(run == null) return; 

      var property = sender.GetType().GetProperty(propertyName); 
      run.Text = (string)property.GetValue(sender, null); 
     } 

    } 


    public static object GetTarget(DependencyObject obj) 
    { 
     return obj.GetValue(TargetProperty); 
    } 

    public static void SetTarget(DependencyObject obj, 
     object value) 
    { 
     obj.SetValue(TargetProperty, value); 
    } 

    public static readonly DependencyProperty TargetProperty = 
     DependencyProperty.RegisterAttached("Target", 
      typeof(object), 
      typeof(BindableRuns), 
      new PropertyMetadata(null, 
       TargetPropertyPropertyChanged)); 

} 
2

Te sugiero que pruebes el BindableRun. Solo lo he usado en WPF, pero no veo por qué no funcionaría en Silverlight.

+0

Hará -gracias – Damian

+0

Desafortunadamente Run está sellado en Silverlight, por lo que esto no funcionará. Estoy buscando un enfoque alternativo: http://paulstovell.wordpress.com/2007/03/21/attached-bindablerun/ pero esto hace uso de UIPropertyMetadata que no está disponible en Silverlight ... cuando lo cambio a PropertyMetadata Recibo un error de tiempo de ejecución AG_E_PARSER_PROPERTY_NOT_FOUND cuando LongListSelector llama a Measure en ContentPresenter. ¡Aún intentando! – Damian