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

发布于 2024-11-16 22:01:55 字数 288 浏览 1 评论 0原文

我有一个 WPF 应用程序,我正在使用 Blend 来设置样式。

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

public Dictionary<DateTime, ObservableCollection<MyViewModel>> TimesAndEvents

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

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

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

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

乞讨 2024-11-23 22:01:55

关于您的最后一个问题:不幸的是,您无法轻松在 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”很重要,因为它告诉 Blend 和编译器该特定元素用于设计时,在编译文件时应忽略它。

完成此操作后,我的设计视图现在如下所示:

包含示例数据的 Blend 设计视图的图像。


设置问题

我从 5 个类开始(其中 2 个是从 WPF 项目模板生成的,我将其推荐用于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。

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

首先,从 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 的 Initialize 方法(我将在底部包含我的实现),那么当您构建并运行您的应用程序时,您应该会看到如下所示的屏幕WPF 应用程序:

您的屏幕应该是什么样子的图像。

问题又是什么?

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

图片在 Blend 的设计视图中描绘了一个空白屏幕。


我的 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;
}

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:

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

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.

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


The DataSourceProvider Solution

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>

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:

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


Setting up the problem

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

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

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

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

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:

An image of what your screen should look like.

What was the problem again?

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

An image depicting a blank screen in Blend's design view.


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;
}
清欢 2024-11-23 22:01:55

我已经走上了在定位器中创建视图模型的设计时实例的路线,我将其引用为上面建议的@ChrisW:

d:DataContext="{Binding Source={StaticResource Locator}, Path=DesignTimeVM}"

这样我就可以使用一些硬编码值来填充我的列表、组合框等。那样容易多了。

我使用 MVVM Light,因此在 ViewModel 的构造函数中我使用这样的模式:

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

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

Any more I've gone the route of creating a Design Time Instance of my Viewmodel in my Locator that I reference as @ChrisW suggested above:

d:DataContext="{Binding Source={StaticResource Locator}, Path=DesignTimeVM}"

So I can have some hard-coded values to populate my lists, comboboxes, etc. Makes styling everything that much easier.

I use MVVM Light and so in my ViewModel's constructor I use a pattern like this:

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

The code will only execute at Design Time, and you will have your Xaml UI bound to actual data.

旧瑾黎汐 2024-11-23 22:01:55

由于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 放在应用程序文件夹中,因此“Source=/Data.xaml”就可以了。每次设计器重新加载(重建将确保这一点)时,将应用松散的 xaml 中的内容。结果应该类似于

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

Since Xaml 2009 support generic types, is possible write a loose xaml(can not be compiled in wpf project) like this to represent a dictionary.

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>

But it is not support by designers like Blend or Visual Studio. If you put it into a xaml that associated with a designer, you will get dozens of errors. To solve this, we need a markup extension to provide value from Data.xaml by using XamlReader.Load method.

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

This markup extension has a Uri type Source property to let user specify which xaml file to load. Then finally, use the markup extension like this.

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>

In this case, I place Data.xaml in application folder, so 'Source=/Data.xaml' will be OK. Every time the designer reloaded(a rebuild will ensure it), the contents in loose xaml will be applied. The result should look like

The loose xaml can contain almost everything, like a ResourceDictionary or something with UiElements. But both Blend or Visual Studio will not check it correctly for you. In the end, hope this is enough for an answer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文