我有一个 WPF 应用程序,我正在使用 Blend 来设计样式。
我的 View 模型之一属于以下类型:
public Dictionary<DateTime, ObservableCollection<MyViewModel>> TimesAndEvents
但是当我尝试在 Expression Blend 中创建一些示例数据时,它根本不会为此属性创建 XAML。
您可以在 XAML 中创建这样的数据类型吗?非设计时间支持正在扼杀我的生产力。
最佳答案
关于您的最后一个问题:不幸的是,您无法在 WPF 中轻松实例化字典。我相信this answer很好地解释了那部分。本书,WPF 4.5 Unleashed很好地总结了链接答案的内容:
A common workaround for this limitation (not being able to instantiate a dictionary in WPF's version of XAML) is to derive a non-generic class from a generic one simply so it can be referenced from XAML...
但即便如此,在我看来,在 xaml 中实例化该字典也是一个痛苦的过程。此外,Blend 不知道如何创建该类型的示例数据。
关于如何获得设计时支持的隐含问题:有几种方法可以在 WPF 中实现设计时数据,但我目前对于复杂场景的首选方法是创建自定义 DataSourceProvider。给予应有的信任:我的想法来自 this article (这甚至比这个问题还要老)。
DataSourceProvider 解决方案
创建一个实现 DataSourceProvider 的类并返回数据上下文的样本。将实例化的 MainWindowViewModel 传递给 OnQueryFinished 方法是使魔法发生的原因(我建议阅读它以了解它是如何工作的)。
internal class SampleMainWindowViewModelDataProvider : DataSourceProvider
{
private MainWindowViewModel GenerateSampleData()
{
var myViewModel1 = new MyViewModel { EventName = "SampleName1" };
var myViewModel2 = new MyViewModel { EventName = "SampleName2" };
var myViewModelCollection1 = new ObservableCollection<MyViewModel> { myViewModel1, myViewModel2 };
var timeToMyViewModelDictionary = new Dictionary<DateTime, ObservableCollection<MyViewModel>>
{
{ DateTime.Now, myViewModelCollection1 }
};
var viewModel = new MainWindowViewModel()
{
TimesAndEvents = timeToMyViewModelDictionary
};
return viewModel;
}
protected sealed override void BeginQuery()
{
OnQueryFinished(GenerateSampleData());
}
}
您现在要做的就是在您的 View 中添加您的数据提供者作为示例数据上下文:
<Window x:Class="SampleDataInBlend.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SampleDataInBlend"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="300">
<d:Window.DataContext>
<local:SampleMainWindowViewModelDataProvider/>
</d:Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding TimesAndEvents}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<ListBox ItemsSource="{Binding Value}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:MyViewModel}">
<TextBlock Text="{Binding EventName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
注:
<d:Window.DataContext>
中的“d”很重要,因为它告诉 Blend 和编译器该特定元素用于设计时,在编译文件时应忽略它。这样做之后,我的设计 View 现在如下所示:
设置问题
我从 5 个类开始(2 个是从 WPF 项目模板生成的,我建议为此使用):
View 模型.cs
public class MyViewModel
{
public string EventName { get; set; }
}
主窗口 View 模型.cs
public class MainWindowViewModel
{
public IDictionary<DateTime, ObservableCollection<MyViewModel>> TimesAndEvents { get; set; } = new Dictionary<DateTime, ObservableCollection<MyViewModel>>();
public void Initialize()
{
//Does some service call to set the TimesAndEvents property
}
}
主窗口.cs
我采用了生成的 MainWindow 类并对其进行了更改。基本上,现在它要求一个 MainWindowViewModel 并将其设置为它的 DataContext。
public partial class MainWindow : Window
{
public MainWindow(MainWindowViewModel viewModel)
{
DataContext = viewModel;
InitializeComponent();
}
}
主窗口.xaml
请注意解决方案中缺少设计数据上下文。
<Window x:Class="SampleDataInBlend.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SampleDataInBlend"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="300">
<Grid>
<ListBox ItemsSource="{Binding TimesAndEvents}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<ListBox ItemsSource="{Binding Value}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:MyViewModel}">
<TextBlock Text="{Binding EventName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
应用程序
首先,删除
StartupUri="MainWindow.xaml"
从 xaml 方面,因为我们将从后面的代码启动 MainWindow。public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var viewModel = new MainWindowViewModel();
// MainWindowViewModel needs to have its dictionary filled before its
// bound to as the IDictionary implementation we are using does not do
// change notification. That is why were are calling Initialize before
// passing in the ViewModel.
viewModel.Initialize();
var view = new MainWindow(viewModel);
view.Show();
}
}
构建并运行
现在,如果一切顺利,您充实了 MainWindowViewModel 的 Initialize 方法 (我将在底部包含我的实现),当您构建和运行 WPF 应用程序时,您应该会看到如下所示的屏幕:
又是什么问题?
问题是设计 View 中没有显示任何内容。
我的 Initialize() 方法
public void Initialize()
{
TimesAndEvents = PretendImAServiceThatGetsDataForMainWindowViewModel();
}
private IDictionary<DateTime, ObservableCollection<MyViewModel>> PretendImAServiceThatGetsDataForMainWindowViewModel()
{
var myViewModel1 = new MyViewModel { EventName = "I'm real" };
var myViewModel2 = new MyViewModel { EventName = "I'm real" };
var myViewModelCollection1 = new ObservableCollection<MyViewModel> { myViewModel1, myViewModel2 };
var timeToMyViewModelDictionary = new Dictionary<DateTime, ObservableCollection<MyViewModel>>
{
{ DateTime.Now, myViewModelCollection1 }
};
return timeToMyViewModelDictionary;
}
关于wpf - WPF 应用程序中字典的表达式混合和示例数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6450231/