You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF中Canvas多自定义UserControl序列化与反序列化问题求助

WPF Canvas 序列化/反序列化问题排查与修复

问题概述

尝试对WPF Canvas中的自定义UserControl(蓝色圆形、红色方形)进行序列化和反序列化,当前能正常保存/加载文件,但存在两个核心问题:

  • 加载时仅生成红色方形,圆形完全不显示
  • 生成的方形全部位于Canvas的(0,0)坐标,但形状数量与保存时一致
  • 保存后的XML仅包含空的<shaypeh />节点,未记录任何坐标和形状类型信息

相关代码

序列化存储类

[Serializable()]
public class shaypeh
{
    private double x, y;
    private bool circleOrSquare; 

    public shaypeh()
    {

    }

    public shaypeh(double x, double y, bool circleOrSquare)
    {
        this.x = x;
        this.y = y;
        this.circleOrSquare = circleOrSquare;
    }

    public bool getCircleOrSquare()
    {
        return circleOrSquare;
    }

    public double getX()
    {
        return x;
    }

    public double getY()
    {
        return y;
    }
}

保存Canvas代码

double top;
double left;
List<shaypeh> shaypehs = new List<shaypeh>();
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "Text file (*.txt)|*.txt|XML (*.xml)|*.xml";
if (sfd.ShowDialog() != true)
{
    return; 
}

foreach(object obj in pictureBoard.Children)
{
    if (obj is Circle)
    {
        UIElement s = (Circle)obj;
        top = Canvas.GetTop(s); 
        left = Canvas.GetLeft(s);
        shaypehs.Add(new shaypeh(left, top, true));
    }
    if (obj is Rectangle)
    {
         UIElement s = (Rectangle)obj;
         top = Canvas.GetTop(s);
         left = Canvas.GetLeft(s);
         shaypehs.Add(new shaypeh(left, top, true));
     }
}
XmlSerializer serializer = new XmlSerializer(typeof(List<shaypeh>));
using (FileStream stream = File.Create(sfd.FileName))
{
    serializer.Serialize(stream, shaypehs);
}

保存后的XML示例(添加2个圆形+1个方形后)

<?xml version="1.0"?>
<ArrayOfShaypeh xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <shaypeh />
  <shaypeh />
  <shaypeh />
</ArrayOfShaypeh>

加载文件代码

OpenFileDialog ofd = new OpenFileDialog();
ofd.DefaultExt = ".xml"; 
ofd.Filter = "Text file (*.txt)|*.txt|XML (*.xml)|*.xml";
if (ofd.ShowDialog()!= true)
{
    return; 
}

pictureBoard.Children.Clear();
XmlSerializer serializer = new XmlSerializer (typeof(List<shaypeh>));
using (FileStream stream = File.Open(ofd.FileName, FileMode.Open))
{
    List<shaypeh> shaypehs = (List<shaypeh>)serializer.Deserialize(stream);
    foreach(shaypeh shape in shaypehs)
    {
        if (shape.getCircleOrSquare() == true)
        {
            Circle c = new Circle();
            Canvas.SetTop(c, shape.getY());
            Canvas.SetLeft(c, shape.getX());
            c.PreviewMouseDown += c_PreviewMouseDown; 
            pictureBoard.Children.Add(c);
        }
        if (shape.getCircleOrSquare() == false)
        {
            Rectangle r = new Rectangle();
            Canvas.SetTop(r, shape.getY());
            Canvas.SetLeft(r, shape.getX());
            r.PreviewMouseDown += c_PreviewMouseDown;
            pictureBoard.Children.Add(r);
         }
    }
}

问题根源与修复方案

1. XmlSerializer无法序列化私有字段

XmlSerializer默认仅序列化公共属性,shaypeh类中的x、y、circleOrSquare均为私有字段,且无对应公共属性,导致序列化时无法写入任何数据,最终XML仅生成空节点。

修复:
将私有字段改为公共属性(符合C#编码规范):

[Serializable()]
public class shaypeh
{
    // 公共属性供XmlSerializer序列化
    public double X { get; set; }
    public double Y { get; set; }
    public bool IsCircle { get; set; }

    public shaypeh()
    {
    }

    public shaypeh(double x, double y, bool isCircle)
    {
        X = x;
        Y = y;
        IsCircle = isCircle;
    }
}

2. 保存代码中形状类型标记错误+坐标空值处理缺失

  • 处理Rectangle时,传入的类型标记参数为true(与Circle的标记一致),导致所有形状都被标记为圆形;
  • Canvas.GetTop/GetLeft在未显式设置坐标时会返回null,直接赋值给double会引发隐式转换错误,导致坐标数据丢失。

修复保存代码:

List<shaypeh> shaypehs = new List<shaypeh>();
SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "Text file (*.txt)|*.txt|XML (*.xml)|*.xml";
if (sfd.ShowDialog() != true)
{
    return; 
}

foreach(object obj in pictureBoard.Children)
{
    if (obj is Circle circle)
    {
        // 处理null情况,默认坐标为0
        double top = Canvas.GetTop(circle) ?? 0;
        double left = Canvas.GetLeft(circle) ?? 0;
        shaypehs.Add(new shaypeh(left, top, true));
    }
    else if (obj is Rectangle rect)
    {
        double top = Canvas.GetTop(rect) ?? 0;
        double left = Canvas.GetLeft(rect) ?? 0;
        // Rectangle标记为false
        shaypehs.Add(new shaypeh(left, top, false));
    }
}

XmlSerializer serializer = new XmlSerializer(typeof(List<shaypeh>));
using (FileStream stream = File.Create(sfd.FileName))
{
    serializer.Serialize(stream, shaypehs);
}

3. 反序列化代码适配属性变更

更新反序列化代码,使用新的公共属性:

OpenFileDialog ofd = new OpenFileDialog();
ofd.DefaultExt = ".xml"; 
ofd.Filter = "Text file (*.txt)|*.txt|XML (*.xml)|*.xml";
if (ofd.ShowDialog()!= true)
{
    return; 
}

pictureBoard.Children.Clear();
XmlSerializer serializer = new XmlSerializer(typeof(List<shaypeh>));
using (FileStream stream = File.Open(ofd.FileName, FileMode.Open))
{
    List<shaypeh> shaypehs = (List<shaypeh>)serializer.Deserialize(stream);
    foreach(shaypeh shape in shaypehs)
    {
        if (shape.IsCircle)
        {
            Circle c = new Circle();
            Canvas.SetTop(c, shape.Y);
            Canvas.SetLeft(c, shape.X);
            c.PreviewMouseDown += c_PreviewMouseDown; 
            pictureBoard.Children.Add(c);
        }
        else
        {
            Rectangle r = new Rectangle();
            Canvas.SetTop(r, shape.Y);
            Canvas.SetLeft(r, shape.X);
            r.PreviewMouseDown += c_PreviewMouseDown;
            pictureBoard.Children.Add(r);
         }
    }
}

内容的提问来源于stack exchange,提问作者AJMullen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 15:44:54