Scenario:
A child form which is made visible via a button.
A delegate is created to run certain code when this child is closed.
The child form is used to edit the underlying data
When the child form is closed the latest version of the data should be displayed on any bound controls on the parent form.
Question -
Here is the relevant code attempt:
public partial class uxRevisionHelperForm : Form
{
public SqlCeConnection conn = new SqlCeConnection(ConfigurationManager.ConnectionStrings["WindFormAppRevisionHelper.Properties.Settings.DefinitionsDBConnectionString"].ConnectionString);
BindingSource definitionsBindingSource = new BindingSource();
public uxRevisionHelperForm()
{
InitializeComponent();
uxDescriptionTextBox.AutoSize = true;
refreshBindingSource();
assignControlsToSource();
}
//>>>>>>>>ALL OF THE FOLLOWING METHOD IS CALLED BY THE DELEGATE WHEN THE CHILD IS CLOSED
public void refreshBindingSource()
{
SqlCeDataAdapter da = new SqlCeDataAdapter(new SqlCeCommand("Select * From tb_RevisionDefinitions",conn));
DataSet ds = new DataSet("Helper");
ds.Tables.Add("DefinitionsTable");
da.Fill(ds.Tables["DefinitionsTable"]);
// Assign the BindingSource.
definitionsBindingSource.DataSource = ds.Tables["DefinitionsTable"];
uxBindingNavigator.BindingSource = this.definitionsBindingSource;
}
void assignControlsToSource()
{
uxDescriptionTextBox.DataBindings.Add(new Binding("Text", definitionsBindingSource, "Description", true));
uxWordPhraseTextBox.DataBindings.Add(new Binding("Text", definitionsBindingSource, "WordPhrase", true));
uxReferenceTextBox.DataBindings.Add(new Binding("Text", definitionsBindingSource, "Reference", true));
}
private void uxUpdateDataButton_Click(object sender, EventArgs e)
{
uxRevisionHelperGroupBox.Enabled = false;
uxBindingNavigator.Hide();
uxFormDatabase myNewDisplay = new uxFormDatabase();
myNewDisplay.FormClosed += delegate { activateGroupBorder(); };
myNewDisplay.Show();
}
public void activateGroupBorder()
{
uxRevisionHelperGroupBox.Enabled = true;
uxBindingNavigator.Show();
refreshBindingSource(); //<<<<<<<<<<<DELEGATE CALLS THIS METHOD
}
}
The above seems to work but do I really have to run all the code in the method refreshBindingSource to make sure the info displayed on the parent form is up-to-date ?
UPDATE
I've followed Amiram's advice and passed in my BindingSource so as not to have to repeat code already in place for the parent form. I've copied in some boiler plate code the method saveToolStripButton_Click; ... really don't know what is going on in that small routine - will those two lines will suffice for saving info back to the database?
public partial class uxFormDatabase : Form
{
BindingSource rawtableBindingSource = null;
public uxFormDatabase(BindingSource myPassedSource)
{
InitializeComponent();
rawtableBindingSource = myPassedSource;
uxDGVtable.AutoSize = true;
uxDGVtable.SizeChanged += new EventHandler(uxDGVtable_change);
dataToDGV();
}
public void uxDGVtable_change(object sender, EventArgs e)
{
if (uxDGVtable.Width < 1158)
{
this.Width = uxDGVtable.Width;
}
}
public void dataToDGV()
{
uxrawdataBindingNavigator.BindingSource = this.rawtableBindingSource;
uxDGVtable.DataSource = this.rawtableBindingSource;
}
private void saveToolStripButton_Click(object sender, EventArgs e)
{
Validate();
rawtableBindingSource.EndEdit();
}
}
If you used different data source for both forms them you have no choice but to reload data (there is a way to automate that with sql server), but you can avoid that if you'll use the same dataset or even the same BindingSource, so the refresh will happen automatically.
Related
In my Add-in for Visio, I have set a handler for 'ShapeAdded'.
This fired for the first 2 or 3 shapes that are added, but then just stop firing altogether.
Here's the basic outline of how my add-in functions:
User adds shape to page
On ShapeAdded, a form is displayed.
User enters text in form, presses search button
Call to stored Procedure (parameter = user text)
Form's datagridview is populated with results.
User double-clicks result row required.
Form closes, selected value becomes shape text.
If I comment out my code after item (3) - then my event handler continues firing without issue. I can add new shapes all day long.
BUT - once I let code call the stored procedure (step 4), then that is where problems arise.
Very specifically : da.Fill(dt)
I may manage 1 to 6 shape adds, but sooner or later, the event just stops firing.
(*update 8th Jan: Recordset size seems to affect the issue. When 1100 rows are returned each time, I manage to add around 6 shapes to my page. When 3 rows returned each time, I get to add up to 18 shapes before the events stop firing.
That tells me there is something not 'clean' about the way I am handling my data calls - but I cannot see what it is !!)
I am totally baffled as to why calling the stored procedure would interfere with my event handler !?!?
Especially without any error messages.
Has anyone any ideas whatsoever as to what I might be doing wrong ?
Or even, ideas around how to debug this in a better manner ?
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Globals.ThisAddIn.Application.MarkerEvent += new Visio.EApplication_MarkerEventEventHandler(Application_MarkerEvent);
}
private void Application_MarkerEvent(Visio.Application visapp, int SequenceNum, string ContextString)
{
if (ContextString.Contains("soln=myApplication") && ContextString.Contains("/cmd=DocCreated"))
{
SetDocEvents();
}
}
public void SetDocEvents()
{
Microsoft.Office.Interop.Visio.Document doc = Globals.ThisAddIn.Application.ActiveDocument;
// set event handler
try
{
doc.ShapeAdded += new Microsoft.Office.Interop.Visio.EDocument_ShapeAddedEventHandler(onShapeAdded);
}
catch (Exception err)
{
System.Diagnostics.Debug.WriteLine(err.Message);
throw;
}
}
private void onShapeAdded(Visio.Shape Shape)
{
Form_Entity fe = new Form_Entity(Shape.Text);
fe.ShowDialog();
fe.Dispose();
}
// ... other stuff
}
Form Code:
public partial class Form_Entity : Form
{
public Int32 eid { get { return m_id; } }
public string ename { get { return m_name; } }
public string eabbr { get { return m_abbr; } }
private Int32 m_id;
private string m_name;
private string m_abbr;
public Form_Entity()
{
InitializeComponent();
}
public Form_Entity(String search)
{
InitializeComponent();
txt_search.Text = search;
}
private void Cmd_Search_Click(object sender, EventArgs e)
{
String sample = txt_search.Text;
DataTable dt = new DataTable();
SqlConnection myConn = new SqlConnection("Server=xxxxxxx;Database=xxxxxxxx;Trusted_Connection=True;");
myConn.Open();
SqlCommand myCmd = new SqlCommand("[dbo].[mySearch]", myConn);
myCmd.Parameters.Add(new SqlParameter("#searchText", sample));
myCmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter da = new SqlDataAdapter(myCmd);
da.Fill(dt);
dataGridView1.DataSource = dt;
myCmd.Dispose();
myConn.Close();
}
}
** Files for this project
Visual Studio Solution
T-SQL to create sample table/data.procedure
Viso Template
ReadMe.txt
http://www.netshed.co.uk/temp/Vis_Sample.zip
I think the problem here is that you're not keeping the doc object in scope and Visio will stop reporting events for which there is no reference.
You can add a field (or property) as follows and then the reference and associated events should be maintained:
public partial class ThisAddIn
{
private Visio.Document _targetDoc = null;
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
Application.MarkerEvent += Application_MarkerEvent;
}
public void SetDocEvents()
{
_targetDoc = Globals.ThisAddIn.Application.ActiveDocument;
// set event handler
try
{
_targetDoc.ShapeAdded += onShapeAdded;
}
catch (Exception err)
{
System.Diagnostics.Debug.WriteLine(err.Message);
throw;
}
}
I need some help on adding a progress bar while the datagridview is being load it. I already have my code that loads the datagridview but as we know the loading takes time to finish loading depending of the records. So, I would like to add a progress bar loading and a label having a the count from 1 to 100 to complete.
I know there is a way using the background work handle event, but not sure how that make it work. I would like something simple but can do the work I need.
my code works great fills the datagridview as I want. but I need to add the progress bar while loading the datagridview.
change the code please review and let me know if I missed something.
So I made the changes and seems to work now, but there is an issue the progress bar does not work immediately takes a few seconds and then I can see the progress bar to move to 100%. Why it does that?
second issue after loading the datagridview the progress bar color goes away after I click the message MessageBox.Show("Successful Completion.");
here is a test image after my combo box select the value we want and display the datagridview
here I made the new changes to the program, but for some reason after I select the combobox the datagridview populates correctly but then I try again sometimes it fails and gives me this error
namespace DatagridViewProgressBar
{
public partial class Form1 : Form
{
//datagridview, bindingsource, data_apapter global objects variables
private DataGridView dataGridView = new DataGridView();
private BindingSource bindingSource = new BindingSource();
private SqlDataAdapter dataAdapter = new SqlDataAdapter();
DataTable dt = new DataTable();
//class objects
Databases lemars = new Databases();
Databases schuyler = new Databases();
Databases detroitlakeskc = new Databases();
public Form1()
{
InitializeComponent();
// To report progress from the background worker we set this property
dbWorker = new BackgroundWorker();
dbWorker.DoWork += new DoWorkEventHandler(dbWorker_DoWork);
dbWorker.ProgressChanged += new ProgressChangedEventHandler(dbWorker_ProgressChanged);
dbWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(dbWorker_RunWorkerCompleted);
dbWorker.WorkerReportsProgress = true;
dbWorker.WorkerSupportsCancellation = true;
}
private void btn_Exit_Click(object sender, EventArgs e)
{
this.Close();
}
private void comboBox_Database_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox_Database.SelectedItem.ToString() == "LeMars21St")
{
if (dbWorker.IsBusy != true)
{
dbWorker.RunWorkerAsync();
}
}
}
private void GetTableToDataGridView()
{
//prgBar_DataGridViewLoading
DatabaseColumns Obj = new DatabaseColumns();
String SqlcmdString = #"SELECT invoice, shipment, Project, invoiceDateTB, CreatedDate, typeName, exportedDate, statusName, total, import_status, Time_Completed, ERROR_DESCRIPTION FROM dbo.AllInvoicesInReadyStatus";
SqlDataReader reader;
int progress;
using (SqlConnection conn = new SqlConnection(lemars._LeMarsConnectionString))
{
reader = null;
SqlCommand Sqlcmd = new SqlCommand(SqlcmdString, conn);
conn.Open();
reader = Sqlcmd.ExecuteReader();
if (reader.HasRows)
{
try
{
dt.Load(reader);
for (int i = 0; i < dt.Rows.Count; i++)
{
Obj.Invoice = dt.Rows[i]["invoice"].ToString();
Obj.Shipment = dt.Rows[i]["shipment"].ToString();
Obj.Project = dt.Rows[i]["Project"].ToString();
Obj.InvoiceDateTB = Convert.ToDateTime(dt.Rows[i]["invoiceDateTB"]);
Obj.CreatedDate = Convert.ToDateTime(dt.Rows[i]["CreatedDate"]);
Obj.TypeName = dt.Rows[i]["typeName"].ToString();
Obj.ExportedDate = Convert.ToDateTime(dt.Rows[i]["exportedDate"]);
Obj.StatusName = dt.Rows[i]["statusName"].ToString();
Obj.Total = Convert.ToDecimal(dt.Rows[i]["total"]);
Obj.ImportStatus = dt.Rows[i]["import_status"].ToString();
if (!Convert.IsDBNull(dt.Rows[i]["Time_Completed"]))
{
Obj.TimeCompleted = Convert.ToDateTime(dt.Rows[i]["Time_Completed"]);
}
Obj.ErrorDescription = dt.Rows[i]["ERROR_DESCRIPTION"].ToString();
progress = i * 100 / dt.Rows.Count;
dbWorker.ReportProgress(progress);
Thread.Sleep(500);
}
}
finally
{
conn.Close();
}
}
}
}
private void dbWorker_DoWork(object sender, DoWorkEventArgs e)
{
GetTableToDataGridView();
dbWorker.ReportProgress(100);
}
private void dbWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar_GetTasks.Value = e.ProgressPercentage;
// eg: Set your label text to the current value of the progress bar
lbl_PercentageCount.Text = (progressBar_GetTasks.Value.ToString() + "%");
}
private void dbWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
dataGridView_ShowAllData.DataSource = dt;
if (e.Cancelled)
{
MessageBox.Show("Process Cancelled.");
}
else if (e.Error != null)
{
MessageBox.Show("Error occurred: " + e.Error.Message);
}
else
{
MessageBox.Show("Successful Completion.");
}
//progressBar_GetTasks.Value = 0;
}
private void btn_CancelOperation_Click(object sender, EventArgs e)
{
if (dbWorker.IsBusy)
{
dbWorker.CancelAsync();
}
}
}
}
Firstly, SELECT * is a bad idea, regardless of how many columns you have, or need. Explicitly stating which columns you want opens up possibilities for using indices and reduces maintainability issues with your code.
Secondly, your main question. I have done something similar recently, and can give some pointers. I am going not going to immediately apply this to your code-snippet, because I think that will complicate things.
EDIT
For thread safety purposes, the code inside dbWorker_DoWork() should not try to access form elements which were created in the main thread. There are obviously ways around this, and once you get to the dbWorker_RunWorkerCompleted() function, you are back in the main thread and you have full access to the necessary form elements.
END EDIT
1) Your form. You need a backgroundworker to do the work, as well as three callback functions to handle what is going on. A progress bar is assumed to be on the form as well (System.Windows.Forms.ProgressBar).
...
using System.ComponentModel;
...
public partial class YourForm : Form
{
BackgroundWorker dbWorker;
...
public YourForm()
{
...
dbWorker = new BackgroundWorker();
dbWorker.DoWork += new DoWorkEventHandler(dbWorker_DoWork);
dbWorker.ProgressChanged += new ProgressChangedEventHandler(dbWorker_ProgressChanged);
dbWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(dbWorker_RunWorkerCompleted);
dbWorker.WorkerReportsProgress = true;
dbWorker.WorkerSupportsCancellation = true;
...
}
...
public void dbWorker_DoWork(object sender, DoWorkEventArgs e)
{
// This is where you put your GetTableToDataGridView() code.
// Add a line inside the loop, for reporting on progress:
// dbWorker.ReportProgress((int)(currentIteration * 100 / totalIterations));
// At the end of the process, set the progress bar to 100% (optional)
dbWorker.ReportProgress(100);
}
public void dbWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
// Here you can also do other things, which depend on the progress.
// eg: Set your label text to the current value of the progress bar.
}
public void dbWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("Process Cancelled.");
}
else if (e.Error != null)
{
MessageBox.Show("Error occurred: "+ e.Error.Message.");
}
else
{
MessageBox.Show("Successful Completion.");
}
progressBar.Value = 0;
}
}
2) Starting the process (this could go in your form load function, or if you start the process manually, in an OnClick event):
...
if (!dbWorker.IsBusy)
{
dbWorker.RunWorkerAsync();
}
...
3) Cancelling the process (if you have a button for cancelling, then this would go in the OnClick event code):
...
if (dbWorker.IsBusy)
{
dbWorker.CancelAsync();
}
...
A couple obvious problems:
Since you have the connection inside a using block, the explicit conn.Close() is unnecessary. The using mechanism will automatically close it even if an exception occurs. In fact, unless you have a need to handle exceptions at this level, you can remove the try block altogether.
Never use SELECT *. You are retrieving a ton of data you don't need and this is probably why it's going slow in the first place.
To the meat of your question: BackgroundWorker is your friend!
I'm more familiar with doing data binding with WPF, so you'll need to do some legwork on your own. But the basic idea is that you have your progressbar's visibility bound to a variable, then you update that variable:
when you launch the thread, to make it visible (using the IsIndeterminate property is useful since there's no real way to do percentages for a SQL query)
when the thread finishes, to hide the progress bar.
In this way, you get the animated progress bar while the query is running.
You can set the mouse cursor to busy/arrow in the same way.
I'm having some trouble with my code regarding a Windows Form Application.
I have a form, that requires the user to enter some data for a new record in the database. I can successfully create a new "Order" in my Database. After I do that, I want to open a form that shows the user all details of the order. Therefore I take an already existing window and want the bindingSource to jump to a certain position.
My Code is as follows:
The "newOrder form"
//do stuff for the creation
//open a customerDetails window with the new entry
//resolve index of the new item
int newIndex = ordersBindingSource.Find("OrderID", newOrderID);
//create a new window and pass the index in the constructor
Order orderDetailsView = new Order(newIndex);
//show the new window and dispose the old one
orderDetailsView.Show();
this.Close();
this.Dispose();
The "Order form" constructor i'm calling:
public Order(int newIndex)
{
//initialize
InitializeComponent();
//set index and repaint
this.ordersBindingSource.Position = newIndex;
this.ordersBindingSource.ResetCurrentItem();
}
This is simply not working and I get the first entry of the dataset.
What am I doing wrong?
Where do you initialize you BindingSource from "Order form"? Make sure your newIndex <= ordersBindingSource.Count().
Try this:
//Form Order
int currentOrder = 0;
//constructor
public Order(int newIndex)
{
//initialize your variable here
currentOrder = newIndex;
//initialize
InitializeComponent();
}
//Create a void method that sets the BindingSource position
void SetupForm()
{
ordersBindingSource.Position = currentOrder;
}
// Override the OnLoad event and call the SetupForm() method here
protected override OnLoad(EventArgs e)
{
SetupForm();
}
I am working in Visual Studio 2010 .NET 4.0 in C# using WinForms.
The form has a single DataGridView that is data bound to a DataSet.
The DataSet is being populated from a Thread that is processing data being read from a ConcurrentQueue.
The code is also using a semaphore to serialize access to the DataSet as it can be accessed from the "worker" Thread and the UI main thread ( when updating the DataGridView ).
The UI/Form has a System.Windows.Forms.Timer that fires every 1/4 second to call a function that causes the DataGridView to be updated with the current contents of the DataSet.
The code runs correctly until the point at which the DataGridView data scrolls and the scroll bar becomes visible. At this point the entire form becomes unresponsive - title caption says "Program Not Responding".
The interesting thing is that this DOESN'T happen while running under the debugger. Only when the program is deployed. And no exceptions are ever raised.
I've tried using both Invoke and BeginInvoke with no change in behavior.
Questions:
1- What might be wrong with my code below?
2- Since I can't observe this behavior while running under the debugger, how do I find out what is causing the problem?
The wall of code is provided below. I know it's a lot of code, but I cut out as much as I could.
// deleted using statements for brevity
namespace namcom
{
public partial class wndXMLtrans : Form
{
private DataSet dsRecords = new DataSet();
private Thread threadConsumeXML = null;
private static Semaphore ResourceLock;
public wndXMLtrans(String ip)
{
InitializeComponent();
ResourceLock = new Semaphore(1, 1);
curSize = this.Size;
m_ip = ip;
}
private Boolean AddRowToDataSet(String[] columns, String xml)
{
Boolean retCode = true;
String value = String.Empty;
Int64 id = -1;
DataRow row;
ResourceLock.WaitOne();
row = dsRecords.Tables[0].NewRow();
// prepare row code omitted - brevity
// add new data row to DataSet
dsRecords.Tables[0].Rows.Add(row);
ResourceLock.Release();
// SQL inserts into DB removed - brevity
return (retCode);
}
private Boolean HandleSingleXMLMessage(String[] columns, String xml, out String exceptionMessage)
{
Boolean boolRet = true;
exceptionMessage = String.Empty;
// store data in dataset and database
if ( closeRequested == false )
{
AddRowToDataSet(columns, xml);
}
return (boolRet);
}
private Boolean StoreG2SMessages(String message)
{
// code removed - brevity
// removed code just parses out string and in a loop calls HandleSingleXMLMessage
// until all XML in message have been processed
HandleSingleXMLMessage(columns,xml, out exceptionMessage) // call in loop
return (ret);
}
// pull XML out of mainwnd.msgQueue and update database
private void G2SParseThread()
{
String Data;
String exceptionMsg = String.Empty;
while ( /* thread is to be active - code removed for brevity */)
{
Data = String.Empty;
if (mainwnd.msgQueue.TryDequeue(out Data) == true)
{
this.StoreG2SMessages(Data);
}
Thread.Sleep(20);
}
// thread ended cleanup code removed - brevity
}
private void StartThreads()
{
// start XML packet processing thread
threadConsumeXML = new Thread(G2SParseThread);
threadConsumeXML.SetApartmentState(ApartmentState.STA);
threadConsumeXML.Start();
threadMonitor = new Thread(() => threadHandleStatusMsg(ref mainwnd.statusQueue));
threadMonitor.Start();
}
private void wndXMLtrans_Shown(object sender, EventArgs e)
{
// remove SQL code - brevity
// fill dsRecords ( DataSet )
try
{
var adapter = new SQLiteDataAdapter(selectSQL, dbConnection);
adapter.Fill(dsRecords);
dataGrid.DataSource = dsRecords.Tables[0].DefaultView;
adapter.Dispose();
}
catch { } // catch code removed - brevity
StartThreads();
}
private void gridUpdateTimer_Tick(object sender, EventArgs e)
{
ResourceLock.WaitOne();
try
{
if (dataGrid != null && numRows < dsRecords.Tables[0].Rows.Count)
{
if (dataGrid.RowCount > 0)
{
dataGrid.FirstDisplayedScrollingRowIndex = dataGrid.RowCount - 1;
}
dataGrid.Refresh();
numRows = dsRecords.Tables[0].Rows.Count;
}
}
catch { }
ResourceLock.Release();
}
}
}
EDIT: Hans provided the answer which is that you can't update a bound DataSet from a worker thread. So I was able to fix the problem by adding the following:
These are called by delegate functions using Invoke. Unbind called before DataSet update and Bind after DataSet is updated. This works, but causes the DataGridView to appear to flicker or redraw multiple times. Any way to remedy this?
public void UnbindDataGridView(DataGridView grid)
{
grid.DataSource = null;
}
public void BindDataGridView(DataGridView grid)
{
grid.DataSource = dsRecords.Tables[0].DefaultView;
}
I have a tableadapter and I want to do something when the RowUpdated event is fired. I can't figure out where to put the code to add the handler to the event.
public partial class MyTableAdapter
{
void OnRowUpdated(object sender, System.Data.Odbc.OdbcRowUpdatedEventArgs e)
{
}
}
How do I get the code below to run when the TableAdapter is created?
Adapter.RowUpdated +=
new System.Data.Odbc.OdbcRowUpdatedEventHandler(OnRowUpdated);
I resolved this in 2 stages.
a. add partial class and extend table adapter
b. call a method in the beginning before using this adapter (say in main form after you create instance of it).
The code below is to resolve my particular issue with SQL CE to be able to update IDs on the table. However you can use the extended method to wrap the RowUpdated event and expose it to other classes (ie MyRowUpdated)
The extension
public partial class ScannedItemsTableAdapter
{
public void InitEvents()
{
this._adapter.RowUpdated += _adapter_RowUpdated;
}
void _adapter_RowUpdated(object sender, SqlCeRowUpdatedEventArgs e)
{
if (e.Status == UpdateStatus.Continue &&
e.StatementType == StatementType.Insert)
{
var pk = e.Row.Table.PrimaryKey;
pk[0].ReadOnly = false;
SqlCeCommand cmd = new SqlCeCommand("SELECT ##IDENTITY",
e.Command.Connection, e.Command.Transaction);
object id = (decimal)cmd.ExecuteScalar();
e.Row[pk[0]] = Convert.ToInt32(id);
e.Row.AcceptChanges();
}
}
}
The call in the main form:
tableAdapter = new ScannedItemsTableAdapter();
tableAdapter.Fill(ds.ScannedItems);
tableAdapter.InitEvents();
it has been a while since i last used this stuff, but if i remember correctly you should be able to override EndInit() in your partial class and add init code like adding your event handler there ...
I have a possible alternative.
Whilst one can subscribe to the tableadapters underlying adapter in the tableadapter partial class I find you cannot easily initialise it without having to remember to call an initialisation of your own.
However I found that I can subscribe to a tableadapters.adapter.rowupdated event directly from within code using the tableadpater as below
AddHandler Me.TA_Case_Transactions.Adapter.RowUpdated, AddressOf Transaction_Row_Written
Private Sub Transaction_Row_Written(p_Sender As Object, p_E As SqlRowUpdatedEventArgs)
beep
End Sub
"The beep is so I can add a breakpoint for testing"
Since I would have to subscribe to the event anyway I think this is more intuitive and doesnt require any "remembering"
EDIT: None of the steps suggested below are useful, apparently. I'm keeping this answer here purely so that anyone else trying to answer it doesn't come up with the same suggestions.
Are you creating the TableAdapter in the designer? If so, can you not just click on the Events part of the properties page, and type "OnRowUpdated" into the RowUpdated entry?
If you're creating the adapter explicitly in your code, just add the call yourself.
Alternatively, does your partial class have a constructor which is being called? Again, if so, you can add the call there.
EDIT: Okay, so presumably this is being used in a particular page or form - can you add it after the InitializeComponent method of that page/form?
http://windowsclient.net/learn/video.aspx?v=14625 if you look at this video you can see, that you just double click on something in the dataset designer and the event gets generated and wired up for you....but this works only for VB programmers :)) sux
Override endinitin your C# class and fire event handler from there
public override void EndInit()
{
base.EndInit();
Adapter.RowUpdated +=
new System.Data.Odbc.OdbcRowUpdatedEventHandler(OnRowUpdated);
}
void OnRowUpdated(object sender, System.Data.Odbc.OdbcRowUpdatedEventArgs e)
{
}
Hope that helps................
// Assumes that connection is a valid SqlConnection object.
SqlDataAdapter custAdapter = new SqlDataAdapter(
"SELECT CustomerID, CompanyName FROM Customers", connection);
// Add handlers.
custAdapter.RowUpdating += new SqlRowUpdatingEventHandler(OnRowUpdating);
custAdapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);
// Set DataAdapter command properties, fill DataSet, modify DataSet.
custAdapter.Update(custDS, "Customers");
// Remove handlers.
custAdapter.RowUpdating -= new SqlRowUpdatingEventHandler(OnRowUpdating);
custAdapter.RowUpdated -= new SqlRowUpdatedEventHandler(OnRowUpdated);
protected static void OnRowUpdating(
object sender, SqlRowUpdatingEventArgs args)
{
if (args.StatementType == StatementType.Delete)
{
System.IO.TextWriter tw = System.IO.File.AppendText("Deletes.log");
tw.WriteLine(
"{0}: Customer {1} Deleted.", DateTime.Now,
args.Row["CustomerID", DataRowVersion.Original]);
tw.Close();
}
}
protected static void OnRowUpdated(
object sender, SqlRowUpdatedEventArgs args)
{
if (args.Status == UpdateStatus.ErrorsOccurred)
{
args.Row.RowError = args.Errors.Message;
args.Status = UpdateStatus.SkipCurrentRow;
}
}
Here's an easy way to do it, using the Designer. Right-click on the dataset (the .xsd file) and select View Code. Just like with a form, it's not going to open the designer code. It's going to open your code. Here's your code:
namespace TimeTrack.TimeTrackDataSetTableAdapters
{
public partial class TimeSlipsTableAdapter
{
public void CustomSetup()
{
Adapter.RowUpdated += Adapter_RowUpdated;
}
private void Adapter_RowUpdated(object sender, OleDbRowUpdatedEventArgs e)
{
// whatever you want to do when RowUpdated fires
}
}
}
In your form, don't forget to
public TimeEntry()
{
InitializeComponent();
timeSlipsTableAdapter.CustomSetup();