【问题标题】:Force a break in a items control with wrap panel template使用换行面板模板强制中断项目控件
【发布时间】:2013-04-18 23:00:10
【问题描述】:

我有一个有趣的问题一直在尝试解决。基本上我有一个使用WrapPanel 的项目控件,因为它是ItemsPanel 来模拟从几个绑定字符串构建的段落。但是,有时我需要强制中断,例如当我开始一个新段落时,但是在 TextBlock DateTemplate 中插入中断实际上并没有在父换行面板中插入中断。代码如下:

<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");

我希望它作为段落继续连接,但在遇到手动中断时尊重它们。像这样:

这是第一行,很长,会飘到第二行 自然是因为它是由一个环绕面板控制的。 我想强制到上面的线结束的下面的线。 迅速的 新的 行

【问题讨论】:

  • 你有什么理由不连接字符串然后将其绑定到一个简单的文本框?
  • 你有什么理由不连接字符串然后将其绑定到一个简单的文本框?
  • “Break”是指“NewLine”吗?
  • @sa_ddam213 是换行符。
  • 真的,在这里工作正常,你能张贴你期望它的样子的图片吗,我想我不明白你在做什么,你能不能只使用StackPanel 作为ItemsPanel 或仅使用TextBox 并填充Lines 属性而不是使用ItemsControl

标签: c# wpf xaml itemscontrol wrappanel


【解决方案1】:

我会放弃 ItemsControl 并改用文本块的 Inlines 集合。不幸的是,您不能直接绑定您的字符串集合,因为TextBlock.Inlines 不是依赖属性,但使用附加的依赖属性解决这个问题并不难:

我还添加了对传播 CollectionChanged 事件的支持,因此将字符串添加到 ViewModel.Fragments 将更新文本块。删除也可以,但限制是第一个匹配字符串的 Fragment 将被删除。

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

流支持:

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

【讨论】:

  • 听起来很有希望,我回家试试看。
  • 嗯,这个解决方案的唯一问题是项目的控制和换行面板我可以控制每个单词的细节,即粗体文本颜色......等等。无论如何用这种方法可以达到这种效果吗?
  • 没关系,我想出了一个解决方法,我可以将 Run 对象添加到内联并设置其中的属性。还有 1 个问题,OnFragmentsChanged 仅在加载时触发,而不是在集合实际更改时触发。
  • 实际上我看到了问题,如果我从列表中添加或减去内容,它会起作用,但实际上我只是更改列表中的内容,我需要它以某种方式触发更改事件,我我准备好了。
  • 好的,我明白了,感谢您的帮助,textblock inlines 属性是我正在做的更好的解决方案。享受 50 次代表。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-07
  • 2016-10-23
  • 1970-01-01
  • 1970-01-01
  • 2021-12-11
相关资源
最近更新 更多