如何从XAML中定义的事件的MarkupExtension传递参数到wpf 4.5中模型中定义的方法

本文关键字:定义 wpf 方法 参数 模型 MarkupExtension XAML 事件 | 更新日期: 2023-09-27 17:50:55

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

我的下一个问题是如何从XAML传递参数到视图模型中的方法?

如何从XAML中定义的事件的MarkupExtension传递参数到wpf 4.5中模型中定义的方法

这是一个允许你传递参数的解决方案,你可以从你的DataContextControls发送任何属性,在你的DataContext

MarkupExtension

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

TestApp:

代码:

/// <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)
    {
    }
}
Xaml:

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

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

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);
}