【问题标题】:When getting the height of a textBlock it says the same number no matter the actual height获取 textBlock 的高度时,无论实际高度如何,它都会显示相同的数字
【发布时间】:2018-12-21 07:03:49
【问题描述】:

我正在制作一个 WPF 聊天程序,我想要显示消息的方式是这样的 (screenshot of how I have it now):

Sent Message
                      Received Message
Sent message2
                      Received Message

我在做什么:

一个滚动查看器,其中一个网格内部和网格内部 2 个堆栈面板,一个用于左侧,一个用于右侧。

<ScrollViewer x:Name="scrlViewer" VerticalScrollBarVisibility="Hidden" Width="472" Margin="10,10,0,67" 
                  HorizontalAlignment="Left" Grid.ColumnSpan="2">
        <Grid x:Name="ScrollViewerChild">
            <StackPanel x:Name="ChatRow1" Width="200" Margin="10,10,0,10" HorizontalAlignment="Left" />

            <StackPanel x:Name="ChatRow2" Width="200" Margin="0,10,10,10" HorizontalAlignment="Right"/>
        </Grid>

    </ScrollViewer>

然后在添加消息时我制作一个边框,然后制作一个 TextBlock。然后我将文本块添加到边框,然后将边框添加到相应的堆栈面板。

然后我尝试获取 TextBlock(或边框,两者都发生同样的事情)的高度,并制作 2 个不透明度为 0 的分隔符。将高度 10 添加到添加消息的面板中的高度之一,以及将 textBlockHeight + 10 添加到未添加任何内容的面板中的高度之一。

public void PrintOwnMessage(string msg)
{

    var border = new Border()
    {
        Margin = new Thickness(0),
        Padding = new Thickness(10,0,0,0),
        BorderThickness = new Thickness(1),
        BorderBrush = Brushes.White,
        Background = Brushes.Azure,
        CornerRadius = new CornerRadius(5)
    };
    var textBlock = new TextBlock()
    {
        Text = msg,
        FontSize = 20,
        TextWrapping = TextWrapping.Wrap


    };
    Console.WriteLine(border.DesiredSize);
    border.Child = textBlock;
    //panel[0] is the rightside panel where this function adds the message to
    panel[0].Children.Add(border);
    msgHeight = textBlock.ActualHeight;
    textBlock.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
    var desiredSizeNew = textBlock.DesiredSize;
    msgHeight = desiredSizeNew.Height + 10;
    panel[0].Children.Add(new Separator() { Opacity = 0, Height = 10});
    panel[1].Children.Add(new Separator() { Opacity = 0, Height = msgHeight });
    scrl.ScrollToEnd();
}

问题:

“desiredSizeNew”变量似乎每次都是一样的。请注意,我以这种方式获取大小是因为尝试获取 .Height 会给我一个 NaN 而 .ActualHeight 总是返回零。

即使消息换成多行,它也总是给我一行的大小(“26.6”),所以只要消息只有 1 行的高度,代码就可以工作。


任何帮助将不胜感激! 我希望我能够很好地解释我的问题。这是我第一次用 c# 做任何东西,所以任何关于实现我想要的更好方法的建议都会很棒。虽然我想知道如何先解决这个问题。

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    我会回答这一点“任何关于实现我想要的更好方法的建议都会很棒”

    我认为您应该尝试使用 DataTemplate。在后面的代码中创建元素通常会以失败告终(大量工作、未使用的部分、错误行为等)。

    我的示例基于 DataTemplate 和两个简单​​的类:

    public class Message
    {
        public string content { get; set; }
        public Message(string content)
        {
            this.content = content;
        }
    }
    
    public class MsgSent:Message
    {
        public MsgSent(string content) : base(content) { }
    }
    public class MsgRecieved : Message
    {
        public MsgRecieved(string content) : base(content) { }
    }
    

    MsgSentMsgRecieved 完全相似。但我们可以想象您聊天中的不同行为。最重要的是,我需要两个不同的 DataTemplate 类。

        <ListView ItemsSource="{Binding}" Name="viewChat" HorizontalContentAlignment="Stretch">
            <ListView.Resources>
                <DataTemplate DataType="{x:Type local:MsgRecieved}">
                    <Border  Background="LightGreen" Width="Auto" HorizontalAlignment="Left" CornerRadius="5" Margin="1">
                        <TextBlock Text="{Binding content}" Margin="5"/>
                    </Border>
                </DataTemplate>
                <DataTemplate DataType="{x:Type local:MsgSent}">
                    <Border  Background="LightBlue" Width="Auto" HorizontalAlignment="Right" CornerRadius="5" Margin="1">
                        <TextBlock Text="{Binding content}" Margin="5"/>
                    </Border>
                </DataTemplate>
            </ListView.Resources>
        </ListView>
    

    我根据之前的两个类写了两个DataTemplate。它们非常相似,但您可以随心所欲地定制它们。如果DataTemplate 上的公共代码变得庞大,您可以将其重新组合到Style 资源中。

    下面是填充聊天的代码:

    ObservableCollection<Message> letsChat = new ObservableCollection<Message>();
    letsChat.Add(new MsgRecieved("Hello world! \nHow are you?"));
    letsChat.Add(new MsgSent("Hi, good to see you!"));
    letsChat.Add(new MsgSent("Hey, are you still there?"));
    letsChat.Add(new MsgSent("Please let me know when you are back."));
    letsChat.Add(new MsgRecieved("Hello world! \nHow are you?"));
    
    viewChat.DataContext = letsChat;
    

    我使用了 ObservableCollection,因此您可以动态填充它。

    【讨论】:

    • 非常感谢!这很棒,而且比我做的简单得多:D
    【解决方案2】:

    要回答这个问题,DesiredSize 不是ActualHeight,在 WPF 完成测量和安排过程之前,它不会有最终值。

    但无论如何你都不想这样做,因为没有什么可以阻止这些值的变化。例如,如果屏幕被旋转,那么宽度可能会改变,这会改变包装,这会改变实际高度......这意味着现在设置为占位符高度的计算高度是错误的。

    您正在尝试对布局进行微观管理。让 WPF 为您做到这一点。布局是 WPF 的工作,它非常擅长。但是你必须正确地描述布局。完成此操作后,WPF 将在事情发生变化时进行调整。如果您发现自己尝试设置宽度或高度,您通常需要更加仔细地考虑布局。有时您必须这样做,但应该很少见

    基本上你已经并排定义了两个StackPanels。每次将消息添加到一侧时,您都会在下方添加一个分隔符,并且您试图通过放置相应的高分隔符来手动保持另一侧的步调。

    如果您将两侧的项目放在一个 StackPanel(而不是两个)中,那么它会为您整理垂直对齐方式。然后你可以左对齐左边的消息,右对齐右边的消息。作为额外的奖励,这意味着双方都有更多的宽度可以玩。但是如果你不喜欢这样,你可以限制TextBlocks的MaxWidth

    我还强烈建议您考虑使用 MVVM 和绑定,而不是在后面的代码中手动创建控件。同样,这将使您无需对不需要的事情进行微观管理。

    【讨论】:

    • 谢谢!我从未听说过我现在正在阅读的 MVVM。
    猜你喜欢
    • 2017-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-20
    • 2013-09-24
    • 2020-11-28
    相关资源
    最近更新 更多