2011-03-09 56 views
11

我使用的Windows Phone 7數據綁定的TextBlock運行在Silverlight/WP7

的Silverlight我想顯示在一個TextBlock大膽一些文本的第一部分,並以正常字體休息。完整的文字必須包裝。我希望粗體部分包含來自我的ViewModel中一個屬性的文本,以及包含來自不同屬性的文本的純文本。

TextBlock在與LongListSelector關聯的DataTemplate中定義。

我最初的嘗試是:

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

這種失敗在與壯觀無益 「AG_E_RUNTIME_MANAGED_UNKNOWN_ERROR」 運行。這是一個已知的問題,因爲Run元素不是FrameworkElement並且不能被綁定。

我的下一個嘗試是到位的佔位符,然後在代碼更新它們:(!是的,我絕望)

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

在後臺代碼:

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; 
} 

這似乎但因爲我使用LongListSelector,雖然項目被回收,但加載的代碼隱藏事件處理程序不會重新初始化運行,所以很快顯示錯誤的文本...

我看過使用LongListSelector的Linked事件(我已經用它來釋放列表中顯示的圖像),但是我看不到如何使用它重新初始化運行的文本屬性。

任何幫助表示讚賞!

+2

+1。好問題,我也想知道。 – Stan 2011-03-11 01:56:50

回答

5

我終於找到了適合我的解決方案。

正如我在評論中提到的,Paul Stovell的approach不起作用。

相反,我用類似的方法來附加屬性添加到TextBlock,勢必將TextBlock的DataContext的,並在運行的附加屬性,表明該視圖模型性質,他們應該被綁定到:

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

然後在我附加的TextBox Target(datacontext)屬性的已更改事件中,我更新了運行,並訂閱了TextBox Target屬性更改的通知。當TextBox Target屬性發生變化時,我相應地更新了任何關聯的Run文本。

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

我建議你試一試BindableRun。我只用它在WPF中,但我不明白爲什麼它不會在Silverlight中工作。

+0

會做 - 感謝 – Damian 2011-03-09 23:44:34

+0

不幸的是運行在Silverlight中是封閉的,所以這是行不通的。我正在尋找一種替代方法:http://paulstovell.wordpress.com/2007/03/21/attached-bindablerun/但這使用UIPropertyMetadata在Silverlight中不可用...當我將其更改爲PropertyMetadata當LongListSelector在ContentPresenter上調用Measure時,我得到一個運行時錯誤AG_E_PARSER_PROPERTY_NOT_FOUND。仍在嘗試! – Damian 2011-03-10 10:23:51