Variable changing, but I don't know where or why - c#

I am working on implementing a undo function for my WPF datagrid, however I a running into problems. I am sure it is just a misunderstanding of the C# language and its syntax, but I don't know what the problem is. When the cell edit is ending, I obtain the previous item and display an item value (Z) (for debugging purposes). It contains the item held BEFORE the edit (which is what I want). But when I try to 'undo' it, I display the SAME value (Z) again but it has changed to the current value of that item in the datagrid.
CellEditEnding Handler
private void toolGrid_CellEditEnding(object sender, DataGridVellEditEndingEventArgs e)
{
undoTool = toolsList[selectedToolNdx];
MessageBox.Show(undoTool.Z.ToString());
}
KeyDown Handler
private void toolGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
{
if (e.Key == Key.Z)
{
MessageBox.Show(undoTool.Z.ToString());
e.Handled = true;
}
}
}
So for instance, I can change the z-value from 3 to 5, and the celleditending event fires and a messagebox shows the value '3' (so far so good). However, I then undo (CTRL-Z) and a messagebox now shows the value '5' even though it should still be 3. Why is the undoTool changing (and why does it seem to be changing by itself)?

The problem is that while 'saving' the old value, it simple saves itself as a reference inside your variable 'undoTool'. Because its a reference it doesn't do anything else then referencing to its base object which is the value you are editing. This means whenever you change the base object all references to it will return the same value as the one you've changed it into.
You should tell specifically tell .NET to create a new object, thus using the new keyword, and then store your variable inside it.

Since you do not know where or how then you must use a debugger or a a thread which has access to the memory space of the variable. In the case of the debugger Add a watch and break on access / read write in the latter use a thread and sleep until something != somethingElse == true; then perform logic after to debug. Depending on the level of access you have in the system you may be able to also ascertain the function call and the caller using some constants but lesser knowns.. How to get the name of the current method from code
or
[CallerMemberName] string memberName = "",
[CallerFilePath] string sourceFilePath = "",
[CallerLineNumber] int sourceLineNumber = 0)
You can also use a the Watch window

Related

C# How can I pass a UI control reference to a class module "generically" and access it' s properties?

I have done a fair amount of C# programming, but it's been all very small stuff, and coming from a C background (not C++) in the embedded space I haven't fully embraced the OO approach, and I'm happy to say I'm trying to change that.
I'm rewriting some serial comm code I've used over and over into a Class Module that I can just drop into future projects and instantiate. I have everything working except one thing: a logging function where I write the com characters to a textbox and the indices from a ring buffer into labels. I can make that all work, actually, but I was hoping to "generalize" more and pass one of any number of things with a ".Text" property for the ring buffer indices (for example, a Label, a TextBox, or a ToolStripStatusLabel).
Here’s the example, say I have a form with a text box, a label, and a ToolStripStatusLabel. The GUI is on one thread and my class module is running on another one (mostly because it is dealing with the serial port, which is perhaps inconsequential to the question?)
Anyway, lets say I have a modular variable in my class (declared as “Object”?) and I want to create a method in the object to pass in a refence to any one of those three UI elements, each one of which has the “.Text” property to which I can write.
The Class module has a delegate to invoke that will allow it to write to another gui element on the form called txtLog which is visually logging the data. At the same time I want to write something to this other passed-in UI object (say I want to display the index variable from the ring buffer).
It works fine if I stick to a Label (or any one of them) and declare everything as a Label:
===================
Up at the top, the modular variable to hold the control reference:
System.Windows.Forms.Label inPtrLbl;
And then a method to pass the assignment into the class:
public void TurnOnLogging(System.Windows.Forms.TextBox location, System.Windows.Forms.Label inLbl, System.Windows.Forms.Label outLbl)
{
comLogging = true;
logBox = location;
inPtrLbl= new System.Windows.Forms.Label();
inPtrLbl = inLbl;
}
Because the class and the form are on different threads, you need to use the Invoke stuff:
private delegate void UpdateUiTextDelegate(byte text, Int32 ptr);
“Receive” which runs for the event that fires when a char is received looks like this (“esLink” is what I named my serial port inside this class) and you can see the Invoke of “WriteData” happening to write the char into the textbox’s .Text property, which also “grants the right” (I know that’s’ the wrong thing to say) to write the text into the label on the same UI thread in the “WriteData” function below it:
private void Recieve(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
byte recieved_data;
// Collecting the characters received to our 'buffer' (string).
while (esLink.BytesToRead > 0)
{
recieved_data = (byte)esLink.ReadByte();
// add it to the circular buffer
rxBuffer[nextBufferInputPoint] = recieved_data;
if (++nextBufferInputPoint >= RX_BUFFER_SIZE)
nextBufferInputPoint = 0;
// then put it in the text box for logging
logBox.Invoke(new UpdateUiTextDelegate(WriteData), recieved_data, nextBufferInputPoint);
}
}
private void WriteData(byte text, Int32 ptr)
{
// Assign the value of the recieved_data to the TextBox and label.
if (comLogging)
{
logBox.Text += (char)text;
inPtrLbl.Text = ptr.ToString();
}
}
So, all this works like a charm, really. As long as I declare the variable in the class to be the same type as what I’m passing in. But I want to pass (almost) anything with a .Text property to it so I have more flexibility in designing my GUI. I tried declaring the passed item as an Object, it gets there but the IDE complains that the object doesn’t have a .Text property. I tried declaring it as something with a .Text property and then “changing” it with a “new” but that didn’t work either. I said, ok, I’ll limit it to three types and create overloaded methods for the three types. The problem there is I could only make that work if I declared the three different types at the top and only used one (and set some kind of control variable to decide which one to use when writing to the UI control).
I’m thinking there has to be an easier way. In principle, I want to declare a generic object that I can turn into anything based on what I pass in and access its .Text property. At the very least, creating an overloaded method for each type (realistically there might be 4 or 5 different types only) would be acceptable (but not ideal) and I could live with that.
(I hope I have explained this well, sorry if not...)
Thanks,
-Vin
Honestly it's a little weird for a serial port library to have a dependency on a UI control (see separation of concerns). I'd suggest you set things up so the caller can pass a delegate instead.
Action<char> _logHandler;
public void TurnOnLogging(Action<char> logHandler)
{
comLogging = true;
_logHandler = logHandler;
}
Then, when you have data to log, call the delegate.
private void WriteData(byte text)
{
if (comLogging)
{
_logHandler((char)text);
}
}
This way the caller can decide how the contents are displayed. They can use a control that has a Text property, or a different type of control if they want to. Or maybe they might not want to use a textbox, or a winforms control at all, but maybe log it to a file.
obj.TurnOnLogging( x => TextBox1.Text += x.ToString() );
Or
obj.TurnOnLogging( x => SomeOtherControl1.Caption += x.ToString() );
Or
obj.TurnOnLogging( x => _logger.Write(x) );
You might also consider getting rid of your unusual mechanism in favor of something more idiomatic, such as a custom event.
You may define the type (of the parameter?) as "Control" (System.Windows.Forms.Control), as most UI control classes are derived from this class. Actually, the Control class has really a large number of properties, such as "Text", "Location", "Size", "Parent", etc.
See https://learn.microsoft.com/dotnet/api/system.windows.forms.control

Is it possible to reference InputActionAsset controls, even when inactive, for use in MonoBehavior Update methods?

This current project has 3rd party controllers/cameras that I am converting over to the new Input system 1.0.1. I'd like to keep its functionality within the old controllers but gain the benefit of being able to switch action maps and such. Most of the controller code does their work in MonoBehavior Update.
I am currently struggling to find a solution to converting these:
if (Input.GetButton())
if (Input.GetButtonDown())
if (Input.GetButtonUp())
I have been referencing the migration documentation to try and find a solution found here: https://docs.unity3d.com/Packages/com.unity.inputsystem#1.0/manual/Migration.html , but I seem to be misunderstanding or missing something with the conversion for these specific controls.
For the button controls, they state that one should use the ButtonControl props, but I am not sure the proper way to get a hold on these controls. Here is an example of what I tried, at the top of the 3rd party controller Update, that was most promising:
var fire1Btn = _inputManager.PlayerInputs.Player.Fire1.activeControl as ButtonControl;
*_inputManager is a MonoBehavior object that stores my ref to the PlayerInputs object which is my : IInputActionCollection, IDisposable class which has the InputActionAsset, this is the .cs file generated automatically from the .inputactions file by Unity.
Then in the 3rd party controller code I replace the respective GetButtonDown GetButton GetButtonUp with this:
if (fire1Btn != null && fire1Btn.wasPressedThisFrame)
if (fire1Btn != null && fire1Btn.isPressed)
if (fire1Btn != null && fire1Btn.wasReleasedThisFrame)
This causes some issues though. .activeControl is null when actions are not firing and I don't think this behaves in the same way as the legacy controls. For example, when I go into the scene and start moving the player controller around, press forward, then release, the character continues to run forward, as if the value coming in from that control did not go back to 0 before the activeControl was nulled out again. There is also a .controls[] array on the object but this has a long list of controls that I could not easily distinguish which, if any, were the correct control.
*edit1: after testing it appears as though wasReleasedThisFrame on an activeControl is never true, which I guess makes sense if this control object is only ever active when its being used.
Is there a proper way to get a hold on these controls? Is this approach naive in some way? Any help or documentation on what to do here would be greatly appreciated.
Based on my reading, tests, and responses from various individuals, I am going to tentatively assume the answer to my original question is:
No, you cannot tap into an input control when its not active
Update 12/8/2020
Based on more recent findings I found that .activeControl will run into issues on composites and I found a better way to tackle this issue. .wasReleasedThisFrame continues to be troublesome though. This newer solution uses an extension method to check press states, with only released state requiring some kind of tracking on previous frame state of a button. Here are the better ways to check state, though the wasReleasedThisFrame method did not work correctly:
inputAction.ReadValue<float>() > 0f; // for isPressed state
inputAction.triggered && inputAction.ReadValue<float>() > 0f; // for wasPressedThisFrame
inputAction.triggered && inputAction.ReadValue<float>() == 0f; // for wasReleasedThisFrame ** Does not function properly **
As for .wasReleasedThisFrame you'll have to implement some kind of tracking of previous frame similar to the one I did below. I ended up using a dictionary on my input manager class to track the states, then passed it into the static extension method on checks.
public static bool WasReleasedThisFrame(this InputAction inputAction, Dictionary<string, ButtonControlMask> maskStates)
{
return maskStates.ContainsKey(inputAction.name) && maskStates[inputAction.name].wasReleasedThisFrame;
}
Bear in mind that if you check these values in some other mono-behavior update method (like how I do mine in my manager class), you could run into ScriptOrder issues unless you are checking the values in LateUpdate() like I am.
I'm still on the hunting for a better solution for the .wasReleasedThisFrame problem.
This is the my previous solution: Based on my original code from the question and specifically assuming you have the correct entries in your InputActionAsset, generated or created the c# input class from the asset file, instantiated the input class, and have reference to it, something like this should work:
public class ButtonControlMask
{
public bool wasPressedThisFrame { get; set; }
public bool isPressed { get; set; }
public bool wasReleasedThisFrame { get; set; }
}
private bool _fire1BtnWasActiveLastFrame = false;
private void LateUpdate() // works in Update as well
{
// set our controls
dynamic fire1Btn = _inputManager.PlayerInputs.Player.Fire1.activeControl as ButtonControl;
if (fire1Btn == null)
{
// never let our control be null, use mask and dynamics to prevent having to check for null
fire1Btn = new ButtonControlMask()
{
// _fire1BtnWasActiveLastFrame will check out last frames state
wasPressedThisFrame = false,
isPressed = false,
wasReleasedThisFrame = _fire1BtnWasActiveLastFrame
};
// if it was active, and is null now, we can now reset our flag
if (_fire1BtnWasActiveLastFrame) _fire1BtnWasActiveLastFrame = false;
}
else
{
// we have an active control, set our active last frame flag
_fire1BtnWasActiveLastFrame = true;
}
// migrated code below ---
if (fire1Btn.wasPressedThisFrame) // This replaces Input.GetButtonDown()
if (fire1Btn.isPressed) // This replaces Input.GetButton()
if (fire1Btn.wasReleasedThisFrame) // This replaces Input.GetButtonUp()
}
Just using the .activeControl we have values for our .wasPressedThisFrame and .isPressed, however, .wasReleasedThisFrame is unknown to us as the control goes null the instant its not being used. To go around this we store the previous frames state in the _fire1BtnWasActiveLastFrame variable based on if the control was null or not. Then in the next frame, if the control is null, _fire1BtnWasActiveLastFrame will inform us that .wasReleasedThisFrame should be true on our masked class for ButtonControl only for this frame.
It's not pretty but this should allow for clean migration of Input.GetButtonDown Input.GetButton Input.GetButtonUp within your legacy code without having to change or check logic.

C# - Visual Studio: Get an array of objects from the form (listboxes, Comboboxes, etc) and do 'stuff' with them?

So, here's my code (very simplified):
object[] Obj = new object[] {
ListBoxID,
CheckBox1
};
string ObjName;
for(int ObjIndex = 0; ObjIndex< Obj.Length; ObjIndex++)
{
switch (Obj[ObjIndex].GetType().Name)
{
#region CheckBox
case "CheckBox":
CheckBox ObjType1 = sender as CheckBox;
ObjName = ObjType1.Name;
The code continues, but it just stops right there. If I remove that last line, it is all good, Except I need the name.
This function does a lot of stuff. It is mainly to reset several fields, like, textboxes, checkboxes, listboxes, comboboxes, using it's own name to do so.
I'm self taught with C# and Visual Studio Itself, so I'm not understanding why this isn't working.
I'm assuming that the "object" thing here, is not meant for this sort of work.
Using the 'checkbox' example, this is supposed to do this:
if (!ObjType1.Name.Contains(Globals.PreventResetFrom)) {
if (ObjType1.Name.Contains(FormName) && (Array.IndexOf(Force, ObjName) >= 0 || (Force.Length == 0 && Array.IndexOf(Skip, ObjName) < 0))) {
#region Actions Made
ObjType1.Checked = false;
#endregion
}
}
break;
}
}
Between every type of object, the only thing that changes is what's inside 'Actions Made' Region. The rest is all the same. And, for each Object, of the same type, it's all the same thing.
I'm looking for a way to, instead of having almost 1000 Lines of code, all repeating the same stuff, I'd like to be able to do it this way...
I'm hopping I've explained myself good :)
Keep in mind that all the 'objects' are the actual objects that the user sees, were the user gives a certain input. This is to cancel everything the user has done.
Thank you for your time!
First this: your question is much longer than needed, please keep it short and to the point (a Minimal example is what we like here).
That aside, I think the solution boils down to this:
case "CheckBox":
CheckBox ObjType1 = Obj[ObjIndex] as CheckBox;
You are looping over the elements of Obj, so it makes no sense to use sender in that line.

Two way binding with UI quirks

I haven't really done a two way binding project before, so this might be a simple question.
I have an business object with all the right interfaces (INotifyPropertyChanged, etc...). I bound a Int32 property called CPP to a textbox named txtCPP via a BindingSource. The rules state that if CPP is less than 0, the text box should be blank, otherwise should display a value.
So to make that happen, I changed the property from Int32 to Int32? (nullable) and when the backing variable of the CPP property is less than zero, I actually return null.
This actually works fine with the UI. The problem comes when I want to persist the business object to the database. An external method takes the business object, reads its properties (including CPP) and persists them to the database. And obviously, instead of CPP being -1, it is being written as null.
I am sure I am not the first person to come up with this issue when doing two-way binding projects. How does one typically handle these problems in a clean way without polluting the form code with edge cases like that?
It sounds as though your rule about the box being blank is a UI rule, and not a data rule (so the data value really is -1, it just should be displayed as blank).
If that's the case, lets move the blank logic into the UI. So, your data object property returns an int (and it returns the real value of the int), and then you can morph it in your display.
One way to do that would be with formatting -- you can specify a Formatter with your databinding, or you can hook the Format event on the databinding.
Of course, you still need to decide what to do when the user enters a blank value into the textbox...
Here's a really simple-minded example, using the Format event in the databinding:
var binding = this.textBox1.DataBindings.Add("Text", MyObject, "AValue", true);
binding.Format += (s, args) =>
{
int i = (int)args.Value;
if (i <= 0)
args.Value = "";
};

StackOverflowException without recursion or infinite loop?

Background
I have a DataGridView control which I am using, and I added my handler below to the DataGridView.CellFormatting event so the values in some cells can be made more human-readable. This event handler has been working great, formatting all values without issue.
Recently however, I have discovered a very rare circumstance causes an unusual bug. The column in my DataGridView for the item's due date always has an int value. 0 indicates the event is never due, any other value is a UTC timestamp for the due date. The MySQL db column corresponding doesn't allow nulls. When the user has moved from one DataGridView row with a due date, to another DataGridView row with a due date (at this point everything is still appears fine), and then presses a button which reloads the data from the database (without sending updates, essentially calling DataAdapter.Fill()), the program generates a StackOverflowException**.
No recursion?
What is so unusual to me is that I do not see where the recursion or infinte-looping is. I added int cellFormatCallCount as a class member, and increment it during each call, but at the time the exception is thrown, the debugger shows the value of that int as 1, which I would expect since I wasn't under the impression and recursion was occuring.
Can somebody help me?
How can I view a stack trace? In VS2008 it says:
{Cannot evaluate expression because the current thread is in a stack overflow state.}
Best regards,
Robinson
private int cellFormatCallCount = 0;
private void myDataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) {
try {
// to format the cell, we will need to know its value and which column its in
string value = "";
string column = "";
// the event is sometimes called where the value property is null
if (e.Value != null) {
cellFormatCallCount++; // here is my test for recursion, this class member will increment each call
// This is the line that throws the StackOverflowException
/* ********************* */
value = e.Value.ToString();
/* ********************* */
column = actionsDataGridView.Columns[e.ColumnIndex].Name;
} else {
return; // null values cannont be formatted, so return
}
if (column == "id") {
// different code for formatting each column
} else if (column == "title") {
// ...
} else {
// ...
}
} finally {
cellFormatCallCount = 0; // after we are done with the formatting, reset our recursion counter
}
}
Apparently e.Value.ToString() invokes the CellFormatting event again. That seems somewhat logical. It should be easy enough to find out with a debugger.
But the actual recursion could be caused somewhere else, like in the per-column formatting that you omitted.
Your recursion check isn't reliable since Value==null will also reset it, and it appears to be shared by all columns. Make it surround the e.Value.ToString() more tightly:
if (e.Value != null)
{
cellFormatCallCount++;
System.Diagnostics.Debug.Assert(cellFormatCallCount <= 1, "Recursion");
value = e.Value.ToString();
cellFormatCallCount--;
...
}
Totally random guess (with no stack trace it's all I can do)...
Are you attempting to display/format a type which has a potentially recursive ToString()?
public string ToString()
{
return ... this.ToString() ...
// or
return String.Format("The value is {0}", this);
}
A typo/error like that could cause a StackOverflowException...
Given that this is an event, might it be triggering its self?
Try making the cellFormatCallCount variable static so that it shared by all instances of the class. I suspect that somehow the event is triggering itself but you aren't seeing it because cellFormatCallCount is only local to each instance of the class handling the event and thus is never incremented beyond 1. If that's the case, then the actual trigger for the stackoverflow (recursion) could be anywhere in the method and it just happens to run out of stack space at that line.
Once you've made the variable static, you could throw an exception when it exceeds a certain (small) value, like 2. That exception should leave a viewable stack trace around.
#Daniel: If that were the issue, wouldn't it already raise the exception in the line:
if (e.Value != null) {
#gnirts: Could you post the full method and stack trace too?
#BCS (below): I think that might be it, but it might easily be in some of the code that is not shown in the deo posted.
PS. I'm sorry, this should have been a comment, but I have not enough reps :-D
It's unrelated to the problem but you can actually have a StackOverflowException without recursion at all just with:
throw new StackOverflowException();
I Have just had a similar problem of stackoverflow with no trace details.
My problem was due to a instance of an object which should not have been instantiated. I removed the offending new object and all was fine.
In the circumstance above without seeing any more code, ensure multiple events are not fired using the =- to cancel the events accordingly.
I hope this might help someone.

Categories