2013-04-18 64 views
1

我有一個有趣的問題,我一直試圖解決。基本上我有一個項目控件,它使用WrapPanel,因爲它的ItemsPanel可以模擬從幾個綁定字符串構建的段落。不過,有時候我需要強制休息一下,比如當我開始一個新的段落時,但是暫停進入TextBlockDateTemplate實際上並沒有在父級換行面板中放置休息。下面是代碼:使用包裝面板模板在物品控制中強制中斷

<ItemsControl ItemsSource="{Binding Fragments}" > 
    <ItemsControl.ItemsPanel> 
     <ItemsPanelTemplate> 
      <WrapPanel /> 
     </ItemsPanelTemplate> 
    </ItemsControl.ItemsPanel> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <TextBlock 
       TextWrapping="Wrap" 
       Text="{Binding}"/> <!--If this text has a break it won't 
             propagate that break to the wrap panel, 
             but instead just in this text block which 
             causes the formatting to look wrong--> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

這裏是片段一個簡單的定義,將顯示什麼我談論:

Fragments = new ObservableCollection<string>(); 
Fragments.Add("This is"); 
Fragments.Add("the first line, "); 
Fragments.Add("it is very long and will drift to the "); 
Fragments.Add("second line naturally since it is controlled by a wrap panel"); 
Fragments.Add("\n\r This I want to force to the line below where the line above ends"); 
Fragments.Add("rapid \n\r new \n\r lines"); 

enter image description here

我想這個流動的段落,只是繼續進行連接,但在遇到碰撞時遵守手動斷點。像這樣:

 
This is the first line, it is very long and will drift to the second line 
naturally since it is controlled by a wrap panel. 
This I want to force to the line below where the line above ends. 
rapid 
new 
lines
+0

是否有任何理由你是不是串聯的字符串,然後將其綁定到一個簡單的文本框? – Kenneth

+0

是否有任何理由不串聯字符串,然後將其綁定到一個簡單的文本框? – Kenneth

+0

作者:「Break」你是不是指「NewLine」? –

回答

2

我會查找ItemsControl並使用文本塊的Inlines集合來代替。不幸的是,你不能直接綁定你的字符串集合,因爲TextBlock.Inlines不是一個依賴項屬性,但要解決這個問題並不難,只需附加依賴項屬性:

我還添加了對CollectionChanged事件傳播的支持,一個到ViewModel.Fragments的字符串將更新文本塊。刪除也會起作用,但限制了與字符串匹配的第一個片段將被刪除。

enter image description here

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:WpfApplication1" 
     Title="MainWindow" Height="350" Width="525"> 

    <Window.DataContext> 
     <local:ViewModel /> 
    </Window.DataContext> 

    <Grid> 
     <TextBlock local:FlowSupport.Fragments="{Binding Fragments}" TextWrapping="WrapWithOverflow" Margin="10" Background="Beige" /> 
    </Grid> 
</Window> 

視圖模型:

public class ViewModel : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 
    private void OnPropertyChanged(string propertyName) 
    { 
     if (this.PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    } 

    private ObservableCollection<string> _fragments; 
    public ObservableCollection<string> Fragments { get { return _fragments; } set { _fragments = value; OnPropertyChanged("Fragments"); } } 

    public ViewModel() 
    { 
     Fragments = new ObservableCollection<string>(); 
     Fragments.Add("This is "); 
     Fragments.Add("the first line, "); 
     Fragments.Add("it is very long and will drift to the "); 
     Fragments.Add("second line naturally since it is controlled by a wrap panel"); 
     Fragments.Add("\nThis I want to force to the line below where the line above ends\n"); 
     Fragments.Add("rapid \nnew \nlines"); 
    } 
} 

FlowSupport:

using System; 
using System.Linq; 
using System.Collections.Generic; 
using System.Collections.ObjectModel; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Documents; 
using System.Collections.Specialized; 

namespace WpfApplication1 
{ 
    public static class FlowSupport 
    { 
     private static Dictionary<TextBlock, NotifyCollectionChangedEventHandler> _collChangedHandlers = new Dictionary<TextBlock,NotifyCollectionChangedEventHandler>(); 

     public static ObservableCollection<string> GetFragments(TextBlock tb) { return (ObservableCollection<string>)tb.GetValue(FragmentsProperty); } 
     public static void SetFragments(TextBlock tb, ObservableCollection<string> value) { tb.SetValue(FragmentsProperty, value); } 

     public static readonly DependencyProperty FragmentsProperty = DependencyProperty.RegisterAttached("Fragments", typeof(ObservableCollection<string>), typeof(FlowSupport), new PropertyMetadata(new ObservableCollection<string>(), OnFragmentsChanged)); 

     private static void OnFragmentsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
     { 
      var tb = d as TextBlock; 
      if (tb != null) 
      { 
       CreateCollectionChangedHandler(tb); // create handler, once per textblock 

       tb.Inlines.Clear(); 
       var oldInlines = e.OldValue as ObservableCollection<string>; 
       if (oldInlines != null) 
       { 
        oldInlines.CollectionChanged -= _collChangedHandlers[tb]; 
       } 
       var inlines = e.NewValue as ObservableCollection<string>; 
       if (inlines != null) 
       { 
        inlines.CollectionChanged += _collChangedHandlers[tb]; 

        foreach (string s in inlines) 
         tb.Inlines.Add(s); 
       } 
      } 
     } 

     private static void CreateCollectionChangedHandler(TextBlock tb) 
     { 
      if (!_collChangedHandlers.ContainsKey(tb)) 
      { 
       _collChangedHandlers.Add(tb, (s1, e1) => 
       { 
        if (e1.NewItems != null) 
        { 
         foreach (string text in e1.NewItems) 
          tb.Inlines.Add(text); 
        } 
        if (e1.OldItems != null) 
        { 
         foreach (string text in e1.OldItems) 
         { 
          Inline inline = tb.Inlines.FirstOrDefault(i => ((Run)i).Text == text); 
          if (inline != null) 
           tb.Inlines.Remove(inline); 
         } 
        } 
       }); 
      } 
     } 
    } 
} 
+0

聽起來很有希望,當我回家時我會嘗試一下。 –

+0

嗯,這個解決方案的唯一問題是項目的控制和包裝面板,我可以控制每個單詞的細節,即大膽文本顏色等。無論如何用這種方法來實現這種效果呢? –

+0

沒關係,我想出了一個解決方法,我可以將'Run'對象添加到內聯並設置其中的屬性。還有一個問題,OnFragmentsChanged只會觸發加載,而不是集合實際發生更改時觸發。 –