Quite new to C#, from a long time ago C++ background and so I seem to be having my trouble transitioning away from pointers to ref in C#.
I have a class (EColour) which I create using the constructor shown.
I assign (or at least try to) a reference to cellTemplate to the variable m_template.
Looking in debug, at the time of construction, m_template is most definitely NOT null.
However, by the time I come to handle OnMouseClick event, I get a null exception error because m_template has magically turned to null.
Could anyone please shed light on what I have done wrong and how to fix it?
public EColour(ref ICellTemplate cellTemplate)
{
m_template = (ColourTemplate)cellTemplate;
}
protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
{
ColorDialog dlg = new ColorDialog();
dlg.AnyColor = m_template.AnyColour; // This throws an exception because m_template is null
base.OnMouseClick(e);
}
ColourTemplate m_template;
In C# we have two main kinds of types:
value type - its all digit types (int, float, double, long ...)
reference type -its types that inherited from object
ICellTemplate is a reference class. So what you need - just send it in argument as a regular variable.
public class EColour
{
private ColourTemplate m_tamplate;
public EColour(ICellTemplate cellTemplate)
{
m_template = (ColourTemplate)cellTemplate;
}
}
Related
This is more a question of terminology than how to do something.
I found a sort of 3rd state a property can be in. The first is a valid property. For example, bool is true or false. The second is null. The third I do not know what to call it. It is a property of an object that has been instantiated. This property kind of looks like it has not been instantiated. Not sure is that is the proper way to state it.
Example: Working with System.Diagnostics.Process to open a virtual keyboard, OSK. I need a routine to toggle the keyboard on and off. This works great.
using System.Diagnostics;
namespace Bsc
{
public static class OnScreenKeyboard
{
private static Process virtualKeyboard = new Process();
public static void ToggleHideShow()
{
try
{
if (virtualKeyboard.HasExited)
virtualKeyboard = Process.Start("osk.exe");
else
virtualKeyboard.Kill();
}
catch
{
virtualKeyboard = Process.Start("osk.exe");
}
}
}
}
Looking at the object virtualKeyboard in a watch window I can see how virtualKeyboard has been instantiated, but not completely. On the first pass virtualKeyboard.HasExited throws an exception. Watching the watch window, it also looks like it is not there. The line has a nice bright red dot in front of it with an X in it.
Name
Value
Type
HasExited
'virtualKeyboard.HasExited' threw an exception of type 'System.InvalidOperationException'
bool {System.InvalidOperationException}
Still on first pass the try/catch jumps to the Process.Start. After this executes the line looks like you would expect for an instantiated property.
Name
Value
Type
HasExited
false
bool
All calls after the first one HasExited works like you would expect and the method toggles the keyboard on and off.
I have not run into an object before that seemingly was only partially instantiated. What are the proper technical terms for this scenario? I have used the term ‘instantiated’, is that correct?
As per the documentation, the HasExited property throws an InvalidOperationException when there is no OS process associated with the Process instance.
Your property is initialized to a new instance of the Process class, which has not been started. Therefore, there is no OS process associated with that instance, and the HasExited property will throw an exception.
Remove the field initializer, and test for null in your method.
public static class OnScreenKeyboard
{
private static Process virtualKeyboard;
public static void ToggleHideShow()
{
try
{
if (virtualKeyboard == null || virtualKeyboard.HasExited)
virtualKeyboard = Process.Start("osk.exe");
else
virtualKeyboard.Kill();
}
catch
{
virtualKeyboard = Process.Start("osk.exe");
}
}
}
It is not possible for an object to be half-initialised based on the working of the language. If an object is not in a correct state, this is due to the working of the class/properties etc, and how the code has been written to initialise the object or not throw an exception when accessing the properties.
If the object does not always do what you expect then you have to check for the exception, do some defensive coding and handle the various scenarios in your own code.
I am making some validation functions for my project but I am stuck on something.
I want to have a single function to handle a couple of different controls and errors.
Here's my code:
private void ValidateControls(Control c)
{
if (c is TextBox)
{
if (c.Text == "")
{
epNew.SetError(c, "Something");
}
}
else if (c is ComboBox)
{
// What now?
// if (c.SelectedItem == null) does not work
}
}
And I am calling it like this:
private void txtNEAN_Validating(object sender, CancelEventArgs e)
{
ValidateControls(txtNEAN);
}
This works fine for textboxes. But if I do:
private void cbbEMerk_Validating(object sender, CancelEventArgs e)
{
ValidateControls(cbbEMerk);
}
if (c.SelectedItem == null) for example does not work.
How can I achieve this? And is this okay to use? If not, what is a better alternative?
I'd love to hear anything!
You have to cast c to a ComboBox in this case
else if (c is ComboBox)
{
if (((ComboBox)c).SelectedItem == null)
}
By the way, don't create a _Validating method for every control if they do the same thing. You can use a single one, or one txtBox_Validating for TextBoxes, one comboBox_Validating for comboboxes, etc.
Try using
((ComboBox)c).SelectedItem
instead. This tells the program to parse the Control c into a ComboBox.
As an alternative, instead of using is you could use as
// Converts c to a ComboBox. If c is not a ComboBox, assigns null to cmbControl
ComboBox cmbControl = c as ComboBox;
if (cmbControl != null)
{
if (cmbControl.SelectedItem != null)
{
// Do stuff here
}
}
// Else it's not a ComboBox
It is also good to know about safety cast using as and is:
Because objects are polymorphic, it is possible for a variable of a base class type to hold a derived type. To access the derived type's method, it is necessary to cast the value back to the derived type. However, to attempt a simple cast in these cases creates the risk of throwing an InvalidCastException. That is why C# provides the is and as operators. You can use these operators to test whether a cast will succeed without causing an exception to be thrown. In general, the as operator is more efficient because it actually returns the cast value if the cast can be made successfully. The is operator returns only a Boolean value. It can therefore be used when you just want to determine an object's type but do not have to actually cast it.
You can see more here
I'm trying to make a function that returns the actual control. I know you can do this when you know the type of the object:
public static Control GetCtl(this object obj)
{
//(obj as obj.GetType()) ?
return (TextBox)obj;
}
But what if I need to get one from some other (unknown) object type, for example TextBox or RichTextBox? I will then use that on:
private void TextBox1_Click(object sender, EventArgs e)
{
sender.GetCtl().Select(0, 0);
}
Though TextBox and RichTextBox seem to have the same Select method, when I tried the above GetCtl() function, it always threw an error. How do I do this properly?
Edit:
Since you cannot access the "complete" control property as sender in all native procedures/methods (i.e _Click(), _MouseMove()) I want to make a function that can return the actual control, not sure how to explain this better, but here's how I would use it:
Private void SomeControl_KeyDown(object sender, KeyEventArgs e){
sender.getCtl(). //<-- so I can access anything here
(sender as Button).Text = "sometext" // <-- so I won't have to do this
//I wish the following would work. But you get the idea
(sender.GetType())sender.Text = "";
//or...
(sender as sender.GetType()).Text = "";
}
RichTextBox and TextBox are both derived from TextBoxBase.
To tell the compiler that you want to treat an object as a specific type, you can perform a cast operation. In this case, you need to cast sender to TextBoxBase and then you can access the Select() method:
private void TextBox1_Click(object sender, EventArgs e)
{
var textbox = sender as TextBoxBase;
if (textbox != null)
textbox.Select(0, 0);
}
This uses the as keyword to cast the sender object to TextBoxBase. Note that you can't simply cast anything to anything else. The object being cast must actually be an instance of the type you're casting to. If sender is not an instance of something derived from TextBoxBase, the cast operation will return null.
Generally winforms event handlers should only be raised in very controlled and known circumstances - you should be very confident that this event is being raised by a control derived from TextBoxBase. There's not much point in raising it from anything else.
If you really want a method to convert sender to a TextBoxBase, you could write it as:
private TextBoxBase ToTextBoxBase(object sender)
{
var textbox = sender as TextBoxBase;
if (textbox == null)
throw new Exception("The given object is not derived from TextBoxBase");
return textbox;
}
You would use it like:
ToTextBoxBase(sender).Select(0, 0);
If the sender is not a TextBoxBase, this call would throw the "The given object is not derived from TextBoxBase" exception.
The important point to understand is that you are working with a strongly typed language. If you attempt to call a method of an object which is not of a type containing that method, the program won't even compile (unless the type is dynamic, but that's another discussion).
It is possible to create a method that you can call with this syntax:
sender.ToTextBoxBase()
This is called an extension method. However, you would be extending the System.Object class for a very specific purpose, which is not recommended.
This question is an extension of this article.
In the same case, I created a instance of WMP ActiveX by its ProgID.
protected const string WMP_PROG_ID = "WMPlayer.OCX.7";
private dynamic _wmp;
protected virtual bool init(){
try{
_wmp = Activator.CreateInstance(Type.GetTypeFromProgID(WMP_PROG_ID));
}
catch{ return false; }
return connectEvent();
}
According the MSDN document, there are an Error event and an error property in WMPlayer object.
So, I try to attach events like this way.
protected bool connectEvent(){
try{
_wmp.PlayStateChange += new StateHandler(_wmp_PlayStateChange);
//_wmp.Error += new Action(_wmp_ErrorEvent);
}
catch { return false; }
return true;
}
protected void _wmp_PlayStateChange(WMPlayerState state){
//do something I like
}
protected void _wmp_ErrorEvent(){
//do some error handling
}
If I keep //_wmp.Error += new Action(_wmp_ErrorEvent) commented,
there's no compile error and PlayStateChange works pretty good.
However, if I remove the comment mark, there's a runtime exception.
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: can not apply operator "+=" between 'System.__ComObject' and 'System.Action'
It seems the two "error" are conflicted because COM is case insensitive. How can I solve it?
My goal is that attach to "Error" event without using AxWindowsMediaPlayer.
I ran in to a very similar problem as you but mine was with Size. The strongly typed ActiveX control redefined Size so I needed to cast it back to Control when the forms designer wanted to size the control on my form.
((Control)this.axLEAD1).Size = new System.Drawing.Size(298, 240);
If you can get a strongly typed class for the com object (by adding a reference or using tlbimp.exe) you may be able to cast to the strongly typed com object instead of __ComObject and have it use the correct method.
According to C# Reference
"The null keyword is a literal that represents a null reference, one
that does not refer to any object. null is the default value of
reference-type variables'
I was surprised to find out that
Commenting e=null line in the following app code (taken from the article "Difference Between Events And Delegates in C#") results in compilation error:
Use of unassigned local variable 'e'
while without commenting it is compiled and run.
I do not get:
Where is variable e used?
Is it possible to force the app to run without the dumb assigning the variable to null?
f
using System;
class Program
{
static void Main(string[] args)
{
DelegatesAndEvents obj = new DelegatesAndEvents();
obj.Execute();
}
}
public class DelegatesAndEvents
{
public event EventHandler MyEvent;
internal void Execute()
{
EventArgs e;
//Commenting the next line results in compilation error
//Error 1 Use of unassigned local variable 'e'
e = null;
int sender = 15;
MyEvent += MyMethod;
MyEvent += MyMethod2;
MyEvent(sender, e);
}
void MyMethod(object sender, EventArgs e)
{
Console.WriteLine(sender);
}
void MyMethod2(object sender, EventArgs e)
{
Console.WriteLine(sender);
Console.ReadLine();
}
}
Update (or the comment to all answers):
So, and I never knew it, there are to kind of nulls - one which is assigned and another which is not assigned... Funny...
They should probably have different types, for checking:
if typeof(unassigned-null) then do this;
if typeof(assigned_null) then do that;
Local variables are not initialized with their default value, whereas fields are.
There's no way for the compiler to know, when compiling Execute what event handlers are registered to MyEvent. (Others may have already been registered before Execute is called, and they may attempt to use e). As such, it doesn't attempt any analysis on any handlers it could know are registered. So, since you want to pass e, it has to be initialized.
If you want to pass null, why introduce e at all? Just have
MyEvent(sender, null);
Which at least makes your intent explicit.
You are using e without assigning a value to it. Using e = null;, you are assigning value to variable e as null is also a value.
Also, according to C# reference default value for reference types is null but here e is a variable. Variables are not defaulted, and must have "definite assignment" before they are used.
To answer your questions:
Question: Where is variable e used?
Answer: It is used where you pass it to MyEvent(sender, e);
Question: Is it possible to force the app to run without the dumb assigning the variable to null?
Answer: No, and it's not dumb because you ARE using e by passing it to another method. You are just not using it directly - but you are still using it.
Actually, you don't need to declare e anywhere - you can just pass null directly to MyEvent:
MyEvent(sender, null);
Then you don't need to do any redundant variable declaration.