文本框的绑定高度 属性

Binding Height property of a TextBox

我需要将 TextBoxes 的高度绑定到我的数据模型。每次 TextBox 调整大小时都应更新模型。 TextBox 被调整大小,因为它包装内容直到到达 MaxHeight,当它到达时它显示滚动条。我做了一个小例子来说明我的问题。

<Window x:Class="BindingTester.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:BindingTester"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Canvas>
    <TextBox 
        Canvas.Left="234" 
        Canvas.Top="71"
        Text="TextBox"
        TextWrapping="Wrap"
        ScrollViewer.VerticalScrollBarVisibility="Auto" 
        MaxHeight="200"
        AcceptsReturn="True"
        Height="{Binding TextBoxHeight, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged/>
</Canvas>

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private double textBoxHeight;

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        //TextBoxHeight = 100.0;
    }

    public double TextBoxHeight
    {
        get { return textBoxHeight; }
        set
        {
            if (value != textBoxHeight)
            {
                textBoxHeight = value;
                RaisePropertyChanged("TextBoxHeight");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

当我使用它时,从源到目标的绑定工作正常。当我在 MainWindow 的构造函数中设置 TextBoxHeight 属性 时,TextBox 的大小完美调整为 100,但大小似乎已固定。当我不设置它时(因为高度为 100,它对于内容 "TextBox" 来说太大了)首先表现得像预期的那样:TextBox 的大小适合内容。模型中的 TextBoxHeight 更新为 NaN

我知道发生这种情况是因为 Height 属性 returns NaN 未设置时,我必须要求 ActualHeight。但无论如何,我认识到如果我输入一些文本(例如换行符)来调整 TextBoxHeight 的大小,TextBoxHeight 属性 仍然没有更新并且 setter 不再被调用......我也尝试过使用 IValueConverter 来使用 ActualHeight 更新它但没有成功。

我知道在这个例子中,即使我通过输入换行符调整 TextBox 的大小,TextBoxHeight 每次都会更新为 NaN,但是 setter 是刚在 TextBox 初始化时第一次调用。令我困惑的是 Binding 似乎不起作用......我知道问题本身的解决方案:订阅 SizeChanged 事件,获取 sender 对象的 DataContext 并手动设置模型。但我认为应该有一个解决方案,无需在代码隐藏中订阅和访问模型,仅绑定属性。有人可以帮忙吗?

我可以建议您使用行为或其他事件来命令机制,然后您可以侦听 OnResizeAction 事件来处理大小更改并将这些事件转换为您想要的任何逻辑。这是一个小例子。 1. Xaml代码:

<Window x:Class="SoResizeIssue.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:soResizeIssue="clr-namespace:SoResizeIssue"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <soResizeIssue:MainViewModel/>
</Window.DataContext>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
    </Grid.RowDefinitions>
    <TextBox HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
             Text="{Binding ContentFromDataContext, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
        <i:Interaction.Behaviors>
            <soResizeIssue:ResizeHandlingBehavior OnResizeAction="{Binding OnResizeAction, UpdateSourceTrigger=PropertyChanged}"/>
        </i:Interaction.Behaviors>
    </TextBox>
</Grid></Window>

2。行为代码:

public class ResizeHandlingBehavior:Behavior<FrameworkElement>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.SizeChanged += AssociatedObjectOnSizeChanged;
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
        AssociatedObject.SizeChanged -= AssociatedObjectOnSizeChanged;
    }

    private void AssociatedObjectOnSizeChanged(object sender, SizeChangedEventArgs args)
    {
        var action = OnResizeAction;
        if(action == null) return;
        action(args);
    }

    public static readonly DependencyProperty OnResizeActionProperty = DependencyProperty.Register(
        "OnResizeAction", typeof (Action<object>), typeof (ResizeHandlingBehavior), new PropertyMetadata(default(Action<object>)));

    public Action<object> OnResizeAction
    {
        get { return (Action<object>) GetValue(OnResizeActionProperty); }
        set { SetValue(OnResizeActionProperty, value); }
    }
}

3。视图模型代码:

public class MainViewModel:BaseObservableObject
{
    private string _contentFromDataContext;
    private Action<object> _onResizeAction;

    public MainViewModel()
    {
        OnResizeAction = new Action<object>(InnerOnResizeAction);
    }

    private void InnerOnResizeAction(object obj)
    {
        var args = obj as SizeChangedEventArgs;
        //do you logic here
    }

    public string ContentFromDataContext
    {
        get { return _contentFromDataContext; }
        set
        {
            _contentFromDataContext = value;
            OnPropertyChanged();
        }
    }

    public Action<object> OnResizeAction
    {
        get { return _onResizeAction; }
        set
        {
            _onResizeAction = value;
            OnPropertyChanged();
        }
    }
} 

更新 5、本方案将Resize的逻辑移到了ViewModel端,而TextBlock会因为Layout的重新构建和实际尺寸的变化而改变他的尺寸。使用此解决方案,您无需绑定到某些实际尺寸参数,您只需观察尺寸变化并将所有逻辑从 window 代码移至视图模型端,因此 window 的 ( xaml.cs) 后面的代码保持完全清晰。

  1. 还有另一种方法可以将事件触发器重定向到名为 Trigger to Command 模式的视图模型,这里是链接:Binding WPF Events to MVVM ViewModel Commands, How to trigger ViewModel command for a specific button events and Firing a Command within EventTrigger of a style?

希望对您有所帮助。 谢谢和问候,

多次阅读您的问题后,我得出以下结论:您需要在 TextBoxHeight 变量中设置文本框的当前高度。如果添加更多文本,您希望 TextBox 的高度发生变化,但只有当 TextBox 的高度超过 100 时,滚动条才应该可见。

以下代码将在添加更多文本时增加 TextBox 的高度。如果减少 text , TextBox 的高度也会减少到 MinHeight 值。

    <TextBox x:Name="Tbx1"
            Width="219"          
            TextWrapping="Wrap"
            ScrollViewer.VerticalScrollBarVisibility="Auto" 
            MaxHeight="100"
            AcceptsReturn="True"
            MinHeight="{Binding TextBoxHeight, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
            SizeChanged="Tbx1_SizeChanged"
     />

////////

private void Tbx1_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            TextBoxHeight = Tbx1.ActualHeight;
        }

如果我没有正确理解您的要求,请指正。