简体   繁体   中英

WPF How to make RichTextBox width follow that of the parent control?

Here I have the following simple window:

在此输入图像描述

The upper part is a DataGrid, below is a TextBox. The window is set to size to content's width. This is the desired layout.

Now I need to display some richtext so I replace the TextBox with RichTextBox. The problem is that now the window stretches to the width of the screen, like so (I've shrunk it, of course, but you get the idea):

在此输入图像描述

I've tried binding the RichTextBox's width to the actual width of the parent:

 <RichTextBox Width="{Binding ElementName=Wrapper, Path=ActualWidth}"/>

but it still expands to the entire window. BTW the same happens if I use TextBox in the above code.

How can I make the RichTextBox's width fit to the parent, while still maintaining dynamic window layout? I mean the DataGrid is the key element to which both the windows's width and the RichTextBox's width must be subject to.

Below is the full code.

XAML

<Window x:Class="RichTextBox_Wrap.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RichTextBox_Wrap"
        mc:Ignorable="d"
        SizeToContent="Width"
        Title="MainWindow" Height="250" >
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="30"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <DataGrid Grid.Row="0" 
                  HeadersVisibility="Column" 
                  CanUserAddRows="False"
                  ItemsSource="{Binding Items}">
        </DataGrid>
        <StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock Text="Info" />
        </StackPanel>
        <DockPanel x:Name="Wrapper" Grid.Row="2">
            <Border BorderBrush="CadetBlue" BorderThickness="1">
                <RichTextBox />
            </Border>
        </DockPanel>
    </Grid>
</Window>

C#

public class Item
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public double Price { get; set; }
    }

    public class ViewModel
    {
        public ObservableCollection<Item> Items { get; set; }

        public ViewModel()
        {
            Items = new ObservableCollection<Item>
            {
                new Item {Name = "Apple", Description="Fruit", Price=3},
                new Item {Name = "Banana", Description="Fruit", Price=5},
                new Item {Name = "Tomato", Description="Vegetable", Price=4},
            };
        }
    }

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ViewModel();
        }
    }

SOLUTIONS

1. mm8's solution almost works but it seems that Loaded event happens to early to calculate the ActualWidth properly - the RichTextBox doesn't fill the width and leaves gaps. But taking the idea further I used the window's "Content_Rendered" event to fix the window's width and then set RichTextBox's width to auto.

<RichTextBox x:Name="RichBox" Grid.Row="1" MinHeight="75" Width="0" Visibility="Hidden" />

private void Window_ContentRendered(object sender, EventArgs e)
{
    var window = sender as Window;
    window.SizeToContent = SizeToContent.Manual;
    window.Width = window.ActualWidth;
    RichBox.Width = double.NaN;
    RichBox.Visibility = Visibility.Visible;
}

2. Spongebrot was spot on. Indeed, when the extra border is removed binding to parent's ActualWidth works. But in real life my control is more complex and the border is there for a reason. But this can be solved by cascading bindings:

<DockPanel x:Name="Wrapper" Grid.Row="2" >
    <Border x:Name="MyBorder" BorderBrush="CadetBlue" BorderThickness="1" Width="{Binding ElementName=Wrapper, Path=ActualWidth}" >
        <RichTextBox x:Name="RichBox" Width="{Binding ElementName=MyBorder, Path=Width}" />
    </Border>
</DockPanel>

It seems that binding to ActualWidth doesn't work as expected if you bind to a grandparent or more distant ancestor. This is seemingly why Sinatr's suggestion doesn't work either.

An easy workaround would be to set the Width of the RichTextBox to 0 in the XAML markup and then handle its Loaded event and set the Width to the window's width once it has been loaded:

<RichTextBox Width="0" Loaded="RichTextBox_Loaded" />

private void RichTextBox_Loaded(object sender, RoutedEventArgs e)
{
    RichTextBox rtb = sender as RichTextBox;
    rtb.Width = this.Width;
}

SizeToContent="Width"

So you want the window to autosize to DataGrid width?

RichTextBox seems special, it will request maximum available space from its parent container, occupying the whole combined desktops width at will. An easy fix is to limit width of it:

<Grid>
    <DataGrid x:Name="dataGrid" />
    <DockPanel>
        <Border>
            <RichTextBox Width="{Binding ActualWidth, ElementName=dataGrid}" />
        </Border>
    </DockPanel>
</Grid>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM