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属性在未设置时返回NaN,而我必须请求ActualHeight。但无论如何,我认识到,如果我输入一些文本(例如换行符)来调整TextBoxHeight的大小,TextBoxHeight属性仍然不会更新,也不会再次调用setter…我还尝试过使用IValueConverter来使用ActualHeight更新它,但没有成功。

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

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

以下代码将在添加更多文本时增加TextBox的高度。如果减少文本,则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;
        }

如果我没有正确理解你的要求,请纠正我。

我可以建议您使用行为或其他事件到命令机制,然后您可以监听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.ViewModel代码:

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重新构建和实际大小的变化而更改其大小。使用这个解决方案,你不需要绑定到一些实际的大小参数——你只需要观察大小的变化,并将所有逻辑从窗口代码转移到视图模型端,因此窗口的(xaml.cs)代码保持完全清晰。

  1. 还有另一种方法可以将事件触发器重定向到名为trigger to Command模式的视图模型,这里有链接:将WPF事件绑定到MVVM ViewModel命令,如何为特定按钮事件触发ViewModel命令以及在样式的EventTrigger内激发命令

我希望它能帮助你。感谢和问候,

最新更新