По поводу вашего последнего вопроса: к сожалению, вы не можете легко создавать словари в WPF. Я считаю, этот ответ хорошо объясняет эту часть. В книге WPF 4.5 Unleashed содержится хорошее резюме того, что говорится в связанном ответе:
Обычный обходной путь для этого ограничения (неспособность создать экземпляр
словарь в WPF версии XAML) для получения неуниверсального
класс из универсального просто так, чтобы на него можно было ссылаться из XAML ...
Но даже тогда создание этого словаря в xaml снова, на мой взгляд, болезненный процесс. Кроме того, Blend не знает, как создавать образцы данных этого типа.
Относительно неявного вопроса о том, как получить поддержку времени разработки: Есть несколько способов получения данных времени разработки в WPF, но на данный момент для сложных сценариев я предпочитаю создавать пользовательский DataSourceProvider. Чтобы отдать должное, где это происходит: я получил идею из этой статьи (которая даже старше этого вопроса).
Решение 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());
}
}
Все, что вам нужно сделать сейчас, это добавить поставщика данных в качестве примера контекста данных в вашем представлении:
<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' в <d:Window.DataContext>
важно, поскольку оно сообщает Blend и компилятору, что этот конкретный элемент предназначен для времени разработки, и его следует игнорировать при компиляции файла.
После этого мой вид дизайна теперь выглядит следующим образом:
![An image of Blend's design view with sample data in it.](https://i.stack.imgur.com/3aOXq.png)
Настройка проблемы
Я начал с 5 классов (2 были сгенерированы из шаблона проекта WPF, который я рекомендую использовать для этого):
- MyViewModel.cs
- MainWindowViewModel.cs
- MainWindow.xaml
- App.xaml
MyViewModel.cs
public class MyViewModel
{
public string EventName { get; set; }
}
MainWindowViewModel.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
}
}
MainWindow.cs
Я взял сгенерированный класс MainWindow и изменил его. По сути, теперь он запрашивает MainWindowViewModel и устанавливает его в качестве своего DataContext.
public partial class MainWindow : Window
{
public MainWindow(MainWindowViewModel viewModel)
{
DataContext = viewModel;
InitializeComponent();
}
}
MainWindow.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>
App.cs
Прежде всего, удалите 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();
}
}
Сборка и запуск
Теперь, если все было сделано правильно и вы реализовали метод Initialize MainWindowViewModel (я включу мою реализацию внизу), вы должны увидеть экран, подобный показанному ниже, когда вы собираете и запускаете свой WPF приложение:
![An image of what your screen should look like.](https://i.stack.imgur.com/J4zPy.png)
В чем опять проблема?
Проблема заключалась в том, что в режиме конструктора ничего не отображалось.
![An image depicting a blank screen in Blend's design view.](https://i.stack.imgur.com/LgJcd.png)
My Initialize () method
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;
}