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 { }
}
}
}
Related
I made an mp3 player in C#. If I select the songs at once, they automatically play one after the other, but when it comes to a song I added later, I get a "System.IndexOutOfRangeException" error. When I add music later, I want the song to play automatically one after the other, how can I do that?
string[] yol, dosya;
private void Btn_Muzik_Ekle_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = true;
if (ofd.ShowDialog()==System.Windows.Forms.DialogResult.OK)
{
dosya = ofd.SafeFileNames;
yol = ofd.FileNames;
for (int x = 0; x < dosya.Length; x++)
{
Lb_Muzik_Listesi.Items.Add(dosya[x]);
}
}
}
private void Lb_Muzik_Listesi_SelectedIndexChanged(object sender, EventArgs e)
{
//This Is Where I Got The Error
OynatmaEkranı.URL = yol[Lb_Muzik_Listesi.SelectedIndex];
OynatmaEkranı.Ctlcontrols.play();
try
{
var file = TagLib.File.Create(yol[Lb_Muzik_Listesi.SelectedIndex]);
var bin = (byte[])(file.Tag.Pictures[0].Data.Data);
Pb_Muzik_Kapak.Image = Image.FromStream(new MemoryStream(bin));
}
catch
{
}
}
private void Zamanlayıcı_Tick(object sender, EventArgs e) //Timer
{
if (OynatmaEkranı.playState==WMPLib.WMPPlayState.wmppsPlaying)
{
Pb_Muzik.Maximum=(int)OynatmaEkranı.Ctlcontrols.currentItem.duration;
Pb_Muzik.Value = (int)OynatmaEkranı.Ctlcontrols.currentPosition;
try
{
Lbl_Muzik_Sure.Text = OynatmaEkranı.Ctlcontrols.currentPositionString;
Lbl_Muzik_Bitis.Text = OynatmaEkranı.Ctlcontrols.currentItem
.durationString.ToString();
}
catch
{
}
}
if (Pb_Muzik.Value==Pb_Muzik.Maximum)
{
if (Lb_Muzik_Listesi.SelectedIndex<Lb_Muzik_Listesi.Items.Count-1)
{
Lb_Muzik_Listesi.SelectedIndex = Lb_Muzik_Listesi.SelectedIndex + 1;
}
}
}
You can avoid this problem managing your data in the ListBox. Create a class with your required info (the file and the name):
public class MuzikItem
{
public MuzikItem(string file)
{
this.Text = System.IO.Path.GetFileNameWithoutExtension(file);
this.Url = file;
}
public string Text { get; set; }
public string Url { get; set; }
public override string ToString()
{
// This is the text to show in ListBox
return this.Text;
}
}
Add items to the ListBox using this class:
foreach (var file in ofd.FileNames)
{
var item = new MuzikItem(file);
Lb_Muzik_Listesi.Items.Add(item);
}
And use it:
var item = Lb_Muzik_Listesi.SelectedItem as MuzikItem;
if (item != null)
{
OynatmaEkranı.URL = item.Url;
OynatmaEkranı.Ctlcontrols.play();
try
{
var file = TagLib.File.Create(item.Url);
var bin = (byte[])file.Tag.Pictures[0].Data.Data;
Pb_Muzik_Kapak.Image = Image.FromStream(new MemoryStream(bin));
}
catch
{
}
}
You try to get the selected item as a MuzikItem (all your items are of this class so this return null only when no item is selected) and with this, you have the Url.
UPDATE
I like manage this things with events. In your Ticks methods:
if (Pb_Muzik.Value == Pb_Muzik.Maximum)
{
OnSongFinished();
}
And create a method to manage this event:
private void OnSongFinished()
{
if (Lb_Muzik_Listesi.SelectedIndex < Lb_Muzik_Listesi.Items.Count - 1)
{
Lb_Muzik_Listesi.SelectedIndex = Lb_Muzik_Listesi.SelectedIndex + 1;
}
else
{
// Stop the player
OynatmaEkrani.Ctlcontrols.stop();
}
}
I create a class that I define some variables with their properties.
also I have two class "Form1" and "Form2".
I assign values to this variables in "Form1" but when I want to use the values in "Form2" after I assigned them and show them through MessageBox.Show(), I find out the variables are empty.
class Property
{
private string a_username;
private string a_email;
public string username
{
get { return a_username; }
set { a_username = value; }
}
public string email
{
get { return a_email; }
set { a_email = value; }
}
public string password { get; set; } = "88306540";
}
the assignment: (this function is in "Form1")
Property pro = new Property();
private void CreateUserInform()
{
userid = File.ReadLines(filePath).Skip(idx).Take(1).First();
// MessageBox.Show(userid);
HtmlElementCollection elemcol = webBrowser2.Document.GetElementsByTagName("option");
int i = 0;
string[] mailservices = new string[elemcol.Count];
foreach (HtmlElement elem in elemcol)
{
mailservices[i] = elem.InnerText;
i += 1;
}
pro.username = userid;
Random rand = new Random();
mailservice = mailservices[rand.Next(10)];
pro.email = pro.username + mailservice;
wb2func_create_mail();
}
call function: (this function is in "Form2" and it called after previous function.)
Property pro = new Property();
public void signup_fill()
{
HtmlElementCollection elemcol = site.Document.GetElementsByTagName("input");
foreach (HtmlElement elem in elemcol)
{
if (elem.Name == "login")
elem.SetAttribute("value", pro.username);
if (elem.Name == "remail")
elem.SetAttribute("value", pro.email);
if (elem.Name == "password")
elem.SetAttribute("value", pro.password);
if (elem.Name == "password2")
elem.SetAttribute("value", pro.password);
}
MessageBox.Show(pro.username);
}
I should mention that the "password" variable was shown pretty good but the others were shown empty.
also when I call them in "Form1" that I used to define them, it works just fine and shows the correct assignment.
the completely Form2 codes:
namespace Bypassing
{
public partial class Form2 : Form
{
string referal_link;
Property pro = new Property();
public Form2(Property form1Property)
{
InitializeComponent();
pro = form1Property;
}
public void signup_fill()
{
HtmlElementCollection elemcol = site.Document.GetElementsByTagName("input");
foreach (HtmlElement elem in elemcol)
{
if (elem.Name == "login")
elem.SetAttribute("value", pro.username);
if (elem.Name == "remail")
elem.SetAttribute("value", pro.email);
if (elem.Name == "password")
elem.SetAttribute("value", pro.password);
if (elem.Name == "password2")
elem.SetAttribute("value", pro.password);
}
MessageBox.Show(pro.username);
}
private void btn_fill_Click(object sender, EventArgs e)
{
signup_fill();
}
private void btn_logout_Click(object sender, EventArgs e)
{
HtmlElementCollection elemcol = site.Document.GetElementsByTagName("a");
foreach (HtmlElement elem in elemcol)
{
if (elem.InnerText == " Log out ")
elem.InvokeMember("click");
}
}
private void btn_next_link_Click(object sender, EventArgs e)
{
}
public bool btn_fill_enabled
{
get { return btn_fill.Enabled; }
set { btn_fill.Enabled = value; }
}
public bool btn_logout_enabled
{
get { return btn_logout.Enabled; }
set { btn_logout.Enabled = value; }
}
public bool btn_next_link_enabled
{
get { return btn_next_link.Enabled; }
set { btn_next_link.Enabled = value; }
}
private void avelon_site_Completed(object sender, WebBrowserNavigatedEventArgs e)
{
Form1 main_win = new Form1();
main_win.text_edit();
}
}
}
Form1 codes:
namespace BitcoinCloudMiningBypassApp
{
public partial class Form1 : Form
{
private string mailservice;
private string userid;
int idx = 29;
bool wb1flag = true;
bool wb2flag = true;
bool wb1ready = false;
bool wb2ready = false;
bool workflag = false;
bool start = false;
string filePath;
//StreamWriter file2 = new StreamWriter("avelon_users_email.txt", true);
//StreamWriter file = new StreamWriter("avelon_referal_links.txt", true);
Property pro = new Property();
private void OpenFileDialogForImportingUserId()
{
//var fileContent = string.Empty;
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = "C:\\";
openFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
openFileDialog.FilterIndex = 1;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
//Get the path of specified file
filePath = openFileDialog.FileName;
btn_start.Enabled = true;
////Read the contents of the file into a stream
//var fileStream = openFileDialog.OpenFile();
//using (StreamReader reader = new StreamReader(fileStream))
//{
// fileContent = reader.ReadToEnd();
//}
}
}
}
private void CreateUserInform()
{
userid = File.ReadLines(filePath).Skip(idx).Take(1).First();
// MessageBox.Show(userid);
HtmlElementCollection elemcol = webBrowser2.Document.GetElementsByTagName("option");
int i = 0;
string[] mailservices = new string[elemcol.Count];
foreach (HtmlElement elem in elemcol)
{
mailservices[i] = elem.InnerText;
i += 1;
}
pro.username = userid;
Random rand = new Random();
mailservice = mailservices[rand.Next(10)];
pro.email = pro.username + mailservice;
//MessageBox.Show(avelon_email);
wb2func_create_mail();
}
public Form1()
{
InitializeComponent();
webBrowser2.Navigate("https://temp-mail.org/en/option/change/");
}
private void btn_start_Click(object sender, EventArgs e)
{
Form2 avelon = new Form2(pro);
avelon.Show();
//VPN vpn = new VPN();
// MessageBox.Show(vpn.ConnectVPN(vpn.SetServer(0)).ToString());
CreateAvelonUserInform();
start = true;
btn_load.Enabled = false;
btn_start.Enabled = false;
avelon.btn_logout_enabled = true;
btn_refresh.Enabled = false;
}
private void Form1_Load(object sender, EventArgs e)
{
btn_load.Enabled = false;
btn_start.Enabled = false;
btn_next.Enabled = false;
//Process.Start("cmd.exe", "taskkill / F / im notepad.exe");
}
private void webBrowser2_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (wb2flag)
{
wb2flag = false;
wb2ready = true;
if (!start)
btn_load.Enabled = wb2ready;
}
else
{
text_edit();
}
}
private void wb2func_create_mail()
{
HtmlElementCollection elemcol = webBrowser2.Document.GetElementsByTagName("option");
foreach (HtmlElement elem in elemcol)
{
if (elem.InnerText == mailservice)
elem.SetAttribute("selected", "selected");
}
HtmlElementCollection elemcol2 = webBrowser2.Document.GetElementsByTagName("input");
foreach (HtmlElement elem in elemcol2)
if (elem.GetAttribute("name") == "mail")
elem.SetAttribute("value", pro.username);
//wb2flag = true;
//workflag = true;
webBrowser2.Document.GetElementById("postbut").InvokeMember("click");
}
private void btn_load_Click(object sender, EventArgs e)
{
//OpenFileDialogForImportingUserId();
VPN vpn = new VPN();
MessageBox.Show(vpn.ConnectVPN(vpn.SetServer(0)).ToString());
//Process.Start("C:\\Users\\Hossein\\Desktop\\output.bat");
//MessageBox.Show(Environment.GetEnvironmentVariable("a", EnvironmentVariableTarget.Process));
//MessageBox.Show(Environment.GetEnvironmentVariable("a", EnvironmentVariableTarget.User));
//MessageBox.Show(Environment.GetEnvironmentVariable("a", EnvironmentVariableTarget.Machine));
}
public void text_edit()
{
txt_username.TextChanged += new EventHandler(text_change);
txt_username.Text = pro.username;
txt_email.Text = pro.email;
}
private void btn_next_Click(object sender, EventArgs e)
{
}
private void btn_refresh_Click(object sender, EventArgs e)
{
webBrowser2.Navigate("https://temp-mail.org/en/option/change/");
}
private void text_change(object s , EventArgs e)
{
Form2 avelon = new Form2(pro);
avelon.btn_fill_enabled = true;
}
}
}
Seems that password is showing fine as it's defined at the Property class itself so, each time you create a new object of that class, it will be setted by default untill you change it.
The reason you don't see the data from Form 1 at Form 2 seems to be that you aren't passing the Property object from Form 1 to Form 2, so you have a Property object filled with data at Form 1 but at Form 2 you remain with a newly created Property object. Just modify the Form 2 constructor so it accepts a Property parameter and pass it from Form 1.
Example (this goes on your Form2's code):
public Form2 (Property form1Property){
InitializeComponent();
pro = form1Property;
}
That creates a code that executes each time you create a new Form2 and requires you to pass a Property object to create it (new Form2(pro); in Form1) so it assigns Form2's Property object to the one you passed when creating it's object on Form1.
Also make your Property class public, so you can use it as a parameter at Form2's constructor.
More info about constructors here
Hope this helps you!
P.S: Looking at your code I see that you're creating a global Form2 object. You should create it at btn_start_Click before you are showing it, so data is filled up correctly (when you show it your Form1's Property object is filled, now when you create it it's not filled)
I am currently writing an application in WPF C# that is some kind of helper for other processes which are written in Java. Those other processes require a configuration.csv in which different "names" are listed with a column that says "SKIP". If the column is X at Skip, my java program will skip those names and therefore their dependent processes.
If I open the CSV with Excel and edit the rows, everything works perfectly fine. That's not the problem. What I want to achieve is to list the rows into a DataGrid in a WPF App (except the first and last row), where the user can tick a checkbox to decide if he wants to skip that specific name or not. By pressing Save, the .CSV gets updated.
I already wrote some code with a friend who's more familiar with this topic. It worked fine in WinForms but doesn't work on WPF. We are not able to get the values of the checkboxes and not able to save them into the CSV.
CODE:
private void OBJ_SaveButton_Click(object sender, RoutedEventArgs e)
{
if (OBJ_DataGrid.Items.Count == 0)
{
MessageBox.Show("Kein Datensatz in der View.");
return;
}
/*if (Directory.Exists(path))
{
if (File.Exists(filepath))
{
string tmp = null;
try
{
FileStream fileStr = new FileStream(filepath, FileMode.Create);
StreamWriter strWriter = new StreamWriter(fileStr);
strWriter.WriteLine("SFObject;Skip");
for(int i=0;i< itmGrd.Count;i++)
{
switch (itmGrd[i].ItemValue)
{
case true:
tmp = itmGrd[i].ItemName + ";X";
break;
case false:
tmp = itmGrd[i].ItemName + ";";
break;
}
strWriter.WriteLine(tmp);
}
strWriter.WriteLine("SuccessMSG;");
strWriter.Close();
fileStr.Close();
LoadConf();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
else
MessageBox.Show("ERR_F0: Pfad nicht gefunden.");
}
else
MessageBox.Show("ERR_D0: Pfad nicht gefunden.");*/
}
private void OBJ_ReloadButton_Click(object sender, RoutedEventArgs e)
{
}
private void OBJ_DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
private void OBJ_DataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
MessageBox.Show(e.Row.ToString());
}
void OnChecked(object sender, RoutedEventArgs e)
{
MessageBox.Show(e.Source.ToString());
}
}
public class ItemGrid
{
public ItemGrid(string name, bool rval)
{
ItemName = name;
ItemValue = rval;
}
public string ItemName { set; get; }
public bool ItemValue { set; get; }
}
public class ItemsGrid : List<ItemGrid>
{
public string path = null;
public string filepath = null;
public ItemsGrid()
{
path = String.Format(#"{0}\build\", Environment.CurrentDirectory);
filepath = Path.Combine(path + "configuration.csv");
if (Directory.Exists(path))
{
if (File.Exists(filepath))
{
string line = null;
StreamReader file = new StreamReader(filepath);
while ((line = file.ReadLine()) != null)
{
if (!line.Equals("SFObject;Skip") && !line.Equals("SuccessMSG;"))
{
string input = (line.IndexOf(";X") != -1 ? (line.Replace(";X", "")) : (line.Replace(";", "")));
Add(new ItemGrid(input, (line.IndexOf(";X") != -1 ? (true) : (false))));
}
}
file.Close();
}
else
MessageBox.Show("ERR_F0: Pfad nicht gefunden.");
}
else
MessageBox.Show("ERR_D0: Pfad nicht gefunden.");
//Add(new ItemGrid("Tom", false));
// Add(new ItemGrid("Jen", false));
}
}
This is how it looks (and should look like).
CSV:
I hope that you guys can help me out, I really don't understand why it's not working. I also have to admit that I am not any near of being proficient in C#.
I'm assuming the CSV you pasted is considered properly formatted.
// HelperClass.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfCsvSkipTicker
{
public static class HelperClass
{
public static List<ItemGrid> ReadCsv(string filepath)
{
if (!File.Exists(filepath)) return null;
var allLines = File.ReadAllLines(filepath);
var result =
from line in allLines.Skip(1).Take(allLines.Length -2)
let temparry = line.Split(';')
let isSkip =
temparry.Length > 1
&& temparry[1] != null
&& temparry[1] == "X"
select new ItemGrid { ItemName = temparry[0], ItemValue = !isSkip };
return result.ToList();
}
public static void WriteCsv(IEnumerable<ItemGrid> items, string filepath)
{
var temparray = items.Select(item => item.ItemName + ";" + (item.ItemValue ? "" : "X")).ToArray();
var contents = new string[temparray.Length + 2];
Array.Copy(temparray, 0, contents, 1, temparray.Length);
contents[0] = "SFOBject;Skip";
contents[contents.Length - 1] = "SuccessMSG;";
File.WriteAllLines(filepath, contents);
}
}
public class ItemGrid
{
public string ItemName { set; get; }
public bool ItemValue { set; get; }
}
}
And...
// MainWindow.xaml.cs
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.dataGridView.ItemsSource = HelperClass.ReadCsv(#"PathToRead\configuration.csv");
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
var temp = new List<ItemGrid>();
for (int i = 0; i < this.dataGridView.Items.Count; i++)
{
if (this.dataGridView.Items[i] is ItemGrid) // DataGrid pads it's item collection with elements we didn't add.
temp.Add((ItemGrid)this.dataGridView.Items[i]);
}
HelperClass.WriteCsv(temp, #"PathToSave\new_configuration.csv");
}
}
Everything works fine until I actually call the SetPicture() Method. Any ideas as to why the image will not display when the selected item is changed. Right now i only have one image preset until i figure out my problem. Thanks for any help
namespace CelestialWindowsApp
{
public partial class Form1 : Form
{
public static List CelestialLibrary = new List(); private static CelestialBody celestialBody; private static string name; private static string description; private static string image; public static CelestialBody NewPlanet { get { return celestialBody; } set { celestialBody = value; } }
public Form1()
{
InitializeComponent();
PopulateLibrary();
}
public void PopulateLibrary()//Runs when the Form 1 is loaded. Adds preset item into the ListNox.
{
CelestialLibrary = new List<CelestialBody>();
CelestialBody cb1 = new CelestialBody("Earth", "Our Home", #"C:\Users\Cassidy\documents\visual studio 2015\Projects\CelestialWindowsApp\CelestialWindowsApp\ImageResources\earthimage.jpg");
CelestialBody cb2 = new CelestialBody("Mars", "4th planet from the sun.");
CelestialBody cb3 = new CelestialBody("Venus", "2nd planet from the son");
CelestialLibrary.Add(cb1);
CelestialLibrary.Add(cb3);
lbLibrary.DataSource = CelestialLibrary;
foreach (var p in CelestialLibrary)
{
lbLibrary.DisplayMember = "ShowBodies";
}
}
public void AddItem() // Adds the new Item to my library and then adds to and updates the ListBox;
{
CelestialBody cb;
name = txtboxName.Text;
description = txtboxDescription.Text;
image = txtboxImagePath.Text;
cb = new CelestialBody(name, description, image);
CelestialLibrary.Add(cb);
MessageBox.Show(name + " has been added to the Library.");
txtboxName.Text = null;
txtboxDescription.Text = null;
lbLibrary.DataSource = null;
lbLibrary.DataSource = CelestialLibrary;
lbLibrary.DisplayMember = "ShowBodies";
}
public void DisplayItemInfo()//Displays the current selected item information.
{
List<CelestialBody> bodyList = (List<CelestialBody>)lbLibrary.DataSource;
CelestialBody currentItem = bodyList[lbLibrary.SelectedIndex];
foreach (CelestialBody cb in bodyList)
{
if (currentItem.MyId == cb.MyId)
celestialBody = new CelestialBody(cb.Name, cb.Description, cb.ImagePath);
{
txtboxItemInfo.Text = celestialBody.Description;
SetPicture(celestialBody.ImagePath);
}
}
}
public void SetPicture(string image)
{
if (picboxBodyImage.Image != null)
{
picboxBodyImage.Image.Dispose();
}
picboxBodyImage.ImageLocation = image;
}
private void btnAddNewBody_Click(object sender, EventArgs e)
{
AddItem();
}
private void lbLibrary_SelectedIndexChanged(object sender, EventArgs e)
{
DisplayItemInfo();
}
}
}
Did you check the path in variable image. Assuming path is correct, Try below
picboxBodyImage.Image = Image.FromFile(image);
I'm stuck with some legacy code that I want to upgrade a bit. I want to change the way the ErrorProvider shows the error status on a Control. Default behavior is the Icon, and a ToolTip if you hover on the icon.
I would like to change this behavior to be more similar to what we use in our WPF controls. Which is a red back-color(Salmon pink) and the tool-tip on the control itself.
Any tips, links or some way forward
EDIT.
See my answer below, on what i ended up with.
ErrorProvider component doesn't support this feature and if you need it you can create it yourself.
You can subscribe to BindingComplete event of a BindingManagerBase and then you can use the event arg which is of type BindingCompleteEventArgs that contains some useful properties:
ErrorText to determine if there is an error in data-binding
Binding.Control to determine the control which is bounded to
These are enough for us to implement our solution.
Code
Here is a sample code which shows how can you handle BindingComplete event and use it to change BackColor and tool-tip of a control based on it's valid or invalid state.
Suppose you have a binding source, myBindingSource which is bound to a SampleModel class which is implemented IDataErrorInfo. You can subscribe to BindingComplete event of this.BindingContext[this.myBindingSource]:
private void Form1_Load(object sender, EventArgs e)
{
this.myBindingSource.DataSource = new SampleModel();
var bindingManager = this.BindingContext[this.myBindingSource];
bindingManager.BindingComplete += bindingManager_BindingComplete;
}
Dictionary<Control, Color> Items = new Dictionary<Control, Color>();
private void bindingManager_BindingComplete(object sender, BindingCompleteEventArgs e)
{
var control = e.Binding.Control;
//Store Original BackColor
if (!Items.ContainsKey(control))
Items[control] = control.BackColor;
//Check If there is an error
if (!string.IsNullOrEmpty(e.ErrorText))
{
control.BackColor = Color.Salmon;
this.errorToolTip.SetToolTip(control, e.ErrorText);
}
else
{
e.Binding.Control.BackColor = Items[e.Binding.Control];
this.errorToolTip.SetToolTip(control, null);
}
}
Thank you Reza Aghaei. This is what i came up with based on your comment and some additional searching... Some of this code comes from msdn resource
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Linq;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ErrorProvider
{
class BackgroundColorErrorProvider: Component, IExtenderProvider, ISupportInitialize
{
public BackgroundColorErrorProvider()
{
currentChanged = new EventHandler(ErrorManager_CurrentChanged);
}
public BackgroundColorErrorProvider(ContainerControl parentControl)
: this()
{
this.parentControl = parentControl;
propChangedEvent = new EventHandler(ParentControl_BindingContextChanged);
parentControl.BindingContextChanged += propChangedEvent;
}
public BackgroundColorErrorProvider(IContainer container)
: this()
{
if (container == null) {
throw new ArgumentNullException("container");
}
container.Add(this);
}
public bool CanExtend(object extendee)
{
return extendee is Control && !(extendee is Form) && !(extendee is ToolBar);
}
private bool inSetErrorManager = false;
private object dataSource;
private string dataMember = null;
private ContainerControl parentControl;
private BindingManagerBase errorManager;
private bool initializing;
private EventHandler currentChanged;
private EventHandler propChangedEvent;
private Dictionary<Control, Color> originalColor = new Dictionary<Control, Color>();
private Color errorBackgroundColor;
public ContainerControl ContainerControl
{
[UIPermission(SecurityAction.LinkDemand, Window = UIPermissionWindow.AllWindows)]
[UIPermission(SecurityAction.InheritanceDemand, Window = UIPermissionWindow.AllWindows)]
get
{
return parentControl;
}
set
{
if (parentControl != value)
{
if (parentControl != null)
parentControl.BindingContextChanged -= propChangedEvent;
parentControl = value;
if (parentControl != null)
parentControl.BindingContextChanged += propChangedEvent;
Set_ErrorManager(this.DataSource, this.DataMember, true);
}
}
}
public string DataMember
{
get { return dataMember; }
set
{
if (value == null) value = "";
Set_ErrorManager(this.DataSource, value, false);
}
}
public object DataSource
{
get { return dataSource; }
set
{
if ( parentControl != null && value != null && String.IsNullOrEmpty(this.dataMember))
{
// Let's check if the datamember exists in the new data source
try
{
errorManager = parentControl.BindingContext[value, this.dataMember];
}
catch (ArgumentException)
{
// The data member doesn't exist in the data source, so set it to null
this.dataMember = "";
}
}
Set_ErrorManager(value, this.DataMember, false);
}
}
public override ISite Site
{
set
{
base.Site = value;
if (value == null)
return;
IDesignerHost host = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
if (host != null)
{
IComponent baseComp = host.RootComponent;
if (baseComp is ContainerControl)
{
this.ContainerControl = (ContainerControl)baseComp;
}
}
}
}
private ToolTip toolTip;
public ToolTip ToolTip
{
get { return toolTip; }
set { toolTip = value; }
}
public Color ErrorBackgroundColor
{
get { return errorBackgroundColor; }
set { errorBackgroundColor = value; }
}
private void Set_ErrorManager(object newDataSource, string newDataMember, bool force)
{
if (inSetErrorManager)
return;
inSetErrorManager = true;
try
{
bool dataSourceChanged = this.DataSource != newDataSource;
bool dataMemberChanged = this.DataMember != newDataMember;
//if nothing changed, then do not do any work
//
if (!dataSourceChanged && !dataMemberChanged && !force)
{
return;
}
// set the dataSource and the dataMember
//
this.dataSource = newDataSource;
this.dataMember = newDataMember;
if (!initializing)
{
UnwireEvents(errorManager);
// get the new errorManager
//
if (parentControl != null && this.dataSource != null && parentControl.BindingContext != null)
{
errorManager = parentControl.BindingContext[this.dataSource, this.dataMember];
}
else
{
errorManager = null;
}
// wire the events
//
WireEvents(errorManager);
// see if there are errors at the current
// item in the list, w/o waiting for the position to change
if (errorManager != null)
UpdateBinding();
}
}
finally
{
inSetErrorManager = false;
}
}
public void UpdateBinding()
{
ErrorManager_CurrentChanged(errorManager, EventArgs.Empty);
}
private void UnwireEvents(BindingManagerBase listManager)
{
if (listManager != null)
{
listManager.CurrentChanged -= currentChanged;
listManager.BindingComplete -= new BindingCompleteEventHandler(this.ErrorManager_BindingComplete);
CurrencyManager currManager = listManager as CurrencyManager;
if (currManager != null)
{
currManager.ItemChanged -= new ItemChangedEventHandler(this.ErrorManager_ItemChanged);
currManager.Bindings.CollectionChanged -= new CollectionChangeEventHandler(this.ErrorManager_BindingsChanged);
}
}
}
private void WireEvents(BindingManagerBase listManager)
{
if (listManager != null)
{
listManager.CurrentChanged += currentChanged;
listManager.BindingComplete += new BindingCompleteEventHandler(this.ErrorManager_BindingComplete);
CurrencyManager currManager = listManager as CurrencyManager;
if (currManager != null)
{
currManager.ItemChanged += new ItemChangedEventHandler(this.ErrorManager_ItemChanged);
currManager.Bindings.CollectionChanged += new CollectionChangeEventHandler(this.ErrorManager_BindingsChanged);
}
}
}
private void ErrorManager_BindingsChanged(object sender, CollectionChangeEventArgs e)
{
ErrorManager_CurrentChanged(errorManager, e);
}
private void ParentControl_BindingContextChanged(object sender, EventArgs e)
{
Set_ErrorManager(this.DataSource, this.DataMember, true);
}
private void ErrorManager_ItemChanged(object sender, ItemChangedEventArgs e)
{
BindingsCollection errBindings = errorManager.Bindings;
int bindingsCount = errBindings.Count;
// If the list became empty then reset the errors
if (e.Index == -1 && errorManager.Count == 0)
{
for (int j = 0; j < bindingsCount; j++)
{
if ((errBindings[j].Control != null))
{
// ...ignore everything but bindings to Controls
SetError(errBindings[j].Control, "");
}
}
}
else
{
ErrorManager_CurrentChanged(sender, e);
}
}
private void SetError(Control control, string p)
{
if (String.IsNullOrEmpty(p))
{
if (originalColor.ContainsKey(control))
control.BackColor = originalColor[control];
toolTip.SetToolTip(control, null);
}
else
{
control.BackColor = ErrorBackgroundColor;
toolTip.SetToolTip(control, p);
}
}
private void ErrorManager_BindingComplete(object sender, BindingCompleteEventArgs e)
{
Binding binding = e.Binding;
if (binding != null && binding.Control != null)
{
SetError(binding.Control, (e.ErrorText == null ? String.Empty : e.ErrorText));
}
}
private void ErrorManager_CurrentChanged(object sender, EventArgs e)
{
if (errorManager.Count == 0)
{
return;
}
object value = errorManager.Current;
if (!(value is IDataErrorInfo))
{
return;
}
BindingsCollection errBindings = errorManager.Bindings;
int bindingsCount = errBindings.Count;
// We can only show one error per control, so we will build up a string...
//
Hashtable controlError = new Hashtable(bindingsCount);
for (int j = 0; j < bindingsCount; j++)
{
// Ignore everything but bindings to Controls
if (errBindings[j].Control == null)
{
continue;
}
string error = ((IDataErrorInfo)value)[errBindings[j].BindingMemberInfo.BindingField];
if (error == null)
{
error = "";
}
string outputError = "";
if (controlError.Contains(errBindings[j].Control))
outputError = (string)controlError[errBindings[j].Control];
// VSWhidbey 106890: Utilize the error string without including the field name.
if (String.IsNullOrEmpty(outputError))
{
outputError = error;
}
else
{
outputError = string.Concat(outputError, "\r\n", error);
}
controlError[errBindings[j].Control] = outputError;
}
IEnumerator enumerator = controlError.GetEnumerator();
while (enumerator.MoveNext())
{
DictionaryEntry entry = (DictionaryEntry)enumerator.Current;
SetError((Control)entry.Key, (string)entry.Value);
}
}
public void BeginInit()
{
initializing = true;
}
public void EndInit()
{
initializing = false;
Set_ErrorManager(this.DataSource, this.DataMember, true);
}
}
}