How to wrap TextBlock content in TreeView?

2019-02-26 06:14发布

I have TreeView, which displays some data using data templates. Here's XAML:

    <TreeView Grid.Row="0" ItemsSource="{Binding Railways}" x:Name="tvDatawareObjects"
              ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
            </Style>
        </TreeView.ItemContainerStyle>
        <TreeView.Resources>
<!-- other templates here... -->
            <HierarchicalDataTemplate DataType="{x:Type viewModels:ProjectViewModel}" ItemsSource="{Binding Phases}">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <TextBlock Text="{Binding Model.Code}" FontWeight="DemiBold" />
                    <TextBlock Text="{Binding Model.Title}" TextWrapping="Wrap" Foreground="Gray" Grid.Row="1" />
                </Grid>
            </HierarchicalDataTemplate>

            <HierarchicalDataTemplate DataType="{x:Type viewModels:CollectionViewModel}" ItemsSource="{Binding Items}">
                <TextBlock Text="{Binding CollectionName}" />
            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>

Text wrapping for <TextBlock Text="{Binding Model.Title}" TextWrapping="Wrap" Foreground="Gray" Grid.Row="1" /> doesn't work. What am I doing wrong?

3条回答
霸刀☆藐视天下
2楼-- · 2019-02-26 07:03

I believe the TextBlock isn't wrapping because it doesn't have a defined width. The grid column that the TextBlock is in has a * width which will grow as the TextBlock grows in width. Try setting a width on the TextBlock or the column and see if the change causes the TextBlock to wrap.

Update:

To be more specific, the problem is that the TreeViewItem will size itself to the size of its contents, the ColumnDefinition will fill the (infinitely) available space and the TextBlock, with no width restriction, will never wrap. This post does a good job of describing how the TreeViewItem behaves. To sum it up: the content area of the TreeViewItem is set to 'Auto' so it will grow to fit the contents. To explicitly set the width of the TreeViewItem try binding your ColumnDefinition width to the TreeView's ActualWidth.

XAML:

<TreeView Width="100">
    <TreeViewItem>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="{Binding RelativeSource={RelativeSource AncestorType=TreeView}, Path=ActualWidth}"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <TextBlock Text="Lorem Ipsum" />
            <TextBlock Text="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book." 
                       TextWrapping="Wrap" Grid.Row="1"/>
            </Grid>
        </TreeViewItem>
    </TreeView>
查看更多
聊天终结者
3楼-- · 2019-02-26 07:03

try this

<TextBlock Text="{Binding Model.Title}" Width="{Binding ActualWidth,
      ElementName=tvDatawareObjects}" TextWrapping="Wrap" Foreground="Gray" Grid.Row="1"/>
查看更多
可以哭但决不认输i
4楼-- · 2019-02-26 07:05

I did it. :)

Accordind to the link, provided in the Dan's answer, the reason is lying in the default TreeViewItemTemplate.

Unfortunately, simple binding to TreeView.ActualWidth can't help. This is because width of each item is less than TreeView.ActualWidth by definition - items are rendered with some horizontal offset, depending on their level.

Hence, to solve the problem, I need to calculate width of item like this:

width = actual_width_of_tree_view - relative_horizontal_offset_of_item

To be more precise, I need ScrollViewer.ViewportWidth, so as TreeViewContent might be scrolled vertically, and visible area of TreeView will be less than TreeView.ActualWidth in this case.

Here's attached property:

    public static Double GetProjectTitleWidth(DependencyObject obj)
    {
        return (Double)obj.GetValue(ProjectTitleWidthProperty);
    }

    public static void SetProjectTitleWidth(DependencyObject obj, Double value)
    {
        obj.SetValue(ProjectTitleWidthProperty, value);
    }

    public static readonly DependencyProperty ProjectTitleWidthProperty = DependencyProperty.RegisterAttached(
        "ProjectTitleWidth", 
        typeof(Double), 
        typeof(DatawareSearchView),
        new UIPropertyMetadata(0.0, ProjectTitleWidthChanged));

    private static void ProjectTitleWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var targetElement = d as FrameworkElement;
        if (targetElement != null)
        {
            var bindingExpr = targetElement.GetBindingExpression(ProjectTitleWidthProperty);
            var sourceElement = bindingExpr.DataItem as FrameworkElement;
            if (sourceElement != null)
            {
                // calculating relative offset
                var leftTop = targetElement.TranslatePoint(new Point(0.0, 0.0), sourceElement);

                // trying to find ScrollViewer
                var border = VisualTreeHelper.GetChild(sourceElement, 0);
                if (border != null)
                {
                    var scrollViewer = VisualTreeHelper.GetChild(border, 0) as ScrollViewer;
                    if (scrollViewer != null)
                    {
                        // setting width of target element
                        targetElement.Width = scrollViewer.ViewportWidth - leftTop.X;
                    }
                }
            }
        }
    }

...and markup:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <TextBlock Text="{Binding Model.Code}" FontWeight="DemiBold" />
    <TextBlock Text="{Binding Model.Title}" TextWrapping="Wrap" Foreground="Gray" x:Name="tbTitle" Grid.Row="1"
               localviews:DatawareSearchView.ProjectTitleWidth="{Binding RelativeSource={RelativeSource AncestorType=TreeView}, Path=ActualWidth}"/>
</Grid>

Of course, provided solution isn't universal - it assumes, that TreeView has Border and ScrollViewer.

查看更多
登录 后发表回答