文本框的绑定高度 属性
Binding Height property of a TextBox
我需要将 TextBox
es 的高度绑定到我的数据模型。每次 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
。但无论如何,我认识到如果我输入一些文本(例如换行符)来调整 TextBox
的 Height
的大小,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) 后面的代码保持完全清晰。
- 还有另一种方法可以将事件触发器重定向到名为 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;
}
如果我没有正确理解您的要求,请指正。
我需要将 TextBox
es 的高度绑定到我的数据模型。每次 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
。但无论如何,我认识到如果我输入一些文本(例如换行符)来调整 TextBox
的 Height
的大小,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) 后面的代码保持完全清晰。
- 还有另一种方法可以将事件触发器重定向到名为 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;
}
如果我没有正确理解您的要求,请指正。