2013-04-18 6 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> 

ViewModel:

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 только активирует загрузку, а не когда коллекция фактически изменяется. –

Смежные вопросы