WPF 应用程序中字典的表达式混合和示例数据
我有一个 WPF 应用程序,我正在使用 Blend 来设置样式。
我的视图模型之一属于以下类型:
public Dictionary<DateTime, ObservableCollection<MyViewModel>> TimesAndEvents
但是当我尝试在 Expression Blend 中创建一些示例数据时,它根本不会为此属性创建 XAML。
您可以在 XAML 中创建这样的数据类型吗?非设计时支持正在扼杀我的生产力。
I have a WPF app which I am using Blend to style.
One of my view models is of the type:
public Dictionary<DateTime, ObservableCollection<MyViewModel>> TimesAndEvents
But when I try to create some sample data in Expression Blend it simply doesnt create the XAML for this property.
Can you create a data type like this in XAML? The non-design time support is killing my productivity.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
关于您的最后一个问题:不幸的是,您无法轻松在 WPF 中实例化字典。我相信这个答案很好地解释了这一部分。 WPF 4.5 Unleashed 这本书很好地总结了链接答案的内容:
但即便如此,在我看来,在 xaml 中实例化该字典又是一个痛苦的过程。此外,Blend 不知道如何创建该类型的示例数据。
关于如何获得设计时支持的隐含问题:有几种方法可以在 WPF 中实现设计时数据,但目前对于复杂场景,我的首选方法是创建自定义 DataSourceProvider 。为了给予应有的信任:我从 得到了这个想法这篇文章(比这个问题还要早)。
DataSourceProvider 解决方案
创建一个实现 DataSourceProvider 并返回数据上下文的示例。将实例化的 MainWindowViewModel 传递给 OnQueryFinished 方法是奇迹发生的原因(我建议阅读它以了解它是如何工作的)。
您现在要做的就是将数据提供程序添加为视图中的示例数据上下文:
注意:
中的“d”很重要,因为它告诉 Blend 和编译器该特定元素用于设计时,在编译文件时应忽略它。完成此操作后,我的设计视图现在如下所示:
设置问题
我从 5 个类开始(其中 2 个是从 WPF 项目模板生成的,我将其推荐用于this):
MyViewModel.cs
MainWindowViewModel.cs
MainWindow.cs
我采用了生成的 MainWindow 类并更改了它。基本上,现在它请求一个 MainWindowViewModel 并将其设置为其 DataContext。
MainWindow.xaml
请注意解决方案中缺少设计数据上下文。
App.cs
首先,从 xaml 端删除
StartupUri="MainWindow.xaml"
,因为我们将从后面的代码启动 MainWindow。构建并运行
现在,如果一切都正确完成并且您充实了 MainWindowViewModel 的 Initialize 方法(我将在底部包含我的实现),那么当您构建并运行您的应用程序时,您应该会看到如下所示的屏幕WPF 应用程序:
问题又是什么?
问题是设计视图中没有显示任何内容。
我的 Initialize() 方法
Regarding your last question: unfortunately, you cannot easily instantiate dictionaries in WPF. I believe this answer explains that part well. The book, WPF 4.5 Unleashed provides a good summary of what the linked answer states:
But even then, instantiating that dictionary in xaml is again, in my opinion, a painful process. Additionally, Blend does not know how to create sample data of that type.
Regarding the implicit question of how to get design time support: there are a few ways to achieve design time data in WPF, but my preferred method at this point in time for complex scenarios is to create a custom DataSourceProvider. To give credit where it is due: I got the idea from this article (which is even older than this question).
The DataSourceProvider Solution
Create a class that implements DataSourceProvider and returns a sample of your data context. Passing the instantiated MainWindowViewModel to the OnQueryFinished method is what makes the magic happen (I suggest reading about it to understand how it works).
All that you have to do now is add your data provider as a sample data context in your view:
Note: the 'd' in
<d:Window.DataContext>
is important as it tells Blend and the compiler that that specific element is for design time and it should be ignored when the file is compiled.After doing that, my design view now looks like the following:
Setting up the problem
I started with 5 classes (2 were generated from the WPF project template, which I recommend using for this):
MyViewModel.cs
MainWindowViewModel.cs
MainWindow.cs
I took the generated MainWindow class and changed it. Basically, now it asks for a MainWindowViewModel and sets it as its DataContext.
MainWindow.xaml
Please note the lack of the design data context from the Solution.
App.cs
First off, remove
StartupUri="MainWindow.xaml"
from the xaml side as we'll be launching MainWindow from the code behind.Build and run
Now, if everything was done correctly and you fleshed out MainWindowViewModel's Initialize method (I will include my implementation at the bottom), you should see a screen like the one below when you build and run your WPF app:
What was the problem again?
The problem was that nothing was showing in the design view.
My Initialize() method
我已经走上了在定位器中创建视图模型的设计时实例的路线,我将其引用为上面建议的@ChrisW:
这样我就可以使用一些硬编码值来填充我的列表、组合框等。那样容易多了。
我使用 MVVM Light,因此在 ViewModel 的构造函数中我使用这样的模式:
代码仅在设计时执行,并且您将把 Xaml UI 绑定到实际数据。
Any more I've gone the route of creating a Design Time Instance of my Viewmodel in my Locator that I reference as @ChrisW suggested above:
So I can have some hard-coded values to populate my lists, comboboxes, etc. Makes styling everything that much easier.
I use MVVM Light and so in my ViewModel's constructor I use a pattern like this:
The code will only execute at Design Time, and you will have your Xaml UI bound to actual data.
由于Xaml 2009支持泛型类型,因此可以像这样编写松散的xaml(不能在wpf项目中编译)来表示字典。
Data.xaml
但它不被 Blend 或 Visual Studio 等设计器支持。如果将其放入与设计器关联的 xaml 中,则会出现数十个错误。为了解决这个问题,我们需要一个标记扩展来使用 XamlReader.Load 方法从 Data.xaml 提供值。
InstanceFromLooseXamlExtension.cs
此标记扩展具有 Uri 类型 Source 属性,可让用户指定要加载的 xaml 文件。最后,像这样使用标记扩展。
MainWindow.xaml
在本例中,我将 Data.xaml 放在应用程序文件夹中,因此“Source=/Data.xaml”就可以了。每次设计器重新加载(重建将确保这一点)时,将应用松散的 xaml 中的内容。结果应该类似于
松散的 xaml 可以包含几乎所有内容,例如 ResourceDictionary 或带有 UiElements 的内容。但 Blend 或 Visual Studio 都不会为您正确检查。最后,希望这足以给出答案。
Since Xaml 2009 support generic types, is possible write a loose xaml(can not be compiled in wpf project) like this to represent a dictionary.
Data.xaml
But it is not support by designers like Blend or Visual Studio. If you put it into a xaml that associated with a designer, you will get dozens of errors. To solve this, we need a markup extension to provide value from Data.xaml by using XamlReader.Load method.
InstanceFromLooseXamlExtension.cs
This markup extension has a Uri type Source property to let user specify which xaml file to load. Then finally, use the markup extension like this.
MainWindow.xaml
In this case, I place Data.xaml in application folder, so 'Source=/Data.xaml' will be OK. Every time the designer reloaded(a rebuild will ensure it), the contents in loose xaml will be applied. The result should look like
The loose xaml can contain almost everything, like a ResourceDictionary or something with UiElements. But both Blend or Visual Studio will not check it correctly for you. In the end, hope this is enough for an answer.