WPF应用程序中字典的表达式混合和示例数据

WPF应用程序中字典的表达式混合和示例数据,wpf,xaml,expression-blend,Wpf,Xaml,Expression Blend,我有一个WPF应用程序,我正在使用混合的风格 我的一个视图模型属于以下类型: public Dictionary<DateTime, ObservableCollection<MyViewModel>> TimesAndEvents 公共字典时间和事件 但是,当我尝试在Expression Blend中创建一些示例数据时,它并没有为此属性创建XAML 可以在XAML中创建这样的数据类型吗?非设计时支持正在扼杀我的生产力。我已经在定位器中创建了我的Viewmodel的设

我有一个WPF应用程序,我正在使用混合的风格

我的一个视图模型属于以下类型:

public Dictionary<DateTime, ObservableCollection<MyViewModel>> TimesAndEvents
公共字典时间和事件
但是,当我尝试在Expression Blend中创建一些示例数据时,它并没有为此属性创建XAML


可以在XAML中创建这样的数据类型吗?非设计时支持正在扼杀我的生产力。

我已经在定位器中创建了我的Viewmodel的设计时实例,我引用了上面提到的@ChrisW:

d:DataContext="{Binding Source={StaticResource Locator}, Path=DesignTimeVM}"
所以我可以用一些硬编码的值来填充我的列表、组合框等。这样就可以更容易地设置所有内容的样式

我使用MVVM灯光,因此在ViewModel的构造函数中,我使用如下模式:

if(IsInDesignMode)
{
  ListUsers = new List<User>();
.
.
.
}
if(IsInDesignMode)
{
ListUsers=新列表();
.
.
.
}

代码将仅在设计时执行,并且您的Xaml UI将绑定到实际数据。

关于最后一个问题:不幸的是,您无法在WPF中轻松实例化词典。我相信这部分解释得很好。这本书很好地总结了链接答案的内容:

此限制的常见解决方法(无法实例化 WPF版本(XAML)中的字典将派生非泛型 从泛型类初始化,以便可以从XAML引用它

但即使如此,在我看来,在xaml中实例化该词典也是一个痛苦的过程。此外,Blend不知道如何创建该类型的示例数据

关于如何获得设计时支持的隐含问题:有几种方法可以在WPF中实现设计时数据,但对于复杂场景,此时我首选的方法是创建自定义数据源提供程序。值得称赞的是:我的想法来自(比这个问题还要古老)


DataSourceProvider解决方案 创建一个实现并返回数据上下文示例的类。将实例化的MainWindowViewModel传递给OnQueryFinished方法是实现这一神奇效果的原因(我建议阅读相关内容以了解其工作原理)

MainWindowViewModel.cs 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>
构建并运行 现在,如果一切都正确完成并且充实了MainWindowViewModel的初始化方法(我将在底部包括我的实现),那么在构建和运行WPF应用程序时,您应该会看到如下屏幕:

又是什么问题? 问题是设计视图中没有显示任何内容


我的Initialize()方法
public void Initialize()
{
TimesAndEvents=假装IMAServiceThatGetSDataFormainWindowViewModel();
}
私有IDictionary服务GetSDataFormainWindowViewModel()
{
var myViewModel1=newmyviewmodel{EventName=“I'm real”};
var myViewModel2=newmyviewmodel{EventName=“I'm real”};
var myViewModelCollection1=新的ObservableCollection{myViewModel1,myViewModel2};
var timeToMyViewModelDictionary=新字典
{
{DateTime.Now,myViewModelCollection1}
};
返回timeToMyViewModelDictionary;
}

因为Xaml 2009支持泛型类型,所以可以编写一个松散的Xaml(不能在wpf项目中编译)来表示字典

Data.xaml

<gnrc:Dictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:sys="clr-namespace:System;assembly=mscorlib"
                 xmlns:gnrc="clr-namespace:System.Collections.Generic;assembly=mscorlib"
                 xmlns:om="clr-namespace:System.Collections.ObjectModel;assembly=System"
                 x:TypeArguments="sys:DateTime,om:ObservableCollection(x:String)">
    <om:ObservableCollection x:TypeArguments="x:String">
        <x:Key>
            <sys:DateTime>2017/12/31</sys:DateTime>
        </x:Key>
        <x:String>The last day of the year.</x:String>
        <x:String>Party with friends.</x:String>
    </om:ObservableCollection>
    <om:ObservableCollection x:TypeArguments="x:String">
        <x:Key>
            <sys:DateTime>2018/1/1</sys:DateTime>
        </x:Key>
        <x:String>Happy new year.</x:String>
        <x:String>Too much booze.</x:String>
    </om:ObservableCollection>
    <om:ObservableCollection x:TypeArguments="x:String">
        <x:Key>
            <sys:DateTime>2018/1/10</sys:DateTime>
        </x:Key>
        <x:String>Just another year.</x:String>
        <x:String>Not much difference.</x:String>
    </om:ObservableCollection>
</gnrc:Dictionary>
<Window x:Class="WpfApp.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:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <ListBox ItemsSource="{local:InstanceFromLooseXaml Source=/Data.xaml}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Expander Header="{Binding Key}">
                    <ListBox ItemsSource="{Binding Value}"/>
                </Expander>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Window>
此标记扩展具有Uri类型的源属性,使用户可以指定要加载的xaml文件。最后,像这样使用标记扩展

main window.xaml

<gnrc:Dictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:sys="clr-namespace:System;assembly=mscorlib"
                 xmlns:gnrc="clr-namespace:System.Collections.Generic;assembly=mscorlib"
                 xmlns:om="clr-namespace:System.Collections.ObjectModel;assembly=System"
                 x:TypeArguments="sys:DateTime,om:ObservableCollection(x:String)">
    <om:ObservableCollection x:TypeArguments="x:String">
        <x:Key>
            <sys:DateTime>2017/12/31</sys:DateTime>
        </x:Key>
        <x:String>The last day of the year.</x:String>
        <x:String>Party with friends.</x:String>
    </om:ObservableCollection>
    <om:ObservableCollection x:TypeArguments="x:String">
        <x:Key>
            <sys:DateTime>2018/1/1</sys:DateTime>
        </x:Key>
        <x:String>Happy new year.</x:String>
        <x:String>Too much booze.</x:String>
    </om:ObservableCollection>
    <om:ObservableCollection x:TypeArguments="x:String">
        <x:Key>
            <sys:DateTime>2018/1/10</sys:DateTime>
        </x:Key>
        <x:String>Just another year.</x:String>
        <x:String>Not much difference.</x:String>
    </om:ObservableCollection>
</gnrc:Dictionary>
<Window x:Class="WpfApp.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:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <ListBox ItemsSource="{local:InstanceFromLooseXaml Source=/Data.xaml}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Expander Header="{Binding Key}">
                    <ListBox ItemsSource="{Binding Value}"/>
                </Expander>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Window>

在本例中,我将Data.xaml放在应用程序文件夹中,因此“Source=/Data.xaml”就可以了。每次重新加载设计器时(重建将确保它),将应用松散xaml中的内容。结果应该是这样的


松散的xaml可以包含几乎所有内容,如ResourceDictionary或带有UiElements的内容。但是Blend或Visual Studio都不会为您正确检查它。最后,希望这足以作为答案。

仍在寻找答案question@Sentry这里可以问很多问题。您是否使用实际的设计时数据提供对模拟视图模型的引用?如果是这样,您能否将设计时数据上下文指定为类似于(在父级上)
d:DataContext=“{StaticResource pathtodesigntimedatavmstuffinesources”的资源类型
所以它只是在设计时将datacontext交换为假的东西?@ChrisW。我在不到一年前开始使用WPF,所以我不太习惯所有的可能性。请原谅我在这里缺乏知识,但我看不出你的问题是什么(模拟视图模型?)与XAML格式的样本数据有关。@Sentry不必担心。至于说你有实际的假数据来提供我假设你在运行时在XAML中的绑定?这些假数据必须存在于某个地方。@ChrisW。我的假数据就是:假。随机字符串,随机数。Blend可以为我的大多数视图模型创建它们,但是n有一个字典,Blend不会生成它。即使对于
字典
<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>
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();
    }        
}
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;
}
<gnrc:Dictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:sys="clr-namespace:System;assembly=mscorlib"
                 xmlns:gnrc="clr-namespace:System.Collections.Generic;assembly=mscorlib"
                 xmlns:om="clr-namespace:System.Collections.ObjectModel;assembly=System"
                 x:TypeArguments="sys:DateTime,om:ObservableCollection(x:String)">
    <om:ObservableCollection x:TypeArguments="x:String">
        <x:Key>
            <sys:DateTime>2017/12/31</sys:DateTime>
        </x:Key>
        <x:String>The last day of the year.</x:String>
        <x:String>Party with friends.</x:String>
    </om:ObservableCollection>
    <om:ObservableCollection x:TypeArguments="x:String">
        <x:Key>
            <sys:DateTime>2018/1/1</sys:DateTime>
        </x:Key>
        <x:String>Happy new year.</x:String>
        <x:String>Too much booze.</x:String>
    </om:ObservableCollection>
    <om:ObservableCollection x:TypeArguments="x:String">
        <x:Key>
            <sys:DateTime>2018/1/10</sys:DateTime>
        </x:Key>
        <x:String>Just another year.</x:String>
        <x:String>Not much difference.</x:String>
    </om:ObservableCollection>
</gnrc:Dictionary>
public class InstanceFromLooseXamlExtension : MarkupExtension
{
    public Uri Source { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        if (Source == null)
        {
            throw new ArgumentNullException(nameof(Source));
        }

        Uri source;
        if (Source.IsAbsoluteUri)
        {
            source = Source;
        }
        else
        {
            var iuc = serviceProvider?.GetService(typeof(IUriContext)) as IUriContext;
            if (iuc == null)
            {
                throw new ArgumentException("Bad service contexts.", nameof(serviceProvider));
            }

            source = new Uri(iuc.BaseUri, Source);
        }

        WebResponse response;
        if (source.IsFile)
        {
            response = WebRequest.Create(source.GetLeftPart(UriPartial.Path)).GetResponse();
        }
        else if(string.Compare(source.Scheme, PackUriHelper.UriSchemePack, StringComparison.Ordinal) == 0)
        {
            var iwrc = new PackWebRequestFactory() as IWebRequestCreate;
            response = iwrc.Create(source).GetResponse();
        }
        else
        {
            throw new ArgumentException("Unsupported Source.", nameof(Source));
        }

        object result;
        try
        {
            result = XamlReader.Load(response.GetResponseStream());
        }
        finally
        {
            response.Close();
        }

        return result;
    }
}
<Window x:Class="WpfApp.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:WpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <ListBox ItemsSource="{local:InstanceFromLooseXaml Source=/Data.xaml}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Expander Header="{Binding Key}">
                    <ListBox ItemsSource="{Binding Value}"/>
                </Expander>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Window>