我需要将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
属性在未设置时返回NaN
,而我必须请求ActualHeight
。但无论如何,我认识到,如果我输入一些文本(例如换行符)来调整TextBox
的Height
的大小,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)代码保持完全清晰。
- 还有另一种方法可以将事件触发器重定向到名为trigger to Command模式的视图模型,这里有链接:将WPF事件绑定到MVVM ViewModel命令,如何为特定按钮事件触发ViewModel命令以及在样式的EventTrigger内激发命令
我希望它能帮助你。感谢和问候,