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

标签 wpf xaml expression-blend

我有一个 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 现在如下所示:

An image of Blend's design view with sample data in it.

设置问题

我从 5 个类开始(2 个是从 WPF 项目模板生成的,我建议为此使用):
  • MyViewModel.cs
  • MainWindowViewModel.cs
  • MainWindow.xaml
  • App.xaml

  • 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 应用程序时,您应该会看到如下所示的屏幕:

    An image of what your screen should look like.

    又是什么问题?

    问题是设计 View 中没有显示任何内容。

    An image depicting a blank screen in Blend's design 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/

    相关文章:

    c# - 使用 MVVM 模式(特别是使用 MVVM Light),如何实现不仅仅是基元的模型?

    c# - 在 ReactiveUI 中取消/忽略 ReactiveAsyncCommand 的结果

    c# - 生成带有图像的辅助图 block 不立即显示图像

    c# - 当对象的属性更改时,WPF 在父级上调用 updatesourcetrigger xaml

    wpf - WPF XAML中Border中每一边的颜色不同?

    .net - Silverlight 与表达混合

    javascript - C# - 当网页上的 javascript 调用 window.close() 时关闭 System.Windows.Forms.WebBrowser

    c# - 在数据网格中显示对象列表

    xaml - 如何在 Visual Studio 2012 设计 View 中在页面编辑模式和模板编辑模式之间切换?

    c# - 处理 wpf 应用程序中任何未处理的点击事件