在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
    }
}