我有一个带有视图模型的UserControl:
public partial class FarmLogPageView : UserControl
{
public FarmLogPage? ViewModel
{
get => (FarmLogPage)DataContext;
set => DataContext = value;
}
public FarmLogPageView()
{
InitializeComponent();
var dir = "D:\\_\\FolderQuantTests\\FarmLogPages\\";
var files = Directory.EnumerateFiles(dir);
string json = File.ReadAllText(files.First());
ViewModel = JsonSerializer.Deserialize<FarmLogPage>(json);
}
}
字符串
我还确保在xaml端定义了类型:
d:DataContext="{d:DesignInstance Type=diskManagerCore:FarmLogPage,
IsDesignTimeCreatable=False}"
型
但是我的构造函数文本是空的(根据构造函数后面的代码,我应该有一些东西)。我确保了视图模型的无参数构造函数。
就好像我的构造函数后面的代码被忽略了。
如何让视图模型的设计示例基于文件中的序列化数据?
PS此测试通过(即,我有正确的类示例内容):
[TestMethod]
public void DeserializeTest()
{
var dir = "D:\\_\\FolderQuantTests\\FarmLogPages\\";
var file = Directory.EnumerateFiles(dir).First(o => o.Contains("Drive"));
Console.WriteLine($"using {file}");
string json = File.ReadAllText(file);
var sut = JsonSerializer.Deserialize<FarmLogPage>(json);
Assert.IsNotNull(sut);
Assert.IsTrue(sut.ContentMap.Count > 0);
Assert.AreEqual(28, sut.ContentMap.Count);
//sut.ContentMap.ToCW();
}
型
完整视图:
<UserControl x:Class="DiskManagerUI.FarmLogPageView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DiskManagerUI"
xmlns:diskManagerCore="clr-namespace:DiskManagerCore;assembly=DiskManagerCore"
d:DataContext="{d:DesignInstance Type=diskManagerCore:FarmLogPage, IsDesignTimeCreatable=False}"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="27"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<StackPanel.Resources>
</StackPanel.Resources>
<TextBlock Text="{Binding PageType, FallbackValue=PageType}" Margin="0 0 10 0"/>
<TextBlock Text="{Binding ErrorRate, FallbackValue=errorrate}" Margin="0 0 10 0"/>
<TextBlock Text="{Binding Name, FallbackValue=name}" Margin="0 0 10 0"/>
</StackPanel>
<ListBox Grid.Row="1" ItemsSource="{Binding ContentMap}" BorderThickness="0" Background="AliceBlue">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1">
<StackPanel>
<TextBlock Text="{Binding Key}" />
<TextBlock Text="{Binding Value}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
型
验证码:
using DiskManagerCore;
using System.IO;
using System.Text.Json;
using System.Windows.Controls;
namespace DiskManagerUI
{
/// <summary>
/// Interaction logic for FarmLogPageView.xaml
/// </summary>
public partial class FarmLogPageView : UserControl
{
public FarmLogPage? ViewModel
{
get => (FarmLogPage)DataContext;
set => DataContext = value;
}
public FarmLogPageView()
{
InitializeComponent();
var dir = "D:\\_\\FolderQuantTests\\FarmLogPages\\";
var file = Directory.EnumerateFiles(dir).First(o => o.Contains("Drive"));
string json = File.ReadAllText(file);
ViewModel = JsonSerializer.Deserialize<FarmLogPage>(json);
}
}
}
型
视图模型:
[Serializable]
public class FarmLogPage
{
protected string[] Content { get; set; }
public string Name { get; set; }
public FarmPageType PageType { get; set; }
public bool HasContentMap { get; set; }
public Dictionary<string, string> ContentMap { get; set; } = new();
/// <summary>
/// For tests or design
/// </summary>
public FarmLogPage()
{
}
}
型
1条答案
按热度按时间cclgggtu1#
你不应该从构造函数中对文件进行示例化或者遍历文件系统。这会使类型的示例化成为一个阻塞操作。这是一种代码味道。
此外,将代码移到构造函数之外还允许您使用异步
JsonSerializer
API(JsonSerializer.DeserializeAsync
),这将进一步提高性能。然后最好调用JsonSerializer.Deserialize
的重载,允许传入Stream
(或文件路径)。让JsonSerializer
处理文件以使其能够提高性能。File.ReadAllText
不是最佳选择。通常,我更喜欢使用StreamReader
来读取文件,因为它允许异步文件访问(例如StreamReader.ReadToEndAsync
)。字符串
最后,要解决这个问题,你必须正确配置设计时数据。你必须将
IsDesignTimeCreatable
设置为true
。否则WPF将创建一个假类型,而不会示例化原始类型(因此永远不会调用构造函数)。此假类型只是为了向XAML设计器提供类型的大纲,以便Intellisense可以提出代码建议。
型
在再次检查了你的代码之后,我意识到我忽略了导致你的问题的设计缺陷。我假设了一个不同的(更干净的)设计,然后你实际上有。
设计器视图中显示的控件的构造函数不是由设计调用的。在当前方案中,XAML引擎通过调用
FarmLogPage
的默认构造函数来创建FarmLogPageView
的示例。但由于在创建设计时数据时绕过了FarmLogPageView
的构造函数,就会得到一个空示例(仅使用默认值初始化)在这一点上,您可以使用来自默认构造函数的设计时示例数据初始化FarmLogPage
。我想用测试或设计器代码污染我的产品类。您可以通过改进类设计来解决问题。
因为你将属性命名为
ViewModel
,所以我假设你正在实现MVVM模式。该模式规定视图不能引用模型或包含模型责任。阅读和写数据 * 是 * 模范业务。
遵循设计MVVM规则,您必须将文件处理移到Model。即使您没有实现MVVM,也应该遵循建议的设计。关键是对
FarmLogPageView
隐藏FarmLogPage
的内部。这允许FarmLogPageView
仅通过调用构造函数来创建示例。这将改善您的整体类设计,因为它清理了责任。FarmLogPageView
必须不知道数据的确切来源。否则,它可以自己存储这些数据。我们必须将检索数据的责任转移到存储/处理数据的对象。作为新类设计的一个副作用,我们可以创建一个简单的视图模型示例并推迟初始化,这允许使用不允许从构造函数执行的异步代码。
简单地修改你的类设计如下:不要让控件通过初始化来初始化数据上下文值。让控件告诉数据上下文类来初始化自己,例如,但是这些细节应该被隐藏。数据上下文类将调用Model类来获取数据(来源未知-只有Model类知道数据源(JSON文件)以及如何对数据进行格式化)。
因为在你的例子中,你需要将一个完整的对象进行封装,所以你必须使用composition并将这些数据作为
FarmLogPage
类型的属性公开。换句话说,你必须将FarmLogPage的wrap
转换为一个新的类型(例如MainViewModel
):FarmLogPage.cs
型
MainViewModel.cs
型
DataProvider.cs
模型类。
型
FarmLogPageView.cs
型
在修复设计之后。您必须创建一个专用的数据上下文设计数据类型。这是必要的,因为您的数据需要在运行时创建。它还没有完成创建
FarmLogPage
示例。示例必须通过示例化创建。当你定义
型
则XAML引擎不知道必须通过示例化创建示例。它只是调用默认构造函数(导致一个空示例)。
即使
FarmLogPageView
构造函数已被调用,XAML设计器也会忽略由非线性化创建的FarmLogPage
示例。此示例仅通过 * 运行时数据上下文 * 可用。但您已指示设计器使用 * 设计时数据上下文 *:两个不同的示例,两个不同的数据上下文。一个已正确初始化,另一个为空。现在,为了能够指定一个设计时数据对象,我们必须设计一个数据类型
FarmLogPageDesignData
,默认情况下使用设计时示例数据初始化:FarmLogPageDesignData.cs
型
然后更新
DesignInstance
,以便XAML引擎可以创建FarmLogPageDesignData
的设计时示例:型
如果你不想让设计时数据污染你的生产类型,你可以避免
FarmLogPageDesignData
类型,而是将设计时示例数据的初始化添加到默认构造函数中(我不推荐这样做)。另一种方法是不初始化整个示例,而是从JSON中读取初始数据并使用它初始化
FarmLogPage
。由于示例化涉及文件操作的特殊情况,我建议实现专用的设计时数据类(
FarmLogPageDesignData
)。