27

我将一个 UI 绑定到一个接口(由几个演示者实现,不能从 UI 程序集中访问)。

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

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

界面设计实例

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

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

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

自定义标记扩展

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

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

谢谢 !

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

4

4 回答 4

41

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

代替

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

你可以写

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

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

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

如果是泛型,只需为您的类型添加一个反引号和类型参数的数量:<x:Type Type="commons:User`1" />

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

于 2017-10-09T00:51:38.127 回答
19

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

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

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

于 2013-11-20T23:15:07.053 回答
5

我刚刚调查了或多或少相同的问题......实际上,我所做的是遵循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>(); } }
    }

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

App.xaml 将ViewModelLocator声明为资源

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

MainWindow.DataContext属性绑定到ViewModelLocatorBasicVM成员。Text 属性绑定了接口IBasicVM的GetContent成员,该成员由 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>

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

于 2014-09-18T19:49:49.557 回答
-1

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

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

  1. 编写一个实现所需接口类型的 C# 类。
  2. 在类中实现接口,但不要自己编写整个实现。而是在实现的方法中调用实现接口的其他类方法。
  3. 开始使用您编写的课程。
  4. 您为获得兼容性而编写的类称为适配器。

示例代码:

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

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

于 2013-11-25T10:25:48.873 回答