扩展 WPF 应用程序

发布于 2024-10-15 08:17:22 字数 412 浏览 3 评论 0原文

我有一个 WPF MVVM 应用程序。我的一个视图有一个用户控件,需要为每个安装进行自定义。它基本上是客户安装的草图,其中一些标签等绑定到视图模型。

现在我的问题是这个用户控件在每个站点/安装上都是不同的。一种方法是使用 xaml 读取器从文件/数据库运行时加载 xaml。这是可行的,但由于我的视图模型是通用的,我必须绑定到方法而不是属性,并且我无法使用 objectdataprovider 加载 xaml。

目前我正在尝试查看是否可以使用 MEF,以便我可以将用户控件创建为插件。所以我现在正在寻找的是:

  1. 如何使用导出 MEF 合同的视图/视图模型定义用户控件
  2. 我的父视图(在我的 wpf 应用程序中)如何加载导入的用户控件

任何提示表示赞赏,或者也许有人有不同的方法?

I've got a WPF MVVM application. One of my views has a user control that needs to be customizable for each installation. It's basically a sketch of the customers installation with some labels etc. bound to a viewmodel.

Now my problem is that this user control is different on each site/installation. One approach is to load the xaml from a file/database runtime using a xaml reader. This works but since my viewmodel is generic I have to bind to methods instead of properties and I can't load a xaml with objectdataprovider.

Currently I'm trying to see if MEF can be used so that I can create the user control as a plug-in. So what I'm looking for now is this:

  1. how can I define a user control with view/view model that exports a contract for MEF
  2. How can my parent view (in my wpf app) load the imported user control

Any tips are appreciated, or maybe someone has a different approach?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

铁轨上的流浪者 2024-10-22 08:17:22

我建议您将 Prism 与 MEF 结合使用。它有模块(在您的情况下是插件)和区域(动态加载视图的机制)的概念。

您将能够使用简单的属性导出视图:

[ViewExport(RegionName = RegionNames.MyRegion)]
public partial class MyView : UserControl {
    public MyView() {
        this.InitializeComponent();
    }

    [Import]
    public MyViewModel ViewModel {
        set { DataContext = value; }
    }
}

[Export]
public class MyViewModel : ViewModelBase
[
  ...
}

并且在主应用程序 XAML 中,您将能够像这样导入插件的视图:

<ContentControl Regions:RegionManager.RegionName="{x:Static Infrastructure:RegionNames.MyRegion}"/>

I suggest you look into Prism in combination with MEF. It has a notion of Modules (plug-ins in your case) and Regions (mechanism of dynamically loading views).

You will be able to export a view using a simple attribute:

[ViewExport(RegionName = RegionNames.MyRegion)]
public partial class MyView : UserControl {
    public MyView() {
        this.InitializeComponent();
    }

    [Import]
    public MyViewModel ViewModel {
        set { DataContext = value; }
    }
}

[Export]
public class MyViewModel : ViewModelBase
[
  ...
}

And in your main application XAML you will be able to import the plugin's views like this:

<ContentControl Regions:RegionManager.RegionName="{x:Static Infrastructure:RegionNames.MyRegion}"/>
梦巷 2024-10-22 08:17:22

我要考虑的一件事是您需要为每次安装安装自定义视图的设计。相反,我希望使该视图更加通用。从长远来看,这将使您的设计更加简单。另外,您正在为每个安装基础进行不同的安装而面临维护噩梦。

从你的描述中很难看出,但听起来视图是某种对象的集合(某种带有标签的绘图或其他东西)。因此,我会这样对待它。

我将创建一个基本抽象类来描述您的视图可以显示的每个对象。由于我没有更多信息,由于缺乏更好的术语,我将其称为“DrawingObject”。此类将保存视图中所有对象共有的所有信息。请注意,ObservableItem 是一个实现 INotifyPropertyChanged 的​​类,SetProperty 在该基类中设置值并引发 PropertyChanged。

abstract class DrawingObject : ObservableItem
{
    Point mPosition;
    public Point Position
    {
        get { return mPosition; }
        set { SetProperty("Position", ref mPosition, value); }
    }

    String mLabelText;
    public String LabelText
    {
        get { return mLabelText; }
        set { SetProperty("LabelText", ref mLabelText, value); }
    }
}

然后,从该基类派生更多自定义对象:

class Counter : DrawingObject
{
    public Counter() : base()
    {
    }
}

然后,您的 ViewModel 将使用基类拥有这些对象的集合。该集合可能是私有的,因为您可能会从构造函数中的某个位置获取对象(即数据库,或平面文件,或...)

class ViewModel : ObservableItem
{

    public ViewModel() : base()
    {
        // Call something to populate DrawingObjects property
        PopulateDrawingObjects();
    }

    ObservableCollection<DrawingObject> mDrawingObjects = 
        new ObservableCollection<DrawingObject>();
    public ObservableCollection<DrawingObject> DrawingObjects
    {
        get { return mDrawingObjects; }
        private set { mDrawingObjects = value; }
    }
}

然后,您的视图将绑定到该集合并适当地绘制它们(我将将其作为实施者的练习)。

我没有展示的一项扩展是 DrawingObject 可能需要实现适当的序列化功能。

显然,这是设计的粗略草图,可能有一些错误(我是凭自己的想法做的),但希望这足以继续下去。

One thing I'd consider is the design where you need to install a custom View for each installation. Instead, I'd look to make that View more generic. This will make your design more simple in the long run. Plus, you are setting up for a maintenance nightmare with a different installation for every installed base.

It's a little difficult to tell from your description, but it sounds like the View is a collection of some kind of an object (some kind of drawing with a label or something). Therefore, I'd treat it as such.

I'd create a base abstract class that describes what every object that your View could show. Since I don't have more information, I'll call this thing a "DrawingObject" for lack of a better term. This class would hold all information common to all objects in your View. Note that ObservableItem is a class that implements INotifyPropertyChanged, and SetProperty sets the value in that base class and raises PropertyChanged.

abstract class DrawingObject : ObservableItem
{
    Point mPosition;
    public Point Position
    {
        get { return mPosition; }
        set { SetProperty("Position", ref mPosition, value); }
    }

    String mLabelText;
    public String LabelText
    {
        get { return mLabelText; }
        set { SetProperty("LabelText", ref mLabelText, value); }
    }
}

Then, derive more custom objects from that base class:

class Counter : DrawingObject
{
    public Counter() : base()
    {
    }
}

Your ViewModel would then just have a collection of these objects, using the base class. The set may be private, because you will probably get the objects from someplace in the constructor (i.e. the database, or a flat file, or...)

class ViewModel : ObservableItem
{

    public ViewModel() : base()
    {
        // Call something to populate DrawingObjects property
        PopulateDrawingObjects();
    }

    ObservableCollection<DrawingObject> mDrawingObjects = 
        new ObservableCollection<DrawingObject>();
    public ObservableCollection<DrawingObject> DrawingObjects
    {
        get { return mDrawingObjects; }
        private set { mDrawingObjects = value; }
    }
}

Then, your View would bind to this collection and draw them appropriately (I'll leave that as an exercise for the implementer).

One extension that I didn't show is that the DrawingObject may need to implement the appropriate serialization functionality.

Obviously, this is a rough sketch of the design, and may have a couple of errors (I did it from my head), but hopefully it's enough to go on.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文