wpf d:DesignInstance 具有接口类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18814615/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 09:39:18  来源:igfitidea点击:

d:DesignInstance with an interface type

c#wpfxamlresharper

提问by Olivier

I'm binding an UI to an interface (which is implemented by several presenters, not accessible from the UI assembly).

我将 UI 绑定到一个界面(由多个演示者实现,无法从 UI 程序集访问)。

I really like d:DesignInstance in designer because it (kind of) makes xaml strongly typed using R#.

我真的很喜欢设计器中的 d:DesignInstance 因为它(有点)使用 R# 使 xaml 强类型化。

Sadly, d:DesignInstance does not support interface types: "Cannot create an instance of an interface."

遗憾的是,d:DesignInstance 不支持接口类型:“无法创建接口的实例。”

Design instance on interface

界面设计实例

The first thing I thought is: Ok, no problem, let's create a custom markup extension which takes a System.Type as parameter, and which ProvideValue method returns a fake instance of it (dummy implementation of this interface, generated by dynamic IL emission).

我想到的第一件事是:好的,没问题,让我们创建一个自定义标记扩展,它接受一个 System.Type 作为参数,并且 ProvideValue 方法返回它的一个假实例(这个接口的虚拟实现,由动态 IL 发射生成) .

This works quite well, bindings are resolved at design-time (I can see that in the design panel since my markup extension fills the object properties with a lorem-ipsum)

这很有效,绑定在设计时解决(我可以在设计面板中看到这一点,因为我的标记扩展用 lorem-ipsum 填充了对象属性)

BUTthe nicest R# feature dont work: Resharper does not recognize datacontext type, and just gives a message "Cannot resolve property '{0}' in data context of type 'object'"

但是最好的 R# 功能不起作用:Resharper 无法识别数据上下文类型,并且只给出一条消息“无法解析‘对象’类型的数据上下文中的属性‘{0}’”

Custom markup extension

自定义标记扩展

Does someone know how to fix this ?

有人知道如何解决这个问题吗?

(any alternative which would allow me to let R# know about an interface datacontext type would be great)

(任何可以让我让 R# 知道接口数据上下文类型的替代方法都会很棒)

Thanks !

谢谢 !

ps:I also tried to create another markup extension which returns the generated runtime type in order to give it to DesignInstance: "{d:DesignInstance Type={utilsUi:InstanceType commons:User}}" => Gives the error "Object of type 'InstanceType' cannot be converted to type 'System.Type'" ... seems that DesignInstance does not support inner markup extensions :(

ps:我还尝试创建另一个标记扩展,它返回生成的运行时类型,以便将其提供给 DesignInstance:“{d:DesignInstance Type={utilsUi:InstanceType commons:User}}” => 给出错误“类型的对象” 'InstanceType' 无法转换为类型 'System.Type'" ... 似乎 DesignInstance 不支持内部标记扩展 :(

采纳答案by Benoit Patra

I have just investigated more or less the same question... Actually, what I did is to follow the principles of MvvMLight. Precisely, I used a ViewModelLocator (which will be more or less static) so that the "right" ViewModel is injected at runtime or at design time. The magic lies in the function ViewModelBase.IsInDesignModeStaticprovided by the MvvMLight framework. Finally, my ViewModelLocatorclass looks like

我刚刚调查了或多或少相同的问题......实际上,我所做的就是遵循MvvMLight的原则。准确地说,我使用了 ViewModelLocator(或多或少是静态的),以便在运行时或设计时注入“正确的”ViewModel。神奇之处在于MvvMLight 框架提供的ViewModelBase.IsInDesignModeStatic函数。最后,我的ViewModelLocator类看起来像

public class ViewModelLocator
    {
        private static readonly IKernel _kernel;

        static ViewModelLocator()
        {
            _kernel = new StandardKernel();
            if (ViewModelBase.IsInDesignModeStatic)
            {
                 _kernel.Bind<IBasicVM>().To<DesignBasicVm>();
            }
            else
            {
                _kernel.Bind<IBasicVM>().To<BasicVm>();
            }
        }

        public IBasicVM BasicVm { get { return _kernel.Get<IBasicVM>(); } }
    }

You may ignore the Ninject_kernelbut you may need it (or a similar Ioc) if you are building your ViewModels with an IoC.

您可能会忽略Ninject _kernel,但如果您使用 IoC 构建您的 ViewModel,您可能需要它(或类似的 Ioc)。

The App.xaml declares the ViewModelLocatoras a ressource

App.xaml 将ViewModelLocator声明为资源

  <Application.Resources>
    <ResourceDictionary>
      <viewModel:ViewModelLocator x:Key="ViewModelLocator" />
    </ResourceDictionary>
  </Application.Resources>

The MainWindow.DataContextproperty is bound to the BasicVMmember of the ViewModelLocator. The Text property is bound the the GetContentmember of the interface IBasicVMwhich is recognized statically by R# (at least R# 7.1 with VS2012)

所述MainWindow.DataContext属性绑定到BasicVM所述的构件ViewModelLocator。Text 属性绑定了接口IBasicVMGetContent成员,该成员由 R# 静态识别(至少 R# 7.1 与 VS2012)

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        DataContext="{Binding BasicVm, Source={StaticResource ViewModelLocator}}"
        >
    <Grid>
        <TextBlock  Text="{Binding GetContent}"/>
    </Grid>
</Window>

You may check this repositorywhich I have created as a template.

您可以查看我作为模板创建的这个存储库

回答by N. Kudryavtsev

I've found out how it's possible to overcome this illogical Visual Studio XAML designer error.

我发现了如何克服这个不合逻辑的 Visual Studio XAML 设计器错误。

Instead of

代替

<SomeControl d:DataContext={d:DesignInstance commons:IUser}>
    <!--element content here-->
</SomeControl>

You can write

你可以写

<SomeControl>
    <d:SomeControl.DataContext>
        <x:Type Type="commons:IUser" />
    </d:SomeControl.DataContext>
    <!--element content here-->
</SomeControl>

Yes, this solution doesn't look as cool but definitely it isn't much worse.

是的,这个解决方案看起来并不酷,但绝对不会更糟。

Typetag allows specifying interfaces, nested types (e.g. n:A+B) and even generics!

Type标签允许指定接口、嵌套类型(例如 n:A+B)甚至泛型!

In case of generics just add a backtick and number of type arguments to your type: <x:Type Type="commons:User`1" />.

在仿制药的情况下,只需添加一个反引号和类型参数的数目,以你的类型:<x:Type Type="commons:User`1" />

BTW all of these also works with styles, <d:Style.DataContext>doesn't produce errors!

顺便说一句,所有这些也适用于样式,<d:Style.DataContext>不会产生错误!

回答by atomaras

You can take advantage of IsDesignTimeCreatable in this case like so:

在这种情况下,您可以像这样利用 IsDesignTimeCreatable:

d:DataContext="{d:DesignInstance commons:User, IsDesignTimeCreatable=False}"

which essentially instructs the designer to only use the type for intellisense and error highlighting instead of actually trying to instantiate a design instance.

这实质上指示设计人员仅将类型用于智能感知和错误突出显示,而不是实际尝试实例化设计实例。

回答by Venkatesh Ellur

@Olivier: This is one of the best scenarios where you can make use of "Adapter Design Pattern"

@Olivier:这是您可以使用“适配器设计模式”的最佳场景之一

Note: I have no idea on Resharper, I am a C# & .NET developer. but I understand there is compatibility problem based on your explanation. below is the possible solution that you can try.

注意:我对 Resharper 一无所知,我是一名 C# 和 .NET 开发人员。但我知道根据您的解释存在兼容性问题。以下是您可以尝试的可能解决方案。

  1. Write one C# class that implement the Interface type you need.
  2. Implement the interface in the class however do not write the entire implementation on your own. instead call the other class methods that implement the interface within the implemented methods.
  3. start using the class that you have written.
  4. the class that you have written to get the compatibility is known as Adapter.
  1. 编写一个实现您需要的接口类型的 C# 类。
  2. 在类中实现接口但是不要自己编写整个实现。而是调用在已实现方法中实现接口的其他类方法。
  3. 开始使用您编写的类。
  4. 您为获得兼容性而编写的类称为适配器。

sample code:

示例代码:

class Program
{
    static void Main(string[] args)
    {
        // create an object of your adapter class and consume the features.
        AdapterInterfaceUI obj = new AdapterInterfaceUI();

        // Even though you have written an adapter it still performs the operation in base class
        // which has the interface implementation and returns the value.
        // NOTE : you are consuming the interface but the object type is under your control as you are the owner of the adapter class that you have written.
        Console.WriteLine(obj.DisplayUI());
        Console.ReadKey();
    }
}


#region code that might be implemented in the component used.
public interface IinterfaceUI
{
    string DisplayUI();
}

public class ActualUI : IinterfaceUI
{
    //Factory pattern that would be implemented in the component that you are consuming.
    public static IinterfaceUI GetInterfaceObject()
    { 
        return new ActualUI();
    }

    public string DisplayUI()
    {
        return "Interface implemented in ActualUI";
    }
}
#endregion


#region The adapter class that you may need to implement in resharper or c# which ever works for you.
public class AdapterInterfaceUI : ActualUI, IinterfaceUI
{
    public string DisplayUI()
    {
        return base.DisplayUI();
    }
}
#endregion

I think this solution will help you out.

我认为这个解决方案会帮助你。