【问题标题】:Common Interface for FrameworkElement and FrameworkContentElement with dynamic programming具有动态编程的 FrameworkElement 和 FrameworkContentElement 的通用接口
【发布时间】:2016-12-11 22:48:39
【问题描述】:

我尽量简短地说:FrameworkElement 和 FrameworkContentElement 共享许多相同的 API,但没有通用接口。仅 DependencyObject 作为基类。

我遇到过this implementation of IFrameworkElement,它手动添加了一个接口和两个包装类。现在这段代码是在 .NET 3.5 中实现的,作者评论说在 .NET 4 中使用动态编程会容易得多:

实际代码非常简单,但每个元素大约有 624 行。 (这将是动态语言中更简单的单行实现 - C# 4.0 即将推出 :))。

我会很好奇它的实现会是什么样子。我认为它可以归结为IFrameworkElement 的动态实现,并阅读ExpandoObjectDynamicObject 看看我是否可以实现自己,但我有点难过。我想人们可以编写一个 DynamicObject 的自定义实现——但这不是一个单一的衬里。动态编程真的可以让这那么变得简单吗?我什至不需要成为一个班轮,我可以用 10 甚至 100 行而不是原来的 1250。

我在想这样的事情:

// Example, not working:
public IFrameworkElement AsFrameworkElement(FrameworkElement ele)
{
  dynamic ife = ele as IFrameworkElement;
  return ife;    
}

IFrameworkElement frameworkElement = AsFrameworkElement(new Button());
frameworkElement.DataContext = "Whatever";

IFrameworkElement frameworkContentElement = AsFrameworkElement(new Paragraph());
frameworkContentElement.DataContext = "Whatever again";

【问题讨论】:

  • 那个网站上的代码 sn-ps 是怎么回事?它们对我来说是重叠的。

标签: c# wpf dynamic frameworkelement


【解决方案1】:

看博客原代码:

var dataContect = "DataContext";
var frameworkElement = sender as FrameworkElement;
if ( frameworkElement != null )
{
    frameworkElement.DataContext = dataContect;
}
else
{
    var frameworkContentElement = sender as FrameworkContentElement;
    if ( frameworkContentElement != null )
    {
        frameworkContentElement.DataContext = dataContect;
    }
}

会变成

var dataContext = "DataContext"
dynamic element = sender;
element.DataContext = dataContext;

已经这样了。在运行时,将通过反射搜索名为 DataContext 的属性(注意:当涉及到动态类型列表时,速度可能会非常慢),然后调用它。

注意:如果该属性不存在,则会抛出RuntimeBinderException。您可以在最后一行添加一些 try...catch。

【讨论】:

  • 但这不会给我带来 IFrameworkElement 界面的舒适感。当然,如果我知道名称,我可以从动态变量中调用所有成员,但我想我可以“转换”这两个对象,所以我可以从界面中选择可用的成员。
  • @Lennart 为了在编写代码时感到舒适,请使用一个具体的类,这样智能感知就可以工作。之后,将其替换为dynamic
  • 它也不会给您带来具体类的性能优势,但它是正确的答案。
  • 你有没有想到类似IFrameworkElement = dynamic_cast(sender, typeof(IFrameworkElement)); 的东西?好吧,那很好,但这不是微软实现动态功能的方式。也许我们可以向他们建议。
【解决方案2】:

我不知道那篇文章的作者到底是什么意思(也许我们应该问他/她),但我想它需要的代码不止一行。 关键是dynamic keyword(4.0 .NET 的新功能)允许所谓的duck typing

本文作者必须编写 2 个包装类,以使 FrameworkElementFrameworkContentElement 实现 IFrameworkElement 接口。

现在使用dynamic 键,我们可以只编写类(为了保持界面的舒适度)。

public interface IFrameworkElement
{
    /* Let's suppose we have just one property, since it is a sample */
    object DataContext
    {
        get;
        set;
    }
}

public class FrameworkElementImpl : IFrameworkElement
{
    private readonly dynamic target;

    public FrameworkElementImpl(dynamic target)
    {
        this.target = target;
    }

    public object DataContext
    {
        get
        {
            return target.DataContext;
        }
        set
        {
            target.DataContext = value;
        }
    }
}

public static class DependencyObjectExtension
{
    public static IFrameworkElement AsIFrameworkElement(this DependencyObject dp)
    {
        if (dp is FrameworkElement || dp is FrameworkContentElement)
        {
            return new FrameworkElementImpl(dp);
        }

        return null;
    }
}

所以现在我们可以在代码中编写如下内容:

System.Windows.Controls.Button b = new System.Windows.Controls.Button();
IFrameworkElement ife = b.AsIFrameworkElement();

ife.DataContext = "it works!";

Debug.Assert(b.DataContext == ife.DataContext);

现在,如果您不想编写包装器(或您希望的代理)类(即我们示例中的FrameworkElementImpl),有一些库可以为您完成(impromptu-interfaceCastle DynamicProxy)。

您可以找到here 一个使用 Castle DynamicProxy 的非常简单的示例:

public class Duck
{
    public void Quack()
    {
        Console.WriteLine("Quack Quack!");
    }

    public void Swim()
    {
        Console.WriteLine("Swimming...");
    }
}

public interface IQuack
{
    void Quack();
}

public interface ISwimmer
{
    void Swim();
}

public static class DuckTypingExtensions
{
    private static readonly ProxyGenerator generator = new ProxyGenerator();

    public static T As<T>(this object o)
    {
        return generator.CreateInterfaceProxyWithoutTarget<T>(new DuckTypingInterceptor(o));
    }
}

public class DuckTypingInterceptor : IInterceptor
{
    private readonly object target;

    public DuckTypingInterceptor(object target)
    {
        this.target = target;
    }

    public void Intercept(IInvocation invocation)
    {
        var methods = target.GetType().GetMethods()
            .Where(m => m.Name == invocation.Method.Name)
            .Where(m => m.GetParameters().Length == invocation.Arguments.Length)
            .ToList();
        if (methods.Count > 1)
            throw new ApplicationException(string.Format("Ambiguous method match for '{0}'", invocation.Method.Name));
        if (methods.Count == 0)
            throw new ApplicationException(string.Format("No method '{0}' found", invocation.Method.Name));
        var method = methods[0];
        if (invocation.GenericArguments != null && invocation.GenericArguments.Length > 0)
            method = method.MakeGenericMethod(invocation.GenericArguments);
        invocation.ReturnValue = method.Invoke(target, invocation.Arguments);
    }
}

如您所见,在这种情况下,只需几行代码即可获得与作者相同的结果

每个元素大约有 624 行 [...]

【讨论】:

    猜你喜欢
    • 2022-11-07
    • 1970-01-01
    • 2016-06-02
    • 2013-08-22
    • 1970-01-01
    • 2019-10-12
    • 1970-01-01
    • 2020-08-10
    • 2013-10-04
    相关资源
    最近更新 更多