C# 如何测量未选中的选项卡?

C# 如何测量未选中的选项卡?,c#,wpf,tabcontrol,virtualization,C#,Wpf,Tabcontrol,Virtualization,简明问题: 是否可以测量未选中选项卡的内容 问题摘要: 假设您有一个带有两个选项卡的选项卡控件。第一个选项卡包含一个带有文本块的网格。选择第二个选项卡。经过一段时间后,TextBlock的文本字段变为很长的字符串。您希望在第一个选项卡的内容可见之前测量其大小 如果只是进行测量,它将无法捕获字符串已更改的事实-您可以在WPF可视化工具中看到TextBlock的文本字段中有新字符串,但TextBlock拒绝重新测量。如果直接测量字符串,可以检测到新的所需大小,但这不是一个好的解决方案;我希望能够测量

简明问题:
是否可以测量未选中选项卡的内容

问题摘要:
假设您有一个带有两个选项卡的选项卡控件。第一个选项卡包含一个带有文本块的网格。选择第二个选项卡。经过一段时间后,TextBlock的文本字段变为很长的字符串。您希望在第一个选项卡的内容可见之前测量其大小

如果只是进行测量,它将无法捕获字符串已更改的事实-您可以在WPF可视化工具中看到TextBlock的文本字段中有新字符串,但TextBlock拒绝重新测量。如果直接测量字符串,可以检测到新的所需大小,但这不是一个好的解决方案;我希望能够测量第一个选项卡的总内容,而不仅仅是字符串

无论如何,对于冗长的示例代码表示歉意,这很难进一步减少。当窗口出现时,代码等待两秒钟,然后交换选项卡。然后更改字符串。当检测到字符串大小更改时,度量循环将背景更改为蓝色,当检测到实际内容大小更改时,将背景更改为红色。红色仅在切换选项卡时出现,但我希望能够在不必切换回第一个选项卡的情况下使红色出现

代码隐藏:

    public MainWindow()
    {
        InitializeComponent();

        // this worker waits a bit so the first tab renders, 
        // then it switches tabs and changes the string.
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += delegate
        {
            Thread.Sleep(2000);
            Application.Current.Dispatcher.BeginInvoke(new Action(delegate
            {
                TestTabControl.SelectedIndex = 1;
                TestBlock.Text = "This is a long string that ought to change the measure of the textblock to sizes never before seen by human eyes";
                this.Background = Brushes.Green;
            }));
        };
        worker.RunWorkerAsync();

        // This worker constantly measures the text block 
        worker = new BackgroundWorker();
        worker.DoWork += delegate
        {
            while (true)
            {
                if (Application.Current != null)
                {
                    Application.Current.Dispatcher.BeginInvoke(new Action(delegate
                    {
                        MeasureFirstTab();
                    }));
                }
                else
                {
                    break;
                }
                Thread.Sleep(100);
            }
        };
        worker.RunWorkerAsync();
    }

    // Turn the background red when the tab width changes
    public void MeasureFirstTab()
    {
        FirstTabContent.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        if (MeasureString(TestBlock.Text).Width > 500)
        {
            this.Background = Brushes.Blue;
        }

        if (FirstTabContent.DesiredSize.Width > 500)
        {
            this.Background = Brushes.Red;
        }  
    }

    private Size MeasureString(string candidate)
    {
        var formattedText = new FormattedText(
            candidate,
            CultureInfo.CurrentUICulture,
            FlowDirection.LeftToRight,
            new Typeface(TestBlock.FontFamily, TestBlock.FontStyle, TestBlock.FontWeight, TestBlock.FontStretch),
            TestBlock.FontSize,
            Brushes.Black);

        return new Size(formattedText.Width, formattedText.Height);
    }
}
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <TabControl Name="TestTabControl">

        <TabItem Header="Changes">
            <Grid Name="FirstTabContent">
                <TextBlock Name="TestBlock" Text="small"/>
            </Grid>
        </TabItem>
        <TabItem Header="Short">                
            <TextBlock Text="Short"/>                
        </TabItem>
    </TabControl>        
</Grid>
XAML:

    public MainWindow()
    {
        InitializeComponent();

        // this worker waits a bit so the first tab renders, 
        // then it switches tabs and changes the string.
        BackgroundWorker worker = new BackgroundWorker();
        worker.DoWork += delegate
        {
            Thread.Sleep(2000);
            Application.Current.Dispatcher.BeginInvoke(new Action(delegate
            {
                TestTabControl.SelectedIndex = 1;
                TestBlock.Text = "This is a long string that ought to change the measure of the textblock to sizes never before seen by human eyes";
                this.Background = Brushes.Green;
            }));
        };
        worker.RunWorkerAsync();

        // This worker constantly measures the text block 
        worker = new BackgroundWorker();
        worker.DoWork += delegate
        {
            while (true)
            {
                if (Application.Current != null)
                {
                    Application.Current.Dispatcher.BeginInvoke(new Action(delegate
                    {
                        MeasureFirstTab();
                    }));
                }
                else
                {
                    break;
                }
                Thread.Sleep(100);
            }
        };
        worker.RunWorkerAsync();
    }

    // Turn the background red when the tab width changes
    public void MeasureFirstTab()
    {
        FirstTabContent.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
        if (MeasureString(TestBlock.Text).Width > 500)
        {
            this.Background = Brushes.Blue;
        }

        if (FirstTabContent.DesiredSize.Width > 500)
        {
            this.Background = Brushes.Red;
        }  
    }

    private Size MeasureString(string candidate)
    {
        var formattedText = new FormattedText(
            candidate,
            CultureInfo.CurrentUICulture,
            FlowDirection.LeftToRight,
            new Typeface(TestBlock.FontFamily, TestBlock.FontStyle, TestBlock.FontWeight, TestBlock.FontStretch),
            TestBlock.FontSize,
            Brushes.Black);

        return new Size(formattedText.Width, formattedText.Height);
    }
}
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <TabControl Name="TestTabControl">

        <TabItem Header="Changes">
            <Grid Name="FirstTabContent">
                <TextBlock Name="TestBlock" Text="small"/>
            </Grid>
        </TabItem>
        <TabItem Header="Short">                
            <TextBlock Text="Short"/>                
        </TabItem>
    </TabControl>        
</Grid>

为子孙后代和将来使用谷歌搜索的人解答

正如在my OP的评论中所指出的,TabControl虚拟化了未选择的选项卡,这显然会导致度量过程只计算先前所需的大小

因此,解决方案是从选项卡项中删除内容,将其添加到网格中,测量内容,然后将其添加回选项卡

以下是新的测量代码:

    // Turn the background red when the tab width changes
    public void MeasureFirstTab()
    {
        // Remember the previous selected item
        object selectedItem = TestTabControl.SelectedItem;
        Grid measureBox = new Grid();
        UIElement content;

        // Iterate through all items
        foreach (TabItem obj in TestTabControl.Items)
        {
            // Get the tab content into the grid
            TestTabControl.SelectedItem = obj;
            content = (UIElement)obj.Content;
            obj.Content = null;
            measureBox.Children.Add(content);

            // Measure the content
            content.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

            if (content.DesiredSize.Width > 500)
            {
                this.Background = Brushes.Red;
            }

            // Return the content to its rightful owner
            measureBox.Children.Clear();
            obj.Content = content;
        }

        // Reset the tab control
        TestTabControl.SelectedItem = selectedItem;

    }

这听起来是一个非常奇怪的要求。WPF
TabControl
为所选TabItem动态创建可视化树。你到底想用这个实现什么?我有一个定制的选项卡控件/扩展程序组合和一个本地化要求。如果控件折叠时语言发生变化,我希望能够测量新选项卡的大小,并将其扩展到最大选项卡的适当宽度,而不管选择了哪个选项卡。