在WPF中第一个控件的第二个控件上执行方法
我的在WPF中第一个控件的第二个控件上执行方法,wpf,mvvm,user-controls,relaycommand,routed-commands,Wpf,Mvvm,User Controls,Relaycommand,Routed Commands,我的ViewControl有一个名为ZoomIn()的方法。如何通过单击按钮控件在视图控件上执行此方法,而不必执行代码隐藏 <controls:ViewControl/> <Button Content="Zoom In"/> 尽管我有一个ViewModel,并试图在我的设计中使用MVVM。我不确定在这种情况下,ZoomIn()做的事情与视图相关,这是怎么可能的 我能想到的一个类似的情况是,当我有一个按钮和一个TextBox,我想在单击按钮时调用TextBox上的Sel
ViewControl
有一个名为ZoomIn()
的方法。如何通过单击按钮
控件在视图控件
上执行此方法,而不必执行代码隐藏
<controls:ViewControl/>
<Button Content="Zoom In"/>
尽管我有一个ViewModel,并试图在我的设计中使用MVVM。我不确定在这种情况下,ZoomIn()做的事情与视图相关,这是怎么可能的
我能想到的一个类似的情况是,当我有一个按钮和一个TextBox,我想在单击按钮时调用TextBox上的SelectAll()方法。实际上有几种不同的方法可以做到这一点,一种解决方案是使用行为和包装类绑定到事件。首先为视图模型将触发的事件定义包装器:
public class EventTriggerWrapper
{
public event EventHandler OnTriggered;
public void Trigger()
{
this.OnTriggered?.Invoke(this, EventArgs.Empty);
}
}
为了演示,这里是按钮和WebBrowser的一些XAML,我将在视图模型中使用包装器的一个实例,在按下按钮时触发web浏览器的Navigate()
函数:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<Button Content="Click Me" Command="{Binding NavigateCommand}" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="10" />
<WebBrowser Grid.Row="1">
<i:Interaction.Behaviors>
<behaviors:MyCustomBehavior EventTrigger="{Binding EventTrigger}" />
</i:Interaction.Behaviors>
</WebBrowser>
</Grid>
因此,剩下的就是使用订阅事件的属性创建行为,然后调用目标控件中所需的任何函数:
public class MyCustomBehavior : Behavior<WebBrowser>
{
public EventTriggerWrapper EventTrigger
{
get { return (EventTriggerWrapper)GetValue(EventTriggerProperty); }
set { SetValue(EventTriggerProperty, value); }
}
// Using a DependencyProperty as the backing store for EventTrigger. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EventTriggerProperty =
DependencyProperty.Register("EventTrigger", typeof(EventTriggerWrapper), typeof(MyCustomBehavior), new PropertyMetadata(null, OnEventTriggerChanged));
private static void OnEventTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behaviour = d as MyCustomBehavior;
var oldValue = e.OldValue as EventTriggerWrapper;
if (oldValue != null)
oldValue.OnTriggered -= behaviour.OnEventTriggered;
var newValue = e.NewValue as EventTriggerWrapper;
if (newValue != null)
newValue.OnTriggered += behaviour.OnEventTriggered;
}
private void OnEventTriggered(object sender, EventArgs e)
{
if (this.AssociatedObject != null)
this.AssociatedObject.Navigate("http://www.google.com"); // <-- change this to the function you want to invoke
}
}
公共类MyCustomBehavior:行为
{
public EventTriggerWrapper EventTrigger
{
获取{return(EventTriggerWrapper)GetValue(EventTriggerProperty);}
set{SetValue(EventTriggerProperty,value);}
}
//使用DependencyProperty作为EventTrigger的后备存储。这将启用动画、样式、绑定等。。。
公共静态只读DependencyProperty EventTriggerProperty=
DependencyProperty.Register(“EventTrigger”、typeof(EventTriggerWrapper)、typeof(MyCustomBehavior)、new PropertyMetadata(null,OneEventTriggerChanged));
私有静态void OnEventTriggerChanged(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
var行为=d作为MyCustomBehavior;
var oldValue=e.oldValue作为EventTriggerWrapper;
if(oldValue!=null)
oldValue.OnTriggered-=行为.OnEventTriggered;
var newValue=e.newValue作为EventTriggerWrapper;
if(newValue!=null)
newValue.OnTriggered+=行为.OnEventTriggered;
}
私有void OnEventTriggered(对象发送方,事件参数e)
{
if(this.AssociatedObject!=null)
此.AssociatedObject.Navigate(“http://www.google.com“”;//我知道方法ZoomIn()在ViewControl.xaml.cs的代码隐藏中,并且您在ViewControl之外有一个按钮(假设两者都放在另一个用户控件或窗口中)。因此您想调用方法ZoomIn()单击按钮。这是我的理解正确吗?如果是,您可以在单击按钮时从主控件访问ViewControl的控件(m_child),其中可以将实际宽度和高度作为参数传递给方法ZoomIn()。您需要更改方法的签名。我希望我没有混淆您。请告诉我您是否清楚。@GK您的问题回答正确,但我不想使用代码隐藏。问题是这与ViewModel不相关,另一方面,我不想污染使用代码隐藏。最接近的问题是到目前为止,我遇到的是:这听起来很像是一种视图责任,你需要在某个地方使用代码。除非它在其他地方被重复使用,否则我认为建立行为没有多大意义。我非常同意Andy的观点。我不认为这会对你的视图造成多大污染。但在这里,行为似乎并不更合适.Thank Mark,对于这样一件小事来说似乎有点复杂。我尝试使用RoutedCommands来完成它,但不幸的是它还没有成功。它有点复杂,但这是因为您试图以一种非设计使用的方式使用数据绑定引擎。视图对象应该绑定到视图模型数据并响应c如果数据发生变化,您实际上要做的就是从视图模型调用视图。这从本质上打破了MVVM范式,这就是为什么它不像您希望的那样简单。我已经回答了这个问题,但在实践中,这样的挑战通常可以通过使用适当的数据绑定来解决(虽然在这种情况下,我需要看到更多的代码)。
public class MainViewModel
{
public EventTriggerWrapper EventTrigger { get; } = new EventTriggerWrapper();
private ICommand _NavigateCommand;
public ICommand NavigateCommand => this._NavigateCommand ?? (this._NavigateCommand = new RelayCommand(OnNavigate));
private void OnNavigate()
{
this.EventTrigger.Trigger();
}
}
public class MyCustomBehavior : Behavior<WebBrowser>
{
public EventTriggerWrapper EventTrigger
{
get { return (EventTriggerWrapper)GetValue(EventTriggerProperty); }
set { SetValue(EventTriggerProperty, value); }
}
// Using a DependencyProperty as the backing store for EventTrigger. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EventTriggerProperty =
DependencyProperty.Register("EventTrigger", typeof(EventTriggerWrapper), typeof(MyCustomBehavior), new PropertyMetadata(null, OnEventTriggerChanged));
private static void OnEventTriggerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behaviour = d as MyCustomBehavior;
var oldValue = e.OldValue as EventTriggerWrapper;
if (oldValue != null)
oldValue.OnTriggered -= behaviour.OnEventTriggered;
var newValue = e.NewValue as EventTriggerWrapper;
if (newValue != null)
newValue.OnTriggered += behaviour.OnEventTriggered;
}
private void OnEventTriggered(object sender, EventArgs e)
{
if (this.AssociatedObject != null)
this.AssociatedObject.Navigate("http://www.google.com"); // <-- change this to the function you want to invoke
}
}