I found a working code serialization of controls, but it has not one function: there is a controls, the controls has an event, after saving it does not saves. How can I solve this problem?
Following is the code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
namespace serial
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// Save control
int i = 0;
foreach (XElement element in
panel1.Controls
.OfType<Control>()
.Select(ToXml))
{
element.Save("Control" + i++ + ".xml");
}
}
private static XElement ToXml(Control control)
{
Type controlType = control.GetType();
var root = new XElement("Root",
new XAttribute("Type", controlType.AssemblyQualifiedName));
PropertyInfo[] fieldInfos = controlType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo fieldInfo in fieldInfos)
{
if (fieldInfo.CanRead && fieldInfo.CanWrite &&
fieldInfo.Name != "Font" && fieldInfo.Name != "Handle")
{
object content = fieldInfo.GetValue(control, null);
if (content != null && content.GetType().IsSerializable)
{
var serializer = new DataContractSerializer(content.GetType());
var str = new StringBuilder();
using (XmlWriter stream = XmlWriter.Create(str))
{
serializer.WriteObject(stream, content);
}
XElement data = XElement.Parse(str.ToString());
var element = new XElement("Property",
new XAttribute("Name", fieldInfo.Name),
new XAttribute("Type", fieldInfo.PropertyType.AssemblyQualifiedName)
, data);
root.Add(element);
}
}
}
return root;
}
private void button2_Click(object sender, EventArgs e)
{
panel1.Controls.Clear();
}
private void button3_Click(object sender, EventArgs e)
{
// Clear panel
panel1.Controls.Clear();
// Load control
IEnumerable<string> newControlsNames = Directory.EnumerateFiles(Environment.CurrentDirectory, "*.xml");
Control[] newControls = newControlsNames
.Select(XElement.Load)
.Select(GetControl)
.Select(c => c as Control)
.ToArray();
// Add control on panel
panel1.Controls.AddRange(newControls);
}
// get control from xml
private static object GetControl(XElement xml)
{
Type controlType = Type.GetType(xml.Attribute("Type").Value);
object control = Activator.CreateInstance(controlType);
IEnumerable<XElement> elements = xml.Elements("Property");
foreach (XElement element in elements)
{
string name = element.Attribute("Name").Value;
Type type = Type.GetType(element.Attribute("Type").Value);
XNode first = element.Nodes().First();
var serializer = new DataContractSerializer(type);
object value;
using (var stream = new MemoryStream(Encoding.Default.GetBytes(first.ToString())))
{
value = serializer.ReadObject(stream);
}
if (value != null)
{
PropertyInfo fieldInfo = controlType.GetProperty(name);
fieldInfo.SetValue(control, value, null);
}
}
return control;
}
private void button4_Click(object sender, EventArgs e)
{
MessageBox.Show("1");
}
private void button5_MouseEnter(object sender, EventArgs e)
{
MessageBox.Show("2");
}
}
}
edit:
Here's my project! http://www.fileserve.com/file/t7kUwWM
The problem with evets is, the list of delegates are not accesible from "outside" the class.
There is workaround - must use reflection and search for private fields typed as delegate (I mean custom delegate not only System.Delegate) and than serialize references to target object and target method (via System.Reflection.MethodInfo).
However this solution is very proprietary and does not guarantee the correct behaviour in all cases, because depends on private state of object.
Related
I'm trying to convert a treeview to a byte array and then back again. So far when the form loads, it will load the structure of my documents. Then as far as I know, it will convert it to a byte array and back but I'm not sure how to convert the byte array back to the tree view.
Here is my code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
string filepath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
ListDirectory(treeView1, filepath);
}
private static void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
var stack = new Stack<TreeNode>();
var rootDirectory = new DirectoryInfo(path);
var node = new TreeNode(rootDirectory.Name) { Tag = rootDirectory };
stack.Push(node);
while (stack.Count > 0)
{
var currentNode = stack.Pop();
var directoryInfo = (DirectoryInfo)currentNode.Tag;
foreach (var directory in directoryInfo.GetDirectories())
{
var childDirectoryNode = new TreeNode(directory.Name) { Tag = directory };
currentNode.Nodes.Add(childDirectoryNode);
stack.Push(childDirectoryNode);
}
foreach (var file in directoryInfo.GetFiles())
currentNode.Nodes.Add(new TreeNode(file.Name));
}
treeView.Nodes.Add(node);
}
private Byte[] SerilizeQueryFilters()
{
BinaryFormatter bf = new BinaryFormatter();
List<TreeNode> list = new List<TreeNode>();
foreach(TreeNode node in treeView1.Nodes)
{
list.Add(node);
}
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, list);
return ms.GetBuffer();
}
}
private void DeSerilizeQueryFilters(byte[] items)
{
BinaryFormatter bf = new BinaryFormatter();
try
{
using (MemoryStream ms = new MemoryStream())
{
ms.Write(items, 0, items.Length);
ms.Position = 0;
_list = bf.Deserialize(ms) as List<TreeNode>;
treeView2.Nodes.AddRange(_list.ToArray());
}
}
catch(Exception e)
{
Console.WriteLine(e.StackTrace);
}
}
private void button1_Click(object sender, EventArgs e)
{
byte[] data = SerilizeQueryFilters();
DeSerilizeQueryFilters(data);
}
}
So the bit that's throwing an error at the moment is
_list = bf.Deserialize(ms) as List<TreeNode>;
and I get this error:
System.Runtime.Serialization.SerializationException
Does anyone have any ideas?
The solution was easy using the recommendation of #Fildor I stop using BinaryFormatter and now I use JSON and using the recommendation of #madreflection
I create a custom class to save the data of the TreeNode.
Custom TreeNode Class
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TreeViewStuff
{
public class Node
{
public int id;
public string text = "";
public int parentId = 0;
public bool cheked = false;
}
}
Deserialize/Serialize TreeView Class
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TreeViewStuff
{
public class TreeviewPersist
{
public static string strJson;
private static TreeView treeView_;
static public string ToJson(TreeView treeView)
{
List<Node> nodes = new List<Node>();
foreach(TreeNode node in treeView.Nodes)
{
SerializeTree(nodes, node);
}
nodes.RemoveAt(0);
return JsonConvert.SerializeObject(nodes);
}
public delegate void FunctionDelegate();
public static void FromJson(string strJson_, TreeView treeView)
{
strJson = strJson_;
treeView_ = treeView;
treeView.BeginInvoke(new FunctionDelegate(FromJson));
}
private static void FromJson()
{
treeView_.Nodes.Clear();
List<Node> nodes = JsonConvert.DeserializeObject<List<Node>>(strJson);
foreach(Node node in nodes)
{
if (node.parentId == 0)
{
TreeNode treeNode = treeView_.Nodes.Add(node.text);
treeNode.Name = $"{node.id}";
treeNode.Checked = node.cheked;
}
else
{
TreeNode[] foundNodes = treeView_.Nodes.Find($"{node.parentId}", true);
if (foundNodes.Length > 0)
{
TreeNode treeNode = foundNodes[0].Nodes.Add(node.text);
treeNode.Checked = node.cheked;
treeNode.Name = $"{node.id}";
}
}
}
}
static private void SerializeTree(List<Node> nodes, TreeNode treeNode)
{
Node node = new Node();
bool suces = Int32.TryParse(treeNode.Name, out node.id);
TreeNode parent = treeNode.Parent;
if (parent != null)
{
suces = Int32.TryParse(parent.Name, out node.parentId);
}
else
{
node.parentId = 0;
}
node.text = treeNode.Text;
node.cheked = treeNode.Checked;
nodes.Add(node);
foreach (TreeNode tn in treeNode.Nodes)
{
SerializeTree(nodes, tn);
}
}
}
}
Directories/Files to TreeView Method
private static void ListDirectory(TreeView treeView, string path)
{
treeView.Nodes.Clear();
int id = 1;
var stack = new Stack<TreeNode>();
var rootDirectory = new DirectoryInfo(path);
var node = new TreeNode(rootDirectory.Name) { Tag = rootDirectory };
stack.Push(node);
while (stack.Count > 0)
{
var currentNode = stack.Pop();
var directoryInfo = (DirectoryInfo)currentNode.Tag;
foreach (var directory in directoryInfo.GetDirectories())
{
var childDirectoryNode = new TreeNode(directory.Name) { Tag = directory };
childDirectoryNode.Name = $"{id++}";
currentNode.Nodes.Add(childDirectoryNode);
stack.Push(childDirectoryNode);
}
foreach (var file in directoryInfo.GetFiles())
{
TreeNode treeNode = new TreeNode(file.Name);
treeNode.Name = $"{id++}";
currentNode.Nodes.Add(new TreeNode(file.Name));
}
}
treeView.Nodes.Add(node);
}
To make this work each TreeNode needs to have a Unique Id that is going to be saved in the name of the TreeNode as I do in the Directories/Files to TreeView Method. Thanks #Fildor and #madreflection for your help!
I have a word file with content control checkboxes. How to find those checkboxes with sprire.doc ???
The demo below explains how to find check box content controls and update their check states with Spire.Doc.
using System;
using System.Windows.Forms;
using Spire.Doc;
using Spire.Doc.Documents;
using System.Collections.Generic;
namespace UpdateCheckBox
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Document document = new Document();
document.LoadFromFile("CheckBoxContentControl.docx");
//Get the content controls in the document
StructureTags structureTags = GetAllTags(document);
List<StructureDocumentTagInline> tagInlines = structureTags.tagInlines;
//Loop through the controls
for (int i = 0; i < tagInlines.Count; i++)
{
//Get the control type
string type = tagInlines[i].SDTProperties.SDTType.ToString();
//Update the check state of check box
if (type == "CheckBox")
{
SdtCheckBox scb = tagInlines[i].SDTProperties.ControlProperties as SdtCheckBox;
if (scb.Checked)
{
scb.Checked = false;
}
else
{
scb.Checked = true;
}
}
}
//Save the document
document.SaveToFile("Output.docx", FileFormat.Docx);
//Open the document
WordDocViewer("Output.docx");
}
static StructureTags GetAllTags(Document document)
{
StructureTags structureTags = new StructureTags();
foreach (Section section in document.Sections)
{
foreach (DocumentObject obj in section.Body.ChildObjects)
{
if (obj.DocumentObjectType == DocumentObjectType.Paragraph)
{
foreach (DocumentObject pobj in (obj as Paragraph).ChildObjects)
{
if (pobj.DocumentObjectType == DocumentObjectType.StructureDocumentTagInline)
{
structureTags.tagInlines.Add(pobj as StructureDocumentTagInline);
}
}
}
}
}
return structureTags;
}
public class StructureTags
{
List<StructureDocumentTagInline> m_tagInlines;
public List<StructureDocumentTagInline> tagInlines
{
get
{
if (m_tagInlines == null)
m_tagInlines = new List<StructureDocumentTagInline>();
return m_tagInlines;
}
set
{
m_tagInlines = value;
}
}
}
private void WordDocViewer(string fileName)
{
try
{
System.Diagnostics.Process.Start(fileName);
}
catch { }
}
}
}
I'm trying to save some application settings to an XML file. To do this, I use the following code in a single Props.cs file:
using System;
//надо добавить для работы класса
using System.Xml.Serialization;
using System.IO;
namespace SettingWinForm
{
//Класс определяющий какие настройки есть в программе
public class PropsFields
{
public String XMLFileName = Environment.CurrentDirectory + "\\settings.xml";
//Чтобы добавить настройку в программу просто добавьте туда строку вида -
//public ТИП ИМЯ_ПЕРЕМЕННОЙ = значение_переменной_по_умолчанию;
public String TextValue = #"File Settings";
public DateTime DateValue = new DateTime(2011, 1, 1);
public Decimal DecimalValue = 555;
public Boolean BoolValue = true;
}
//Класс работы с настройками
public class Props
{
public PropsFields Fields;
public Props()
{
Fields = new PropsFields();
}
//Запись настроек в файл
public void WriteXml()
{
XmlSerializer ser = new XmlSerializer(typeof(PropsFields));
TextWriter writer = new StreamWriter(Fields.XMLFileName);
ser.Serialize(writer, Fields);
writer.Close();
}
//Чтение настроек из файла
public void ReadXml()
{
if (File.Exists(Fields.XMLFileName))
{
XmlSerializer ser = new XmlSerializer(typeof(PropsFields));
TextReader reader = new StreamReader(Fields.XMLFileName);
Fields = ser.Deserialize(reader) as PropsFields;
reader.Close();
}
else
{
//можно написать вывод сообщения если файла не существует
}
}
}
}
I also have a Form in a Form1.cs file that contains textBox1, comboBox1, checkBox1, and two Buttons.
using System;
using System.Windows.Forms;
namespace SettingWinForm
{
public partial class Form1 : Form
{
#region Settings action
Props props = new Props(); //экземпляр класса с настройками
//Запись настроек
private void writeSetting()
{
props.Fields.TextValue = textBox1.Text;
props.Fields.TextValue = comboBox1.Text;
props.Fields.BoolValue = checkBox1.Checked;
props.WriteXml();
}
private void readSetting()
{
props.ReadXml();
textBox1.Text = props.Fields.TextValue;
comboBox1.Text = props.Fields.TextValue;
checkBox1.Checked = props.Fields.BoolValue;
}
#endregion
#region Form Action
public Form1()
{
InitializeComponent();
}
#endregion
private void button1_Click_1(object sender, EventArgs e)
{
writeSetting();
}
private void button2_Click_1(object sender, EventArgs e)
{
readSetting();
}
// Очистить
private void button3_Click(object sender, EventArgs e)
{
textBox1.Text = "";
comboBox1.Text = "";
checkBox1.Checked = false;
}
}
}
How can I read and write multi-line text from textBox1 to an XML file?
What you have will work fine for saving and reading text from textBox1, whether single or multi-line. The problem in the code you've shown is you are saving both the textBox1 text AND then the comboBox1 text to the same field, TextValue. You are overwriting the first value with the second.
You need to create a new text field (i.e. TextValue2) for the comboBox text. Or maybe store the comboBox index value instead into DecimalValue if it is not editable.
I have created a new User Control that has a property like...
private Font m_DisplayFont;
public Font DisplayFont
{
get { return m_DisplayFont; }
set { m_DisplayFont = value; }
}
I want to set m_DisplayFont to the parent's font when I drop the new User Control into a container (Form, GroupBox, etc).
I currently have tried the following but can not get the parent when the class is constructed. Any suggested would be welcome. Thanks!
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Reflection;
namespace MyTestControl
{
public partial class UserControl1 : ProgressBar
{
private Font m_DisplayFont;
public Font DisplayFont
{
get { return m_DisplayFont; }
set { m_DisplayFont = value; }
}
public UserControl1()
{
InitializeComponent();
object parent = base.Parent;
m_DisplayFont = null;
if (parent != null)
{
//See if parent contains a font
Type type = parent.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(type.GetProperties());
foreach (PropertyInfo propinfo in props)
{
if (propinfo.Name == "Font")
{
m_DisplayFont = (Font)propinfo.GetValue(parent, null);
}
}
}
if (m_DisplayFont == null) m_DisplayFont = new Font("Verdana", 20.25f);
}
}
}
You can use the ParentChanged event:
Occurs when the Parent property value changes.
private void ParentChanged(Object sender, EventArgs args)
{
var parent = this.Parent;
if (parent == null)
return;
var fontProp = parent
.GetType()
.GetProperty("Font");
var font = (fontProp == null) ?
new Font("Verdana", 20.25f) : (Font)fontProp.GetValue(parent, null);
this.m_DisplayFont = font;
}
This is the simplest ever custom property editor that contains just a form with one more PropertyGrid:
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms.Design;
namespace PageControls
{
public partial class PropertyGridEditor : Form
{
public object ObjectToEdit;
public delegate void PropertyValueChangedEventHandler(object sender, PropertyValueChangedEventArgs e);
public static event PropertyValueChangedEventHandler PropertyValueChangedStatic;
public event EventHandler<PropertyValueChangedEventArgs> PropertyValueChanged;
public PropertyGridEditor(object obj_to_edit)
{
InitializeComponent();
this.ObjectToEdit = obj_to_edit;
}
private void PropertyGridEditor_Load(object sender, EventArgs e)
{
this.prop_grid.SelectedObject = ObjectToEdit;
}
private void PropertyGridEditor_FormClosed(object sender, FormClosedEventArgs e)
{
this.DialogResult = System.Windows.Forms.DialogResult.OK;
}
private void prop_grid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
var evt = PropertyGridEditor.PropertyValueChangedStatic;
if (evt != null)
evt(s, e);
var evt2 = this.PropertyValueChanged;
if (evt2 != null)
evt2(s, e);
}
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class InnerPropertyGridEditor : UITypeEditor
{
public InnerPropertyGridEditor()
{
}
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
// Indicates that this editor can display a Form-based interface.
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
// Attempts to obtain an IWindowsFormsEditorService.
IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if (edSvc == null)
return null;
using (PropertyGridEditor form = new PropertyGridEditor(value)) //when two or more properties were selected the value is null :/
if (edSvc.ShowDialog(form) == DialogResult.OK)
return form.ObjectToEdit;
return value; // If OK was not pressed, return the original value
}
}
}
So, now I have a class:
class Test
{
public bool Prop1 { get; set; }
public bool Prop2 { get; set; }
}
And I have main class that has this Test class as property.
class MainClass
{
[Editor(typeof(InnerPropertyGridEditor), typeof(UITypeEditor))]
public Test test_prop { get; set; }
...
}
My main PropertyEditor supports multi selected objects.
So, I can select two or more MainClasses to edit their properties.
The problem is - when I do that and tries to edit test_prop InnerPropertyGridEditor appears empty, because of passed value is null.
Actually, I hoped it to be at least object[] so I can implement something.
Ok, in case if no one will ever answer this I will show the hacky solution I made:
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms.Design;
using System.Reflection;
namespace PageControls
{
public partial class PropertyGridEditor : Form
{
public object Result;
public static event EventHandler<PropertyValueChangedEventArgs> PropertyValueChangedStatic;
public event EventHandler<PropertyValueChangedEventArgs> PropertyValueChanged;
public PropertyGridEditor(object[] obj_to_edit)
{
InitializeComponent();
this.prop_grid.SelectedObjects = obj_to_edit;
this.Result = obj_to_edit[0];
}
private void PropertyGridEditor_Load(object sender, EventArgs e)
{
}
private void PropertyGridEditor_FormClosed(object sender, FormClosedEventArgs e)
{
this.DialogResult = System.Windows.Forms.DialogResult.OK;
}
private void prop_grid_PropertyValueChanged(object s, PropertyValueChangedEventArgs e)
{
var evt = PropertyGridEditor.PropertyValueChangedStatic;
if (evt != null)
evt(s, e);
var evt2 = this.PropertyValueChanged;
if (evt2 != null)
evt2(s, e);
}
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class InnerPropertyGridEditor : UITypeEditor
{
public InnerPropertyGridEditor()
{
}
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
// Indicates that this editor can display a Form-based interface.
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
// Attempts to obtain an IWindowsFormsEditorService.
IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if (edSvc == null)
return null;
object[] values = new object[context.Instance is object[] ? ((object[])context.Instance).Length : 1];
if (context.Instance is object[])
for (int i = 0; i < ((object[])context.Instance).Length; i++)
{
PropertyInfo pi = ((object[])context.Instance)[i].GetType().GetProperty(context.PropertyDescriptor.Name);
values[i] = pi != null ? pi.GetValue(((object[])context.Instance)[i], null) : null;
}
else
values[0] = value;
using (PropertyGridEditor form = new PropertyGridEditor(values))
if (edSvc.ShowDialog(form) == DialogResult.OK)
return form.Result;
return value; // If OK was not pressed, return the original value
}
}
}