我有一个WPF应用程序,我正在使用Blend来设置样式。
我的一个视图模型是:
public Dictionary<DateTime, ObservableCollection<MyViewModel>> TimesAndEvents
但是当我尝试在Expression Blend中创建一些示例数据时,它根本不会为此属性创建XAML。
你能在XAML中创建这样的数据类型吗?非设计时间支持正在削弱我的工作效率。
答案 0 :(得分:0)
我已经在我的定位器中创建了我的Viewmodel的设计时实例的路线,我在上面提到了@ChrisW:
d:DataContext="{Binding Source={StaticResource Locator}, Path=DesignTimeVM}"
所以我可以使用一些硬编码的值来填充我的列表,组合框等。使样式更容易。
我使用MVVM Light,所以在我的ViewModel的构造函数中,我使用这样的模式:
if(IsInDesignMode)
{
ListUsers = new List<User>();
.
.
.
}
代码只会在设计时执行,您将Xaml UI绑定到实际数据。
答案 1 :(得分:0)
关于您的上一个问题:,遗憾的是,您无法轻松在WPF中实例化词典。我相信this answer很好地解释了这一部分。这本书WPF 4.5 Unleashed提供了链接答案所说明内容的一个很好的总结:
此限制的常见解决方法(无法实例化 WPF的XAML版本中的字典是派生非泛型的 来自通用类的类只是因为它可以从XAML引用...
但即便如此,在我看来,在xaml中实例化字典再次是一个痛苦的过程。此外,Blend不知道如何创建该类型的样本数据。
关于如何获得设计时支持的隐含问题:有几种方法可以在WPF中实现设计时数据,但此时我在复杂场景中的首选方法是创建自定义DataSourceProvider。给予应有的信用:我从this article得到了这个想法(比这个问题还要老)。
创建一个实现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>
注意:&#39; d&#39;在<d:Window.DataContext>
中很重要,因为它告诉Blend和编译器该特定元素是设计时间的,并且在编译文件时应该忽略它。
完成后,我的设计视图现在如下所示:
我从5个类开始(2个是从WPF项目模板生成的,我建议使用它):
public class MyViewModel
{
public string EventName { get; set; }
}
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类并更改了它。基本上,现在它要求一个MainWindowViewModel并将其设置为DataContext。
public partial class MainWindow : Window
{
public MainWindow(MainWindowViewModel viewModel)
{
DataContext = viewModel;
InitializeComponent();
}
}
请注意解决方案中缺少设计数据上下文。
<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>
首先,从xaml端移除StartupUri="MainWindow.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的初始化方法(我将在底部包含我的实现),你应该会看到如下所示的屏幕构建并运行您的WPF应用程序:
问题是设计视图中没有显示任何内容。
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;
}
答案 2 :(得分:0)
由于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>
但它不是像Blend或Visual Studio这样的设计师的支持。如果将它放入与设计器关联的xaml中,您将收到许多错误。要解决这个问题,我们需要一个标记扩展来通过使用XamlReader.Load方法从Data.xaml提供值。
<强> InstanceFromLooseXamlExtension.cs 强>
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;
}
}
此标记扩展具有Uri类型Source属性,以允许用户指定要加载的xaml文件。最后,像这样使用标记扩展。
<强> MainWindow.xaml 强>
<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放在应用程序文件夹中,因此&#39; Source = / Data.xaml&#39;会好的。每次设计器重新加载(重建将确保它)时,将应用松散xaml中的内容。结果应该看起来像
松散的xaml几乎可以包含所有内容,例如ResourceDictionary或UiElements。但Blend或Visual Studio都不会为您正确检查它。最后,希望这足以得到答案。