2

我正在为我在这里讨论过的按钮单击事件创建一个标记扩展。

我的下一个问题是如何将参数从 XAML 传递给 viewmodel 中的方法?

4

2 回答 2

1

这是一个允许您传递参数的解决方案,您可以发送您的任何属性以及您的DataContext属性ControlsDataContext

标记扩展

public class Call : MarkupExtension
{
    public Call() { }

    public string MethodName { get; set; }
    public string Path { get; set; }
    public string ElementName { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        IProvideValueTarget targetProvider = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget;
        if (targetProvider != null)
        {
            var target = targetProvider.TargetObject as FrameworkElement;
            if (target != null)
            {
                if (targetProvider.TargetProperty is MethodInfo)
                {
                    var targetEventAddMethod = targetProvider.TargetProperty as MethodInfo;
                    if (targetEventAddMethod != null)
                    {
                        var delegateType = targetEventAddMethod.GetParameters()[1].ParameterType;
                        var methodInfo = this.GetType().GetMethod("MyProxyHandler", BindingFlags.NonPublic | BindingFlags.Instance);
                        return Delegate.CreateDelegate(delegateType, this, methodInfo); ;
                    }
                }
                else if (targetProvider.TargetProperty is EventInfo)
                {
                    var targetEventInfo = targetProvider.TargetProperty as EventInfo;
                    if (targetEventInfo != null)
                    {
                        var delegateType = targetEventInfo.EventHandlerType;
                        MethodInfo methodInfo = this.GetType().GetMethod("MyProxyHandler", BindingFlags.NonPublic | BindingFlags.Instance);
                        return Delegate.CreateDelegate(delegateType, this, methodInfo);
                    }
                }
            }
        }
        return null;
    }

    void MyProxyHandler(object sender, EventArgs e)
    {
        FrameworkElement target = sender as FrameworkElement;
        if (target == null) return;
        var dataContext = target.DataContext;
        if (dataContext == null) return;

        //get the method on the datacontext from its name
        MethodInfo methodInfo = dataContext.GetType().GetMethod(MethodName, BindingFlags.Public | BindingFlags.Instance);
        if (!string.IsNullOrEmpty(ElementName))
        {
            var element = FindVisualChildren(dataContext as DependencyObject).FirstOrDefault(c => c.Name.Equals(ElementName));
            if (element != null)
            {
                var path = element.GetType().GetProperty(Path);
                if (path != null)
                {
                    methodInfo.Invoke(dataContext, new object[] { path.GetValue(element, null) });
                }
                return;
            }
        }

        if (!string.IsNullOrEmpty(Path))
        {
            var path = dataContext.GetType().GetProperty(Path);
            if (path != null)
            {
                methodInfo.Invoke(dataContext, new object[] { path.GetValue(dataContext, null) });
            }
            return;
        }

        methodInfo.Invoke(dataContext,null);
    }

    private IEnumerable<FrameworkElement> FindVisualChildren(DependencyObject obj) 
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(obj, i);
            if (child != null && child is FrameworkElement)
            {
                yield return (FrameworkElement)child;
            }
            var childOfChild = FindVisualChildren(child);
            if (childOfChild != null)
            {
                foreach (var subchild in childOfChild)
                {
                    yield return subchild;
                }
            }
        }
    }
}

测试应用:

代码:

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window 
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private int myVar = 700;
    public int MyProperty
    {
        get { return myVar; }
        set { myVar = value; }
    }

    public void TestMethod1()
    {

    }

    public void TestMethod2(int value)
    {

    }

    public void TestMethod3(string value)
    {

    }
}

xml:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication11"
        xmlns:System="clr-namespace:System;assembly=mscorlib" x:Class="WpfApplication11.MainWindow"
        Title="MainWindow" Height="123" Width="275" x:Name="UI"
        >
    <Grid DataContext="{Binding ElementName=UI}">
        <StackPanel>
        <TextBlock Name="txtblock" Text="Hello" />
            <Button Click="{local:Call  MethodName=TestMethod1}" Height="24"/>
            <Button Click="{local:Call  MethodName=TestMethod2, Path=MyProperty}"  Height="24"/>
            <Button Click="{local:Call  MethodName=TestMethod3, ElementName=txtblock, Path=Text}" Height="24" />
        </StackPanel>
    </Grid>
</Window>
于 2013-01-08T09:19:16.157 回答
0

这是我对事件方法绑定的实现:

http://www.singulink.com/CodeIndex/post/building-the-ultimate-wpf-event-method-binding-extension

支持完整的属性路径支持、任意数量的参数、绑定和参数值的其他扩展方法,并根据提供的参数类型解析方法。

用法:

<!--  Basic usage  -->
<Button Click="{data:MethodBinding OpenFromFile}" Content="Open" />

<!--  Pass in a binding as a method argument  -->
<Button Click="{data:MethodBinding Save, {Binding CurrentItem}}" Content="Save" />

<!--  Another example of a binding, but this time to a property on another element  -->
<ComboBox x:Name="ExistingItems" ItemsSource="{Binding ExistingItems}" />
<Button Click="{data:MethodBinding Edit, {Binding SelectedItem, ElementName=ExistingItems}}" />

<!--  Pass in a hard-coded method argument, XAML string automatically converted to the proper type  -->
<ToggleButton Checked="{data:MethodBinding SetWebServiceState, True}"
                Content="Web Service"
                Unchecked="{data:MethodBinding SetWebServiceState, False}" />

<!--  Pass in sender, and match method signature automatically -->
<Canvas PreviewMouseDown="{data:MethodBinding SetCurrentElement, {data:EventSender}, ThrowOnMethodMissing=False}">
    <controls:DesignerElementTypeA />
    <controls:DesignerElementTypeB />
    <controls:DesignerElementTypeC />
</Canvas>

    <!--  Pass in EventArgs  -->
<Canvas MouseDown="{data:MethodBinding StartDrawing, {data:EventArgs}}"
        MouseMove="{data:MethodBinding AddDrawingPoint, {data:EventArgs}}"
        MouseUp="{data:MethodBinding EndDrawing, {data:EventArgs}}" />

<!-- Support binding to methods further in a property path -->
<Button Content="SaveDocument" Click="{data:MethodBinding CurrentDocument.DocumentService.Save, {Binding CurrentDocument}}" />

查看模型方法签名:

public void OpenFromFile();
public void Save(DocumentModel model);
public void Edit(DocumentModel model);

public void SetWebServiceState(bool state);

public void SetCurrentElement(DesignerElementTypeA element);
public void SetCurrentElement(DesignerElementTypeB element);
public void SetCurrentElement(DesignerElementTypeC element);

public void StartDrawing(MouseEventArgs e);
public void AddDrawingPoint(MouseEventArgs e);
public void EndDrawing(MouseEventArgs e);

public class Document
{
    // Fetches the document service for handling this document
    public DocumentService DocumentService { get; }
}

public class DocumentService
{
    public void Save(Document document);
}
于 2016-07-05T18:25:50.173 回答