LinqDataSource Result becoming Null before OnSelected was called - c#

I'm assigning list to e.Result in LinqDataSource OnSelecting Event result contains 5 rows in it. On Execution of OnSelected Event the result doesn't contains any rows . Why is this happening ?... I'm I missing any thing? Here is my code of Selecting event
protected new void OnDataSourceSelecting(object sender, LinqDataSourceSelectEventArgs e)
{
int AdminAccessID = 1;
List<VIEW_ManagerOwned> result = _dataContext.VIEW__ManagerOwneds.Where(ma => (ma.LastName == "West") & (ma.FirstName == "Stacie") & ma.AdminUserAccessID == Convert.ToInt32(AdminAccessID) & ma.SecurityUserID == Convert.ToInt32(1766)).ToList();
e.Result = result;
}
protected void ListLinqDataSource_Selected(object sender, LinqDataSourceStatusEventArgs e)
{
}

I'm no expert on LinqDataSourceSelectEventArgs but my suspicion is that you are expecting the value of e.Result to be passed to the next event hander, in this case ListLinqDataSource_Selected but it is likely that this handler is getting a different set of event args which would explain why you are not getting your list in e.Result from the Selecting handler.
One potential solution is to cache the value of result in a class level variable so that you can access it after you have it populated.
Again I could be wrong about the event handler not sending the previous event args to the next handler, but I believe this is the most likely cause of the issue.
ADDITIONS:
Simply regarding your code, are you sure you want to use bitwise & in your LINQ statement and not a conditional &&? There are times to use each but you may get undesired results using the & operator here. Also, consider formatting your code with less horizontal space and more vertical space, it is difficult to read when you have such a long line of code. Also generally you do not want to assign to event args the way you are doing it, as you can see it is not producing the results you are expecting. (no pun intended on results).

Related

Detect column reordering - ColumnDisplayIndexChanged raises multiple times

I am working on a .net 4.6.1 C# winforms project that has a datagridview where users can change the order of columns.
I would like to store the new order in a db table, but have trouble finding the right event for detecting when a user changed the order of the columns.
After searching here, I was pointed to the DataGridView.ColumnDisplayIndexChanged event in this thread. But that one does not solve my issue. (it only gives a solution for multiple events when you fill the datagrid view, but that is answered easily by adding the handler after setting the datasource)
That sort of works, but gets fired multiple times when a user changes the order of columns (it f.e. looks like when changing columns A,B,C,D to D,A,B,C the event gets fired 3 times (probably for A,B,D,C - A,D,B,C - D,A,B,C)
I am having a hard time finding out how I can detect if the event is the final one (since I don't want to store all these new orders, only the final one)
My questions are:
Is this event the 'best' one to use for my case?
If so, how can I detect the final ColumnDisplayIndexChanged event (D,A,B,C)?
When you reorder columns, ColumnDisplayIndexChanged will raise for all the columns which their display index has been changed. For example if you move colum A to the position after C, the event will raise for all those three columns.
There is a solution to catch the last one. DataGridViewColumn has an internal property called DisplayIndexHasChanged which is true if the event should be fired for the column. The private method which raise the event, looks into list of the columns and for each column if that property is true, first sets it to false, then raises the event. You can read internal implementations here.
You can check if there is no column having DisplayIndexHasChanged with true value, you can say it's the last event in the sequence:
private void dgv_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e)
{
var g = (DataGridView)sender;
var property = typeof(DataGridViewColumn).GetProperty("DisplayIndexHasChanged",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (g.Columns.Cast<DataGridViewColumn>().Any(x => (bool)property.GetValue(x)))
return;
else
MessageBox.Show("Changed");
}
Just keep in mind, you should disable capturing that event when you add columns:
private void f_Load(object sender, EventArgs e)
{
LoadData();
}
void LoadData()
{
dgv.ColumnDisplayIndexChanged -= dgv_ColumnDisplayIndexChanged;
dgv.DataSource = null;
var dt = new DataTable();
dt.Columns.Add("A");
dt.Columns.Add("B");
dt.Columns.Add("C");
dgv.DataSource = dt;
dgv.ColumnDisplayIndexChanged += dgv_ColumnDisplayIndexChanged;
}
My suggestion would be not to do any custom logic to find out if its the last one or something along those lines. The best approach would be to save after each event but you can debounce it.
Using a debounce approach you can cancel the old event if the new event is fired right after depending on some amount of time you wish to allow inbetween calls.
Ex: write to storage only if there is no new event after lets say 1 second or 5 seconds depending on what is accepteable for your application
Say we decide to save with a debounce of 1 second
First event occurs you trigger the action which has 1 second to execute
If another event is triggered the old action is ignored and the new action now has 1 second to execute and so on for other sequential actions
public static Action Debounce(this Action func, int milliseconds = 300)
{
var last = 0;
return arg =>
{
var current = Interlocked.Increment(ref last);
Task.Delay(milliseconds).ContinueWith(task =>
{
if (current == last) func(arg);
task.Dispose();
});
};
}
Assuming the following action below for saving your data
Action a = (arg) =>
{
save my data here
};
first assign the debouncer to your action
var debouncedWrapper = a.Debounce(1000); //1 sec debounce
Then you can use it as follows
public void datagridchangeevent(object sender, Event e)
{
debouncedWrapper()
}
This will ignore sequential calls and the aciton will be executed only if nothing is called for one second

checking items in a list and take a decision

I want to build a program in Windows forms where the user can create a pizza by pressing buttons.
My problem is that when the user presses an ingredient more than once, the list will just increment. I tried various methods but they don't seem to work.
I have seen a solution using a for loop checking individual items in the list however I will have to implement that 19 times which is not really efficient (once for every button)
string check = "Thin Base";
if (My_Pizza.Contains(check))
{
My_Pizza.Items.Remove("ThinBase");
My_Pizza.Items.Add("Thin Base");
}
You have "ThinBase" and "Thin Base". Not the same thing.
Also, if an item is already in the list, you don't need to do anything. Simply invert your check
string check = "Thin Base";
if (!My_Pizza.Items.Contains(check))
{
My_Pizza.Items.Add(check);
}
General idea is not to hardcode string values, but create reusable method that will do what you need: check if certain value is already in the list and if it not, add this item to list. This will help you to avoid duplicate code.
In the button event handler you simply call this method and provide string value as parameter. I'm not sure how exactly you handle button clicks, but I would suggest creating single reusable method once again and acquire string value from button.Text property.
Here is code sample for you to demonstrate the idea.
private void OnButtonClick(object sender, EventArgs e)
{
Button clickedButton = (Button) sender;
if (clickedButton != null)
{
string buttonContent = clickedButton.Text;
CheckAndAdd(buttonContent);
}
}
private void CheckAndAdd(string valueToCheck)
{
if (!My_Pizza.Items.Contains(valueToCheck))
{
My_Pizza.Items.Add(valueToCheck);
}
}

Use Rx native instead of Event

I want to convert event code using e.cancel to RX code.
public void CounsumerMethod(object sender, EventArgs e) {
if (x == 0) {
e.Cancel = false;
}
}
You cannot effectively convert this code to proper Rx code, because this is not a Pure event - the caller of the event (i.e. the framework code) will immediately check the "Cancel" value, whereas Rx will not guarantee that it will set that variable in-context. You'll make an Observable that works sometimes and mysteriously fails in other scenarios.

What is the best way to mask an UI Event?

I am working with a DataGridView, and I use the CellValueChanged event.
I dont want this event to be triggered when I change a cell value by the code. However, I want it to be triggered when the user edits it.
That's why I enclose my cell value change operations with the following code :
void changeCellOperation()
{
dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged);
...
cell.Value = myNewCellValue
...
dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged);
}
I ended to have several differents functions where my DataGridView cells are updated this way.
Because these functions are called from different places and can be nested, I cannot afford to keep this code as is to avoid event unwanted event reactivation.
So I ended up this way :
int valueChangedEventMask = 0;
void changeCellOperation()
{
valueChangedEventMask++;
...
cell.Value = myNewCellValue
...
valueChangedEventMask--;
}
void dgv_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (valueChangedEventMask > 0)
return
...
}
This works fine. Also when the calls are nested, including inside the event itself.
But the CellValueChanged event is now fired too many times for no reasons.
Because I often have to cope with this pattern, I am looking for a solution that can be applicable generally for Events in UIs, not only the DataGridView.
So my question is:
What is the best tip to mask UI Events correctly and avoid unnecessary Events fires ?
CellValueChanged is not an UI event, but a property changed event. That means you can not use it to distinguish user input from programmatic change. You can always use subscriber/unsucscribe or flag+/- or BeginEdit/EndEdit-similar technique, but maybe you have to find another (better) approach. To example, in case of checkbox you can use Click event instead of Changed, because (surprise!) it will tell you when the user click it and otherwise safely change value of Checked programmatically.
In case of DataGridView easiest would be to use Changed with some flag (which will be set when edit begins and reset when ends - see, CellBeginEdit/CellEndEdit ).
You could use CellEndEdit instead of CellValueChange. I don't know what your method dgv_CellValueChanged does, just be careful that CellEndEdit is fired every time you exit the edit mode for the cell, even if its value has not been changed. This means that you have to keep trace of the current values of your cells if you don't want the method to be executed when the value doesn't change.
I would avoid events related with the mouse such as CellClick because your users could use just the keyboard.
Anyway I usually avoid this kind of problems by separating the logic from the user interface, i.e. I write a separate class which is bound to the form. Take a look at MVVM (you can implement your own version in WinForms if you want) or the good old MVC.
I ended up mixing both solutions in a very simple one. I use a counter and I only hook/unhook the events I want to mask.
EventMask valueChangedEventMask;
// In the class constructor
valueChangedEventMask = new EventMask(
() => { dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged); },
() => { dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged); }
);
// The value change operation I want to hide from the event
void changeCellOperation()
{
valueChangedEventMask.Push();
...
cell.Value = myNewCellValue
...
valueChangedEventMask.Pop();
}
// The class
public class EventMask
{
Action hook;
Action unHook;
int count = 0;
public EventMask(Action hook, Action unHook)
{
this.hook = hook;
this.unHook = unHook;
}
public void Push()
{
count++;
if (count == 1)
unHook();
}
public void Pop()
{
count--;
if (count == 0)
hook();
}
}

Is is possible to know for sure if a WebBrowser is navigating or not?

I'm trying to find a way for my program to know when a WebBrowser is navigating and when is not. This is because the program will interact with the loaded document via JavaScript that will be injected in the document. I don't have any other way to know when it starts navigating than handling the Navigating event since is not my program but the user who will navigate by interacting with the document. But then, when DocumentCompleted occurs doesn't necessarily mean that it have finished navigating. I've been googling a lot and found two pseudo-solutions:
Check for WebBrowser's ReadyState property in the DocumentCompleted event. The problem with this is that if not the document but a frame in the document loads, the ReadyState will be Completed even if the main document is not completed.
To prevent this, they advise to see if the Url parameter passed to DocumentCompleted matches the Url of the WebBrowser. This way I would know that DocumentCompleted is not being invoked by some other frame in the document.
The problem with 2 is that, as I said, the only way I have to know when a page is navigating is by handling the Navigating (or Navigated) event. So if, for instance, I'm in Google Maps and click Search, Navigating will be called, but just a frame is navigating; not the whole page (on the specific Google case, I could use the TargetFrameName property of WebBrowserNavigatingEventArgs to check if it's a frame the one that is navigating, but frames doesn't always have names). So after that, DocumentCompleted will be called, but not with the same Url as my WebBrowsers Url property because it was just a frame the one that navigated, so my program would thing that it's still navigating, forever.
Adding up calls to Navigating and subtracting calls to DocumentCompleted wont work either. They are not always the same. I haven't find a solution to this problem for months already; I've been using solutions 1 and 2 and hoping they will work for most cases. My plan was to use a timer in case some web page has errors or something but I don't think Google Maps has any errors. I could still use it but the only uglier solution would be to burn up my PC.
Edit: So far, this is the closest I've got to a solution:
partial class SafeWebBrowser
{
private class SafeNavigationManager : INotifyPropertyChanged
{
private SafeWebBrowser Parent;
private bool _IsSafeNavigating = false;
private int AccumulatedNavigations = 0;
private bool NavigatingCalled = false;
public event PropertyChangedEventHandler PropertyChanged;
public bool IsSafeNavigating
{
get { return _IsSafeNavigating; }
private set { SetIsSafeNavigating(value); }
}
public SafeNavigationManager(SafeWebBrowser parent)
{
Parent = parent;
}
private void SetIsSafeNavigating(bool value)
{
if (_IsSafeNavigating != value)
{
_IsSafeNavigating = value;
OnPropertyChanged(new PropertyChangedEventArgs("IsSafeNavigating"));
}
}
private void UpdateIsSafeNavigating()
{
IsSafeNavigating = (AccumulatedNavigations != 0) || (NavigatingCalled == true);
}
private bool IsMainFrameCompleted(WebBrowserDocumentCompletedEventArgs e)
{
return Parent.ReadyState == WebBrowserReadyState.Complete && e.Url == Parent.Url;
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null) PropertyChanged(this, e);
}
public void OnNavigating(WebBrowserNavigatingEventArgs e)
{
if (!e.Cancel) NavigatingCalled = true;
UpdateIsSafeNavigating();
}
public void OnNavigated(WebBrowserNavigatedEventArgs e)
{
NavigatingCalled = false;
AccumulatedNavigations++;
UpdateIsSafeNavigating();
}
public void OnDocumentCompleted(WebBrowserDocumentCompletedEventArgs e)
{
NavigatingCalled = false;
AccumulatedNavigations--;
if (AccumulatedNavigations < 0) AccumulatedNavigations = 0;
if (IsMainFrameCompleted(e)) AccumulatedNavigations = 0;
UpdateIsSafeNavigating();
}
}
}
SafeWebBrowser inherits WebBrowser. The methods OnNavigating, OnNavigated and OnDocumentCompleted are called on the corresponding WebBrowser overridden methods. The property IsSafeNavigating is the one that would let me know if it's navigating or not.
Waiting till the document has loaded is a difficult problem, but you want to continually check for .ReadyState and .Busy (dont forget that). I will give you some general information you will need, then I will answer your specific question at the end.
BTW, NC = NavigateComplete and DC = DocumentComplete.
Also, if the page you are waiting for has frames, you need to get a ref to them and check their .busy and .readystate as well, and if the frames are nested, the nested frames .readystate and .busy as well, so you need to write a function that recursively retreives those references.
Now, regardless of how many frames it has, first fired NC event is always the top document, and last fired DC event is always that of the top (parent) document as well.
So you should check to see if its the first call and the pDisp Is WebBrowser1.object (literally thats what you type in your if statement) then you know its the NC for top level document, then you wait for this same object to appear in a DC event, so save the pDisp to a global variable, and wait until a DC is run and that DC's pDisp is equal to the Global pDisp you've saved during the first NC event (as in, the pDisp that you saved globally in the first NC event that fired). So once you know that pDisp was returned in a DC, you know the entire document is finished loading.
This will improve your currect method, however, to make it more fool proof, you need to do the frames checking as well, since even if you did all of the above, it's over 90% good but not 100% fool proof, need to do more for this.
In order to do successful NC/DC counting in a meaningful way (it is possible, trust me) you need to save the pDisp of each NC in an array or a collection, if and only if it doesn't already exist in that array/collection. The key to making this work is checking for the duplicate NC pDisp, and not adding it if it exists. Because what happens is, NC fires with a particular URL, then a server-side redirect or URL change occurs and when this happens, the NC is fired again, BUT it happens with the same pDisp object that was used for the old URL. So the same pDisp object is sent to the second NC event now occuring for the second time with a new URL but still all being done with the exact same pDisp object.
Now, because you have a count of all unique NC pDisp objects, you can (one by one) remove them as each DC event occurs, by doing the typical If pDisp Is pDispArray(i) Then (this is in VB) comparison wrapped in a For Loop, and for each one taken off, your array count will get closer to 0. This is the accurate way to do it, however, this alone is not enough, as another NC/DC pair can occur after your count reaches 0. Also, you got to remember to do the exact same For Loop pDisp checking in the NavigateError event as you do in the DC event, because when a navigation error occurs, a NavigateError event is fired INSTEAD of the DC event.
I know this was a lot to take, but it took me years of having to deal with this dreaded control to figure these things out, I've got other code & methods if you need, but some of the stuff I mentioned here in relation to WB Navigation being truely ready, haven't been published online before, so I really hope you find them useful and let me know how you go. Also, if you want/need clarification on some of this let me know, unfortunately, the above isn't everything if you want to be 100% sure that the webpage is done loading, cheers.
PS: Also, forgot to mention, relying on URL's to do any sort of counting is inaccurate and a very bad idea because several frames can have the same URL - as an example, the www.microsoft.com website does this, there are like 3 frames or so calling MS's main site that you see in the address bar. Don't use URL's for any counting method.
First I've converted the document to XML and then used my magic method:
nodeXML = HtmlToXml.ConvertToXmlDocument((IHTMLDocument2)htmlDoc.DomDocument);
if (ExitWait(false))
return false;
conversion:
public static XmlNode ConvertToXmlDocument(IHTMLDocument2 doc2)
{
XmlDocument xmlDoc = new XmlDocument();
IHTMLDOMNode htmlNodeHTML = null;
XmlNode xmlNodeHTML = null;
try
{
htmlNodeHTML = (IHTMLDOMNode)((IHTMLDocument3)doc2).documentElement;
xmlDoc.AppendChild(xmlDoc.CreateXmlDeclaration("1.0", ""/*((IHTMLDocument2)htmlDoc.DomDocument).charset*/, ""));
xmlNodeHTML = xmlDoc.CreateElement("html"); // create root node
xmlDoc.AppendChild(xmlNodeHTML);
CopyNodes(xmlDoc, xmlNodeHTML, htmlNodeHTML);
}
catch (Exception err)
{
Utils.WriteLog(err, "Html2Xml.ConvertToXmlDocument");
}
magic method:
private bool ExitWait(bool bDelay)
{
if (m_bStopped)
return true;
if (bDelay)
{
DateTime now = DateTime.Now;
DateTime later = DateTime.Now;
TimeSpan difT = (later - now);
while (difT.TotalMilliseconds < MainDef.IE_PARSER_DELAY)
{
Application.DoEvents();
System.Threading.Thread.Sleep(10);
later = DateTime.Now;
difT = later - now;
if (m_bStopped)
return true;
}
}
return m_bStopped;
}
where m_bStopped is false by default, IE_PARSER_DELAY is a timeout value.
I hope this helps.

Categories

Resources