WPF C# - 使用具有代码隐藏属性的动态设备保存画布(变量)

时间:2018-01-30 02:24:54

标签: c# wpf serialization dynamicobject

我们已经搜索了所有堆栈溢出和类似网站,以寻找适用于我们的应用程序的内容,但是一切都只能让我们在那里。

我们有一个应用程序允许用户将设备拖放到放置画布上。在设备被丢弃时,他们的路由器属性"已创建,您可以更改其姓名,地址,添加备注。

我们还让用户连接设备之间的线路。 (我们还将创建的路由器属性添加到可观察集合中)。

我们已尝试xmlserialization,它让我们保存设备的物理方面,但在加载xml文件时,它不再有任何已保存设备的地址,备注等,并且不会# 39; t允许添加连接或转到其属性。

我意识到我们需要以某种方式序列化后面的代码,然后在反序列化时将其添加回每个设备,但我们似乎无法找到一种方法来序列化可观察的路由器属性集合。 / p>

有没有人对最简单的方法有任何建议,允许我们将画布,子项及其代码保存在属性后面?我附上图片作为参考,路由器属性类,我很乐意在需要时包含任何代码。我们非常感谢任何帮助。

温馨的问候, 泰勒

例如

enter image description here

enter image description here

public class RouterProperties : INotifyPropertyChanged
{
    private ArrayList incomingConnections = new ArrayList();
    private ArrayList outgoingCnnections = new ArrayList();
    private bool isLocked = true;
    private bool isSelected = false;
    private string deviceName = "Router";
    private string hostName = "Host name";
    private string routerIP = "192.168.0.1";
    private string note = "Notes";
    private string status = "Yellow";
    private BitmapImage icon;

    // getters and setters removed for brevity

    public ArrayList IncomingConnections
    ...

    public ArrayList OutgoingCnnections
    ...

    public bool IsLocked
    ...

    public bool IsSelected
    ...

    public string DeviceName
    ...

    public string HostName
    ...

    public string RouterIP
    ...

    public string Note
    ...

    public string Status
    ...

    public BitmapImage Icon
    ...

MainWindow类

 public ObservableCollection<RouterProperties> devices = new ObservableCollection<RouterProperties>();

编辑保存xaml的代码

// De-Serialize XML to UIElement using a given filename.
        public static UIElement DeSerializeXAML(string filename)
        {
            // Load XAML from file. Use 'using' so objects are disposed of properly.
            using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Open, System.IO.FileAccess.Read))
            {
                return System.Windows.Markup.XamlReader.Load(fs) as UIElement;
            }
        }

        // Serializes any UIElement object to XAML using a given filename.
        public static void SerializeToXAML(UIElement element, string filename)
        {
            // Use XamlWriter object to serialize element
            string strXAML = System.Windows.Markup.XamlWriter.Save(element);

            // Write XAML to file. Use 'using' so objects are disposed of properly.
            using (System.IO.FileStream fs = System.IO.File.Create(filename))
            {
                using (System.IO.StreamWriter streamwriter = new System.IO.StreamWriter(fs))
                {
                    streamwriter.Write(strXAML);
                }
            }
        }

        private void menuSave_Click(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
            dlg.FileName = "UIElement File"; // Default file name
            dlg.DefaultExt = ".xaml"; // Default file extension
            dlg.Filter = "Xaml File (.xaml)|*.xaml"; // Filter files by extension

            // Show save file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process save file dialog box results
            if (result == true)
            {
                // Save document
                string filename = dlg.FileName;
                SerializeToXAML(canvasMain, filename);
            }
        }

        private void menuLoad_Click(object sender, RoutedEventArgs e)
        {
            Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.DefaultExt = ".xaml"; // Default file extension
            dlg.Filter = "Xaml File (.xaml)|*.xaml"; // Filter files by extension

            // Show open file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process open file dialog box results
            if (result == true)
            {
                string filename = dlg.FileName;
                Canvas canvas = DeSerializeXAML(filename) as Canvas;

                // Add all child elements (lines, rectangles etc) to canvas
                while (canvas.Children.Count > 0)
                {
                    UIElement obj = canvas.Children[0]; // Get next child
                    canvas.Children.Remove(obj); // Have to disconnect it from result before we can add it
                    canvasMain.Children.Add(obj); // Add to canvas
                }
            }
        }

1 个答案:

答案 0 :(得分:0)

不幸的是,我没有看到你当前的方法的解决方案,或者至少没有想到的。

以下是问题的基本原理

Serialization Limitations of XamlWriter.Save

运行时,非设计时表示

  

通过调用Save来序列化的基本理念是   结果将是序列化对象的表示,at   运行。原始XAML文件的许多设计时属性可能   在XAML加载时已经优化或丢失   内存中的对象,并且在调用“保存到”时不会保留   连载。序列化结果是一个有效的表示   构造应用程序的逻辑树,但不一定是   产生它的原始XAML。这些问题极其重要   难以使用保存序列化作为广泛的XAML的一部分   设计表面。

取消引用扩展引用

  

各种标记扩展格式对象的公共引用,   如StaticResource或Binding,将被取消引用   序列化过程。这些已经在当时被取消引用   内存中的对象是由应用程序运行时创建的   保存逻辑不会重新访问原始XAML以恢复此类   对序列化输出的引用。这可能冻结任何   数据绑定或资源获取的值是最后使用的值   运行时表示,只有有限或间接的能力   区分这样的值与本地设置的任何其他值。图像是   也被序列化为对象的对象引用,因为它们存在于   项目,而不是原始的来源参考,失去了什么   文件名或URI最初被引用。甚至宣布资源   在同一页面内看到序列化到它们的位置   被引用,而不是被保存为资源的关键字   集合。

我的第一个解决方案是为每个控件和路由器属性分配一个GUID或id。然而,似乎这不起作用,XamlWriter.Save只是不保留那种性质的绑定或事物。

但是我认为你需要从ViewModel第一种方法

来攻击它

也就是说,您的ViewModel需要保留可视对象的所有实现属性,位置以及可视化重建画布所需的任何内容。在创建每个可视路由器时,您需要将其所有相关状态保留在某处

即使实现细节与路由器ViewModel是分开的,您也可以对它们进行序列化,并使用某种ID在运行时重新链接它们。

虽然我的Spidey感觉告诉我你应该重新设计架构,将所有相关内容放在一个高级ViewModel中,尽管这完全取决于应用程序的架构是什么。

也许你可以拥有这样的结构

[Serializable] 
public class RouterAndState
{
    public RouterProperties {get;set;}
    Public RouterVisualState  {get;set;}
}

[Serializable] 
public class RouterVisualState  
{
    // its location (x,y) and anything else it needs to be recreated
}

如果要将路由器属性保存到数据库,路由器实体实际上并不关心画布的可视布局是什么,并且它不是真正应该保存的东西,但也许它可以保存在相关的表中有一个映射到使用的路由器和一个映射到它的布局,即RouterMap表,带有RouterProperties的外键和视觉布局配置

另一种方法是从routerProperties生成可视状态并自动生成布局,这很简洁,但是你需要实现更多的逻辑来自动配置加载时的布局。

但是,如果这是一个相当简单的事情,只需使用上述内容将其全部序列化为文件并使用它完成

我希望这会有所帮助