WPF应用程序中Dictionary的Expression Blend和示例数据 [英] Expression Blend and Sample data for Dictionary in WPF application

查看:69
本文介绍了WPF应用程序中Dictionary的Expression Blend和示例数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个WPF应用,正在使用Blend进行样式设置.

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

但是当我尝试在Expression Blend中创建一些示例数据时,它根本不会为此属性创建XAML.

But when I try to create some sample data in Expression Blend it simply doesnt create the XAML for this property.

可以在XAML中创建这样的数据类型吗?非设计时间的支持正在削弱我的生产力.

Can you create a data type like this in XAML? The non-design time support is killing my productivity.

推荐答案

关于最后一个问题:不幸的是,您不能轻松在WPF中实例化字典.我相信此答案很好地解释了这一部分. WPF 4.5发布提供了很好的总结链接答案的内容:

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:

此限制的常见解决方法(无法实例化 WPF版本的XAML中的字典)是要派生非泛型 简单地从一个通用类中获取类,以便可以从XAML中对其进行引用...

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不知道如何创建该类型的样本数据.

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.

关于如何获得设计时间支持的隐含问题:有几种方法可以在WPF中获得设计时间数据,但目前针对复杂场景的首选方法是创建自定义DataSourceProvider.要归功于应收款项:我从这篇文章(比这个问题还要老).

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).

创建一个实现 DataSourceProvider 并返回数据上下文的样本.将实例化的MainWindowViewModel传递给OnQueryFinished方法是使魔术发生的原因(我建议阅读此书以了解其工作原理).

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).

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());
    }
}

您现在要做的就是将数据提供者添加为视图中的示例数据上下文:

All that you have to do now is add your data provider as a sample data context in your 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和编译器该特定元素用于设计时,并且在编译文件时应将其忽略.

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:

我从5个类开始(其中2个是从WPF项目模板生成的,我建议为此使用):

I started with 5 classes (2 were generated from the WPF project template, which I recommend using for this):

  1. MyViewModel.cs
  2. MainWindowViewModel.cs
  3. MainWindow.xaml
  4. 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.

MainWindow.cs

I took the generated MainWindow class and changed it. Basically, now it asks for a MainWindowViewModel and sets it as its DataContext.

public partial class MainWindow : Window
{        
    public MainWindow(MainWindowViewModel viewModel)
    {
        DataContext = viewModel;
        InitializeComponent();
    }
}

MainWindow.xaml

请注意,解决方案中缺少设计数据上下文.

MainWindow.xaml

Please note the lack of the design data context from the Solution.

<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

首先,从xaml一侧删除StartupUri="MainWindow.xaml",因为我们将从后面的代码中启动MainWindow.

App.cs

First off, remove StartupUri="MainWindow.xaml" from the xaml side as we'll be launching MainWindow from the code behind.

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应用:

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:

问题在于设计视图中什么都没显示.

The problem was that nothing was showing in the design view.

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应用程序中Dictionary的Expression Blend和示例数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆